package com.subgraph.orchid.directory.downloader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import com.subgraph.orchid.ConsensusDocument;
import com.subgraph.orchid.Directory;
import com.subgraph.orchid.Router;
import com.subgraph.orchid.TorConfig;
import com.subgraph.orchid.TorConfig.AutoBoolValue;
import com.subgraph.orchid.data.HexDigest;
public class DescriptorProcessor {
private final static int MAX_DL_PER_REQUEST = 96;
private final static int MAX_DL_TO_DELAY = 16;
private final static int MIN_DL_REQUESTS = 3;
private final static int MAX_CLIENT_INTERVAL_WITHOUT_REQUEST = 10 * 60 * 1000;
private final TorConfig config;
private final Directory directory;
private Date lastDescriptorDownload;
DescriptorProcessor(TorConfig config, Directory directory) {
this.config = config;
this.directory = directory;
}
private boolean canDownloadDescriptors(int downloadableCount) {
if(downloadableCount >= MAX_DL_TO_DELAY)
return true;
if(downloadableCount == 0)
return false;
if(lastDescriptorDownload == null)
return true;
final Date now = new Date();
final long diff = now.getTime() - lastDescriptorDownload.getTime();
return diff > MAX_CLIENT_INTERVAL_WITHOUT_REQUEST;
}
/*
* dir-spec.txt section 5.3
*/
private List< List<HexDigest> > partitionDescriptors(List<Router> descriptors) {
final int size = descriptors.size();
final List< List<HexDigest> > partitions = new ArrayList< List<HexDigest> >();
if(size <= 10) {
partitions.add(createPartitionList(descriptors, 0, size));
return partitions;
} else if(size <= (MIN_DL_REQUESTS * MAX_DL_PER_REQUEST)) {
final int chunk = size / MIN_DL_REQUESTS;
int over = size % MIN_DL_REQUESTS;
int off = 0;
for(int i = 0; i < MIN_DL_REQUESTS; i++) {
int sz = chunk;
if(over != 0) {
sz++;
over--;
}
partitions.add(createPartitionList(descriptors, off, sz));
off += sz;
}
return partitions;
} else {
int off = 0;
while(off < descriptors.size()) {
partitions.add(createPartitionList(descriptors, off, MAX_DL_PER_REQUEST));
off += MAX_DL_PER_REQUEST;
}
return partitions;
}
}
private List<HexDigest> createPartitionList(List<Router> descriptors, int offset, int size) {
final List<HexDigest> newList = new ArrayList<HexDigest>();
for(int i = offset; i < (offset + size) && i < descriptors.size(); i++) {
final HexDigest digest = getDescriptorDigestForRouter(descriptors.get(i));
newList.add(digest);
}
return newList;
}
private HexDigest getDescriptorDigestForRouter(Router r) {
if(useMicrodescriptors()) {
return r.getMicrodescriptorDigest();
} else {
return r.getDescriptorDigest();
}
}
private boolean useMicrodescriptors() {
return config.getUseMicrodescriptors() != AutoBoolValue.FALSE;
}
List< List<HexDigest> > getDescriptorDigestsToDownload() {
final ConsensusDocument consensus = directory.getCurrentConsensusDocument();
if(consensus == null || !consensus.isLive()) {
return Collections.emptyList();
}
final List<Router> downloadables = directory.getRoutersWithDownloadableDescriptors();
if(!canDownloadDescriptors(downloadables.size())) {
return Collections.emptyList();
}
lastDescriptorDownload = new Date();
return partitionDescriptors(downloadables);
}
}