commit 8a5a8e2c2fcd5317242feb0f485b0600102951f7
Author: Karsten Loesing <karsten.loesing(a)gmx.net>
Date: Wed Feb 1 11:04:21 2012 +0100
Parse all statistics in extra-info descriptors.
---
.../torproject/descriptor/ExtraInfoDescriptor.java | 25 +-
.../torproject/descriptor/impl/DescriptorImpl.java | 12 +
.../descriptor/impl/ExtraInfoDescriptorImpl.java | 406 ++++++++++++++------
.../torproject/descriptor/impl/ParseHelper.java | 103 +++++
.../descriptor/impl/ServerDescriptorImplTest.java | 5 +
5 files changed, 419 insertions(+), 132 deletions(-)
diff --git a/src/org/torproject/descriptor/ExtraInfoDescriptor.java b/src/org/torproject/descriptor/ExtraInfoDescriptor.java
index bd4bcd8..bc9f49d 100644
--- a/src/org/torproject/descriptor/ExtraInfoDescriptor.java
+++ b/src/org/torproject/descriptor/ExtraInfoDescriptor.java
@@ -143,7 +143,7 @@ public interface ExtraInfoDescriptor extends Descriptor {
/* Return the mean number of cells contained in circuit queues by
* circuit deciles. */
- public List<Integer> getCellQueueCells();
+ public List<Double> getCellQueuedCells();
/* Return the mean times in milliseconds that cells spend in circuit
* queues by circuit deciles. */
@@ -160,7 +160,7 @@ public interface ExtraInfoDescriptor extends Descriptor {
/* Return the interval length of the included statistics on
* bi-directional connection usage, or -1 if no such statistics are
* included. */
- public long getConnBiDirectIntervalLength();
+ public long getConnBiDirectStatsIntervalLength();
/* Return the number of connections on which this relay read and wrote
* less than 2 KiB/s in a 10-second interval, or -1 if no statistics on
@@ -194,18 +194,19 @@ public interface ExtraInfoDescriptor extends Descriptor {
public long getExitStatsIntervalLength();
/* Return statistics on KiB written by port with map keys being ports
- * and map values being KiB rounded up to the next full KiB, or null if
- * no exit statistics are included. */
- public SortedMap<Integer, Integer> getExitKibibytesWritten();
+ * (or "other") and map values being KiB rounded up to the next full
+ * KiB, or null if no exit statistics are included. */
+ public SortedMap<String, Integer> getExitKibibytesWritten();
- /* Return statistics on KiB read by port with map keys being ports and
- * map values being KiB rounded up to the next full KiB, or null if no
- * exit statistics are included. */
- public SortedMap<Integer, Integer> getExitKibibytesRead();
+ /* Return statistics on KiB read by port with map keys being ports (or
+ * "other") and map values being KiB rounded up to the next full KiB, or
+ * null if no exit statistics are included. */
+ public SortedMap<String, Integer> getExitKibibytesRead();
/* Return statistics on opened exit streams with map keys being ports
- * and map values being the number of opened streams, rounded up to the
- * nearest multiple of 4, or null if no exit statistics are included. */
- public SortedMap<Integer, Integer> getExitStreamsOpened();
+ * (or "other") and map values being the number of opened streams,
+ * rounded up to the nearest multiple of 4, or null if no exit
+ * statistics are included. */
+ public SortedMap<String, Integer> getExitStreamsOpened();
}
diff --git a/src/org/torproject/descriptor/impl/DescriptorImpl.java b/src/org/torproject/descriptor/impl/DescriptorImpl.java
index d8705e2..3192e21 100644
--- a/src/org/torproject/descriptor/impl/DescriptorImpl.java
+++ b/src/org/torproject/descriptor/impl/DescriptorImpl.java
@@ -218,6 +218,18 @@ public abstract class DescriptorImpl implements Descriptor {
}
}
+ protected void checkKeywordsDependOn(Set<String> dependentKeywords,
+ String dependingKeyword) throws DescriptorParseException {
+ for (String dependentKeyword : dependentKeywords) {
+ if (this.parsedKeywords.containsKey(dependentKeyword) &&
+ !this.parsedKeywords.containsKey(dependingKeyword)) {
+ throw new DescriptorParseException("Keyword '" + dependentKeyword
+ + "' is contained, but keyword '" + dependingKeyword + "' is "
+ + "not.");
+ }
+ }
+ }
+
protected int getKeywordCount(String keyword) {
if (!this.parsedKeywords.containsKey(keyword)) {
return 0;
diff --git a/src/org/torproject/descriptor/impl/ExtraInfoDescriptorImpl.java b/src/org/torproject/descriptor/impl/ExtraInfoDescriptorImpl.java
index 73ea040..5320563 100644
--- a/src/org/torproject/descriptor/impl/ExtraInfoDescriptorImpl.java
+++ b/src/org/torproject/descriptor/impl/ExtraInfoDescriptorImpl.java
@@ -11,12 +11,11 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.SortedMap;
+import java.util.TreeMap;
import org.torproject.descriptor.BandwidthHistory;
import org.torproject.descriptor.ExtraInfoDescriptor;
-/* TODO Implement methods to parse the various statistics (other than
- * bandwidth histories. */
/* TODO Write a test class. */
public class ExtraInfoDescriptorImpl extends DescriptorImpl
implements ExtraInfoDescriptor {
@@ -46,21 +45,39 @@ public class ExtraInfoDescriptorImpl extends DescriptorImpl
Set<String> exactlyOnceKeywords = new HashSet<String>(Arrays.asList((
"extra-info,published").split(",")));
this.checkExactlyOnceKeywords(exactlyOnceKeywords);
+ Set<String> dirreqStatsKeywords = new HashSet<String>(Arrays.asList((
+ "dirreq-stats-end,dirreq-v2-ips,dirreq-v3-ips,dirreq-v2-reqs,"
+ + "dirreq-v3-reqs,dirreq-v2-share,dirreq-v3-share,dirreq-v2-resp,"
+ + "dirreq-v3-resp,dirreq-v2-direct-dl,dirreq-v3-direct-dl,"
+ + "dirreq-v2-tunneled-dl,dirreq-v3-tunneled-dl,").split(",")));
+ Set<String> entryStatsKeywords = new HashSet<String>(Arrays.asList(
+ "entry-stats-end,entry-ips".split(",")));
+ Set<String> cellStatsKeywords = new HashSet<String>(Arrays.asList((
+ "cell-stats-end,cell-processed-cells,cell-queued-cells,"
+ + "cell-time-in-queue,cell-circuits-per-decile").split(",")));
+ Set<String> connBiDirectStatsKeywords = new HashSet<String>(
+ Arrays.asList("conn-bi-direct".split(",")));
+ Set<String> exitStatsKeywords = new HashSet<String>(Arrays.asList((
+ "exit-stats-end,exit-kibibytes-written,exit-kibibytes-read,"
+ + "exit-streams-opened").split(",")));
+ Set<String> bridgeStatsKeywords = new HashSet<String>(Arrays.asList(
+ "bridge-stats-end,bridge-stats-ips".split(",")));
Set<String> atMostOnceKeywords = new HashSet<String>(Arrays.asList((
- "read-history,write-history,geoip-db-digest,dirreq-stats-end,"
- + "dirreq-v2-ips,dirreq-v3-ips,dirreq-v2-reqs,dirreq-v3-reqs,"
- + "dirreq-v2-share,dirreq-v3-share,dirreq-v2-resp,dirreq-v3-resp,"
- + "dirreq-v2-direct-dl,dirreq-v3-direct-dl,dirreq-v2-tunneled-dl,"
- + "dirreq-v3-tunneled-dl,dirreq-read-history,"
- + "dirreq-write-history,entry-stats-end,entry-ips,cell-stats-end,"
- + "cell-processed-cells,cell-queued-cells,cell-time-in-queue,"
- + "cell-circuits-per-decile,conn-bi-direct,exit-stats-end,"
- + "exit-kibibytes-written,exit-kibibytes-read,"
- + "exit-streams-opened,bridge-stats-end,bridge-stats-ips,"
- + "router-signature").split(",")));
+ "read-history,write-history,dirreq-read-history,"
+ + "dirreq-write-history,geoip-db-digest,router-signature").
+ split(",")));
+ atMostOnceKeywords.addAll(dirreqStatsKeywords);
+ atMostOnceKeywords.addAll(entryStatsKeywords);
+ atMostOnceKeywords.addAll(cellStatsKeywords);
+ atMostOnceKeywords.addAll(connBiDirectStatsKeywords);
+ atMostOnceKeywords.addAll(exitStatsKeywords);
+ atMostOnceKeywords.addAll(bridgeStatsKeywords);
this.checkAtMostOnceKeywords(atMostOnceKeywords);
- /* TODO Add more checks to see that only statistics details lines are
- * included with corresponding statistics interval lines. */
+ this.checkKeywordsDependOn(dirreqStatsKeywords, "dirreq-stats-end");
+ this.checkKeywordsDependOn(entryStatsKeywords, "entry-stats-end");
+ this.checkKeywordsDependOn(cellStatsKeywords, "cell-stats-end");
+ this.checkKeywordsDependOn(exitStatsKeywords, "exit-stats-end");
+ this.checkKeywordsDependOn(bridgeStatsKeywords, "bridge-stats-end");
this.checkFirstKeyword("extra-info");
return;
}
@@ -205,82 +222,139 @@ public class ExtraInfoDescriptorImpl extends DescriptorImpl
private void parseGeoipDbDigestLine(String line, String lineNoOpt,
String[] partsNoOpt) throws DescriptorParseException {
- /* TODO Implement me. */
+ if (partsNoOpt.length != 2) {
+ throw new DescriptorParseException("Illegal line '" + line
+ + "' in extra-info descriptor.");
+ }
+ this.geoipDbDigest = ParseHelper.parseTwentyByteHexString(line,
+ partsNoOpt[1]);
}
private void parseGeoipStartTimeLine(String line, String lineNoOpt,
String[] partsNoOpt) throws DescriptorParseException {
- /* TODO Implement me. */
+ if (partsNoOpt.length != 3) {
+ throw new DescriptorParseException("Illegal line '" + line
+ + "' in extra-info descriptor.");
+ }
+ this.geoipStartTimeMillis = ParseHelper.parseTimestampAtIndex(line,
+ partsNoOpt, 1, 2);
}
private void parseGeoipClientOriginsLine(String line, String lineNoOpt,
String[] partsNoOpt) throws DescriptorParseException {
- /* TODO Implement me. */
+ this.geoipClientOrigins = ParseHelper.parseCommaSeparatedKeyValueList(
+ line, partsNoOpt, 1, 2);
}
private void parseDirreqStatsEndLine(String line, String lineNoOpt,
String[] partsNoOpt) throws DescriptorParseException {
- /* TODO Implement me. */
+ long[] parsedStatsEndData = this.parseStatsEndLine(line, partsNoOpt,
+ 5);
+ this.dirreqStatsEndMillis = parsedStatsEndData[0];
+ this.dirreqStatsIntervalLength = parsedStatsEndData[1];
+ }
+
+ private long[] parseStatsEndLine(String line, String partsNoOpt[],
+ int partsNoOptExpectedLength) throws DescriptorParseException {
+ if (partsNoOpt.length != partsNoOptExpectedLength ||
+ partsNoOpt[3].length() < 2 || !partsNoOpt[3].startsWith("(") ||
+ !partsNoOpt[4].equals("s)")) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ long[] result = new long[2];
+ result[0] = ParseHelper.parseTimestampAtIndex(line, partsNoOpt, 1, 2);
+ result[1] = ParseHelper.parseSeconds(line,
+ partsNoOpt[3].substring(1));
+ return result;
}
private void parseDirreqV2IpsLine(String line, String lineNoOpt,
String[] partsNoOpt) throws DescriptorParseException {
- /* TODO Implement me. */
+ this.dirreqV2Ips = ParseHelper.parseCommaSeparatedKeyValueList(line,
+ partsNoOpt, 1, 2);
}
private void parseDirreqV3IpsLine(String line, String lineNoOpt,
String[] partsNoOpt) throws DescriptorParseException {
- /* TODO Implement me. */
+ this.dirreqV3Ips = ParseHelper.parseCommaSeparatedKeyValueList(line,
+ partsNoOpt, 1, 2);
}
private void parseDirreqV2ReqsLine(String line, String lineNoOpt,
String[] partsNoOpt) throws DescriptorParseException {
- /* TODO Implement me. */
+ this.dirreqV2Reqs = ParseHelper.parseCommaSeparatedKeyValueList(line,
+ partsNoOpt, 1, 2);
}
private void parseDirreqV3ReqsLine(String line, String lineNoOpt,
String[] partsNoOpt) throws DescriptorParseException {
- /* TODO Implement me. */
+ this.dirreqV3Reqs = ParseHelper.parseCommaSeparatedKeyValueList(line,
+ partsNoOpt, 1, 2);
}
private void parseDirreqV2ShareLine(String line, String lineNoOpt,
String[] partsNoOpt) throws DescriptorParseException {
- /* TODO Implement me. */
+ this.dirreqV2Share = this.parseShareLine(line, partsNoOpt);
}
private void parseDirreqV3ShareLine(String line, String lineNoOpt,
String[] partsNoOpt) throws DescriptorParseException {
- /* TODO Implement me. */
+ this.dirreqV3Share = this.parseShareLine(line, partsNoOpt);
+ }
+
+ private double parseShareLine(String line, String[] partsNoOpt)
+ throws DescriptorParseException {
+ double share = -1.0;
+ if (partsNoOpt.length == 2 && partsNoOpt[1].length() >= 2 &&
+ partsNoOpt[1].endsWith("%")) {
+ String shareString = partsNoOpt[1];
+ shareString = shareString.substring(0, shareString.length() - 1);
+ try {
+ share = Double.parseDouble(shareString);
+ } catch (NumberFormatException e) {
+ /* Handle below. */
+ }
+ }
+ if (share < 0.0) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ return share;
}
private void parseDirreqV2RespLine(String line, String lineNoOpt,
String[] partsNoOpt) throws DescriptorParseException {
- /* TODO Implement me. */
+ this.dirreqV2Resp = ParseHelper.parseCommaSeparatedKeyValueList(line,
+ partsNoOpt, 1, 0);
}
private void parseDirreqV3RespLine(String line, String lineNoOpt,
String[] partsNoOpt) throws DescriptorParseException {
- /* TODO Implement me. */
+ this.dirreqV3Resp = ParseHelper.parseCommaSeparatedKeyValueList(line,
+ partsNoOpt, 1, 0);
}
private void parseDirreqV2DirectDlLine(String line, String lineNoOpt,
String[] partsNoOpt) throws DescriptorParseException {
- /* TODO Implement me. */
+ this.dirreqV2DirectDl = ParseHelper.parseCommaSeparatedKeyValueList(
+ line, partsNoOpt, 1, 0);
}
private void parseDirreqV3DirectDlLine(String line, String lineNoOpt,
String[] partsNoOpt) throws DescriptorParseException {
- /* TODO Implement me. */
+ this.dirreqV3DirectDl = ParseHelper.parseCommaSeparatedKeyValueList(
+ line, partsNoOpt, 1, 0);
}
private void parseDirreqV2TunneledDlLine(String line, String lineNoOpt,
String[] partsNoOpt) throws DescriptorParseException {
- /* TODO Implement me. */
+ this.dirreqV2TunneledDl = ParseHelper.parseCommaSeparatedKeyValueList(
+ line, partsNoOpt, 1, 0);
}
private void parseDirreqV3TunneledDlLine(String line, String lineNoOpt,
String[] partsNoOpt) throws DescriptorParseException {
- /* TODO Implement me. */
+ this.dirreqV3TunneledDl = ParseHelper.parseCommaSeparatedKeyValueList(
+ line, partsNoOpt, 1, 0);
}
private void parseDirreqReadHistoryLine(String line, String lineNoOpt,
@@ -297,74 +371,118 @@ public class ExtraInfoDescriptorImpl extends DescriptorImpl
private void parseEntryStatsEndLine(String line, String lineNoOpt,
String[] partsNoOpt) throws DescriptorParseException {
- /* TODO Implement me. */
+ long[] parsedStatsEndData = this.parseStatsEndLine(line, partsNoOpt,
+ 5);
+ this.entryStatsEndMillis = parsedStatsEndData[0];
+ this.entryStatsIntervalLength = parsedStatsEndData[1];
}
private void parseEntryIpsLine(String line, String lineNoOpt,
String[] partsNoOpt) throws DescriptorParseException {
- /* TODO Implement me. */
+ this.entryIps = ParseHelper.parseCommaSeparatedKeyValueList(line,
+ partsNoOpt, 1, 2);
}
private void parseCellStatsEndLine(String line, String lineNoOpt,
String[] partsNoOpt) throws DescriptorParseException {
- /* TODO Implement me. */
+ long[] parsedStatsEndData = this.parseStatsEndLine(line, partsNoOpt,
+ 5);
+ this.cellStatsEndMillis = parsedStatsEndData[0];
+ this.cellStatsIntervalLength = parsedStatsEndData[1];
}
private void parseCellProcessedCellsLine(String line, String lineNoOpt,
String[] partsNoOpt) throws DescriptorParseException {
- /* TODO Implement me. */
+ this.cellProcessedCells = ParseHelper.
+ parseCommaSeparatedIntegerValueList(line, partsNoOpt, 1);
}
private void parseCellQueuedCellsLine(String line, String lineNoOpt,
String[] partsNoOpt) throws DescriptorParseException {
- /* TODO Implement me. */
+ this.cellQueuedCells = ParseHelper.parseCommaSeparatedDoubleValueList(
+ line, partsNoOpt, 1);
}
private void parseCellTimeInQueueLine(String line, String lineNoOpt,
String[] partsNoOpt) throws DescriptorParseException {
- /* TODO Implement me. */
+ this.cellTimeInQueue = ParseHelper.
+ parseCommaSeparatedIntegerValueList(line, partsNoOpt, 1);
}
private void parseCellCircuitsPerDecileLine(String line,
String lineNoOpt, String[] partsNoOpt)
throws DescriptorParseException {
- /* TODO Implement me. */
+ int circuits = -1;
+ if (partsNoOpt.length == 2) {
+ try {
+ circuits = Integer.parseInt(partsNoOpt[1]);
+ } catch (NumberFormatException e) {
+ /* Handle below. */
+ }
+ }
+ if (circuits < 0) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ this.cellCircuitsPerDecile = circuits;
}
private void parseConnBiDirectLine(String line, String lineNoOpt,
String[] partsNoOpt) throws DescriptorParseException {
- /* TODO Implement me. */
+ long[] parsedStatsEndData = this.parseStatsEndLine(line, partsNoOpt,
+ 6);
+ this.connBiDirectStatsEndMillis = parsedStatsEndData[0];
+ this.connBiDirectStatsIntervalLength = parsedStatsEndData[1];
+ List<Integer> parsedConnBiDirectStats = ParseHelper.
+ parseCommaSeparatedIntegerValueList(line, partsNoOpt, 5);
+ if (parsedConnBiDirectStats.size() != 4) {
+ throw new DescriptorParseException("Illegal line '" + line + "' in "
+ + "extra-info descriptor.");
+ }
+ this.connBiDirectBelow = parsedConnBiDirectStats.get(0);
+ this.connBiDirectRead = parsedConnBiDirectStats.get(1);
+ this.connBiDirectWrite = parsedConnBiDirectStats.get(2);
+ this.connBiDirectBoth = parsedConnBiDirectStats.get(3);
}
private void parseExitStatsEndLine(String line, String lineNoOpt,
String[] partsNoOpt) throws DescriptorParseException {
- /* TODO Implement me. */
+ long[] parsedStatsEndData = this.parseStatsEndLine(line, partsNoOpt,
+ 5);
+ this.exitStatsEndMillis = parsedStatsEndData[0];
+ this.exitStatsIntervalLength = parsedStatsEndData[1];
}
private void parseExitKibibytesWrittenLine(String line,
String lineNoOpt, String[] partsNoOpt)
throws DescriptorParseException {
- /* TODO Implement me. */
+ this.exitKibibytesWritten = ParseHelper.
+ parseCommaSeparatedKeyValueList(line, partsNoOpt, 1, 0);
}
private void parseExitKibibytesReadLine(String line, String lineNoOpt,
String[] partsNoOpt) throws DescriptorParseException {
- /* TODO Implement me. */
+ this.exitKibibytesRead = ParseHelper.parseCommaSeparatedKeyValueList(
+ line, partsNoOpt, 1, 0);
}
private void parseExitStreamsOpenedLine(String line, String lineNoOpt,
String[] partsNoOpt) throws DescriptorParseException {
- /* TODO Implement me. */
+ this.exitStreamsOpened = ParseHelper.parseCommaSeparatedKeyValueList(
+ line, partsNoOpt, 1, 0);
}
private void parseBridgeStatsEndLine(String line, String lineNoOpt,
String[] partsNoOpt) throws DescriptorParseException {
- /* TODO Implement me. */
+ long[] parsedStatsEndData = this.parseStatsEndLine(line, partsNoOpt,
+ 5);
+ this.bridgeStatsEndMillis = parsedStatsEndData[0];
+ this.bridgeStatsIntervalLength = parsedStatsEndData[1];
}
private void parseBridgeStatsIpsLine(String line, String lineNoOpt,
String[] partsNoOpt) throws DescriptorParseException {
- /* TODO Implement me. */
+ this.bridgeIps = ParseHelper.parseCommaSeparatedKeyValueList(line,
+ partsNoOpt, 1, 2);
}
private void parseRouterSignatureLine(String line, String lineNoOpt,
@@ -400,79 +518,89 @@ public class ExtraInfoDescriptorImpl extends DescriptorImpl
return this.writeHistory;
}
+ private String geoipDbDigest;
public String getGeoipDbDigest() {
- /* TODO Implement me. */
- throw new UnsupportedOperationException();
+ return this.geoipDbDigest;
}
+ private long dirreqStatsEndMillis = -1L;
public long getDirreqStatsEndMillis() {
- /* TODO Implement me. */
- throw new UnsupportedOperationException();
+ return this.dirreqStatsEndMillis;
}
+ private long dirreqStatsIntervalLength = -1L;
public long getDirreqStatsIntervalLength() {
- /* TODO Implement me. */
- throw new UnsupportedOperationException();
+ return this.dirreqStatsIntervalLength;
}
+ private SortedMap<String, Integer> dirreqV2Ips;
public SortedMap<String, Integer> getDirreqV2Ips() {
- /* TODO Implement me. */
- throw new UnsupportedOperationException();
+ return this.dirreqV2Ips == null ? null :
+ new TreeMap<String, Integer>(this.dirreqV2Ips);
}
+ private SortedMap<String, Integer> dirreqV3Ips;
public SortedMap<String, Integer> getDirreqV3Ips() {
- /* TODO Implement me. */
- throw new UnsupportedOperationException();
+ return this.dirreqV3Ips == null ? null :
+ new TreeMap<String, Integer>(this.dirreqV3Ips);
}
+ private SortedMap<String, Integer> dirreqV2Reqs;
public SortedMap<String, Integer> getDirreqV2Reqs() {
- /* TODO Implement me. */
- throw new UnsupportedOperationException();
+ return this.dirreqV2Reqs == null ? null :
+ new TreeMap<String, Integer>(this.dirreqV2Reqs);
}
+ private SortedMap<String, Integer> dirreqV3Reqs;
public SortedMap<String, Integer> getDirreqV3Reqs() {
- /* TODO Implement me. */
- throw new UnsupportedOperationException();
+ return this.dirreqV3Reqs == null ? null :
+ new TreeMap<String, Integer>(this.dirreqV3Reqs);
}
+ private double dirreqV2Share = -1.0;
public double getDirreqV2Share() {
- /* TODO Implement me. */
- throw new UnsupportedOperationException();
+ return this.dirreqV2Share;
}
+ private double dirreqV3Share = -1.0;
public double getDirreqV3Share() {
- /* TODO Implement me. */
- throw new UnsupportedOperationException();
+ return this.dirreqV3Share;
}
+ private SortedMap<String, Integer> dirreqV2Resp;
public SortedMap<String, Integer> getDirreqV2Resp() {
- /* TODO Implement me. */
- throw new UnsupportedOperationException();
+ return this.dirreqV2Resp == null ? null :
+ new TreeMap<String, Integer>(this.dirreqV2Resp);
}
+ private SortedMap<String, Integer> dirreqV3Resp;
public SortedMap<String, Integer> getDirreqV3Resp() {
- /* TODO Implement me. */
- throw new UnsupportedOperationException();
+ return this.dirreqV3Resp == null ? null :
+ new TreeMap<String, Integer>(this.dirreqV3Resp);
}
+ private SortedMap<String, Integer> dirreqV2DirectDl;
public SortedMap<String, Integer> getDirreqV2DirectDl() {
- /* TODO Implement me. */
- throw new UnsupportedOperationException();
+ return this.dirreqV2DirectDl == null ? null :
+ new TreeMap<String, Integer>(this.dirreqV2DirectDl);
}
+ private SortedMap<String, Integer> dirreqV3DirectDl;
public SortedMap<String, Integer> getDirreqV3DirectDl() {
- /* TODO Implement me. */
- throw new UnsupportedOperationException();
+ return this.dirreqV3DirectDl == null ? null :
+ new TreeMap<String, Integer>(this.dirreqV3DirectDl);
}
+ private SortedMap<String, Integer> dirreqV2TunneledDl;
public SortedMap<String, Integer> getDirreqV2TunneledDl() {
- /* TODO Implement me. */
- throw new UnsupportedOperationException();
+ return this.dirreqV2TunneledDl == null ? null :
+ new TreeMap<String, Integer>(this.dirreqV2TunneledDl);
}
+ private SortedMap<String, Integer> dirreqV3TunneledDl;
public SortedMap<String, Integer> getDirreqV3TunneledDl() {
- /* TODO Implement me. */
- throw new UnsupportedOperationException();
+ return this.dirreqV3TunneledDl == null ? null :
+ new TreeMap<String, Integer>(this.dirreqV3TunneledDl);
}
private BandwidthHistory dirreqReadHistory;
@@ -485,104 +613,142 @@ public class ExtraInfoDescriptorImpl extends DescriptorImpl
return this.dirreqWriteHistory;
}
+ private long entryStatsEndMillis = -1L;
public long getEntryStatsEndMillis() {
- /* TODO Implement me. */
- throw new UnsupportedOperationException();
+ return this.entryStatsEndMillis;
}
+ private long entryStatsIntervalLength = -1L;
public long getEntryStatsIntervalLength() {
- /* TODO Implement me. */
- throw new UnsupportedOperationException();
+ return this.entryStatsIntervalLength;
}
+ private SortedMap<String, Integer> entryIps;
public SortedMap<String, Integer> getEntryIps() {
- /* TODO Implement me. */
- throw new UnsupportedOperationException();
+ return this.entryIps == null ? null :
+ new TreeMap<String, Integer>(this.entryIps);
}
+ private long cellStatsEndMillis = -1L;
public long getCellStatsEndMillis() {
- /* TODO Implement me. */
- throw new UnsupportedOperationException();
+ return this.cellStatsEndMillis;
}
+ private long cellStatsIntervalLength = -1L;
public long getCellStatsIntervalLength() {
- /* TODO Implement me. */
- throw new UnsupportedOperationException();
+ return this.cellStatsIntervalLength;
}
+ private List<Integer> cellProcessedCells;
public List<Integer> getCellProcessedCells() {
- /* TODO Implement me. */
- throw new UnsupportedOperationException();
+ return this.cellProcessedCells == null ? null :
+ new ArrayList<Integer>(this.cellProcessedCells);
}
- public List<Integer> getCellQueueCells() {
- /* TODO Implement me. */
- throw new UnsupportedOperationException();
+ private List<Double> cellQueuedCells;
+ public List<Double> getCellQueuedCells() {
+ return this.cellQueuedCells == null ? null :
+ new ArrayList<Double>(this.cellQueuedCells);
}
+ private List<Integer> cellTimeInQueue;
public List<Integer> getCellTimeInQueue() {
- /* TODO Implement me. */
- throw new UnsupportedOperationException();
+ return this.cellTimeInQueue == null ? null :
+ new ArrayList<Integer>(this.cellTimeInQueue);
}
+ private int cellCircuitsPerDecile = -1;
public int getCellCircuitsPerDecile() {
- /* TODO Implement me. */
- throw new UnsupportedOperationException();
+ return this.cellCircuitsPerDecile;
}
+ private long connBiDirectStatsEndMillis = -1L;
public long getConnBiDirectStatsEndMillis() {
- /* TODO Implement me. */
- throw new UnsupportedOperationException();
+ return this.connBiDirectStatsEndMillis;
}
- public long getConnBiDirectIntervalLength() {
- /* TODO Implement me. */
- throw new UnsupportedOperationException();
+ private long connBiDirectStatsIntervalLength = -1L;
+ public long getConnBiDirectStatsIntervalLength() {
+ return this.connBiDirectStatsIntervalLength;
}
+ private int connBiDirectBelow = -1;
public int getConnBiDirectBelow() {
- /* TODO Implement me. */
- throw new UnsupportedOperationException();
+ return this.connBiDirectBelow;
}
+ private int connBiDirectRead = -1;
public int getConnBiDirectRead() {
- /* TODO Implement me. */
- throw new UnsupportedOperationException();
+ return this.connBiDirectRead;
}
+ private int connBiDirectWrite = -1;
public int getConnBiDirectWrite() {
- /* TODO Implement me. */
- throw new UnsupportedOperationException();
+ return this.connBiDirectWrite;
}
+ private int connBiDirectBoth = -1;
public int getConnBiDirectBoth() {
- /* TODO Implement me. */
- throw new UnsupportedOperationException();
+ return this.connBiDirectBoth;
}
+ private long exitStatsEndMillis = -1L;
public long getExitStatsEndMillis() {
- /* TODO Implement me. */
- throw new UnsupportedOperationException();
+ return this.exitStatsEndMillis;
}
+ private long exitStatsIntervalLength = -1L;
public long getExitStatsIntervalLength() {
- /* TODO Implement me. */
- throw new UnsupportedOperationException();
+ return this.exitStatsIntervalLength;
+ }
+
+ /* TODO Add custom comparators to the maps returned by all three
+ * exit-stats methods to sort keys alphanumerically, not
+ * alphabetically. */
+
+ private SortedMap<String, Integer> exitKibibytesWritten;
+ public SortedMap<String, Integer> getExitKibibytesWritten() {
+ return this.exitKibibytesWritten == null ? null :
+ new TreeMap<String, Integer>(this.exitKibibytesWritten);
+ }
+
+ private SortedMap<String, Integer> exitKibibytesRead;
+ public SortedMap<String, Integer> getExitKibibytesRead() {
+ return this.exitKibibytesRead == null ? null :
+ new TreeMap<String, Integer>(this.exitKibibytesRead);
+ }
+
+ private SortedMap<String, Integer> exitStreamsOpened;
+ public SortedMap<String, Integer> getExitStreamsOpened() {
+ return this.exitStreamsOpened == null ? null :
+ new TreeMap<String, Integer>(this.exitStreamsOpened);
+ }
+
+ private long geoipStartTimeMillis = -1L;
+ public long getGeoipStartTimeMillis() {
+ return this.geoipStartTimeMillis;
+ }
+
+ private SortedMap<String, Integer> geoipClientOrigins;
+ public SortedMap<String, Integer> getGeoipClientOrigins() {
+ return this.geoipClientOrigins == null ? null :
+ new TreeMap<String, Integer>(this.geoipClientOrigins);
}
- public SortedMap<Integer, Integer> getExitKibibytesWritten() {
- /* TODO Implement me. */
- throw new UnsupportedOperationException();
+ private long bridgeStatsEndMillis = -1L;
+ public long getBridgeStatsEndMillis() {
+ return this.bridgeStatsEndMillis;
}
- public SortedMap<Integer, Integer> getExitKibibytesRead() {
- /* TODO Implement me. */
- throw new UnsupportedOperationException();
+ private long bridgeStatsIntervalLength = -1L;
+ public long getBridgeStatsIntervalLength() {
+ return this.bridgeStatsIntervalLength;
}
- public SortedMap<Integer, Integer> getExitStreamsOpened() {
- /* TODO Implement me. */
- throw new UnsupportedOperationException();
+ private SortedMap<String, Integer> bridgeIps;
+ public SortedMap<String, Integer> getBridgeIps() {
+ return this.bridgeIps == null ? null :
+ new TreeMap<String, Integer>(this.bridgeIps);
}
}
diff --git a/src/org/torproject/descriptor/impl/ParseHelper.java b/src/org/torproject/descriptor/impl/ParseHelper.java
index facd4ea..2779d5a 100644
--- a/src/org/torproject/descriptor/impl/ParseHelper.java
+++ b/src/org/torproject/descriptor/impl/ParseHelper.java
@@ -4,6 +4,8 @@ package org.torproject.descriptor.impl;
import java.text.ParseException;
import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.List;
import java.util.SortedMap;
import java.util.TimeZone;
import java.util.TreeMap;
@@ -61,6 +63,16 @@ public class ParseHelper {
return port;
}
+ public static long parseSeconds(String line, String secondsString)
+ throws DescriptorParseException {
+ try {
+ return Long.parseLong(secondsString);
+ } catch (NumberFormatException e) {
+ throw new DescriptorParseException("'" + secondsString + "' in "
+ + "line '" + line + "' is not a valid time in seconds.");
+ }
+ }
+
public static String parseExitPattern(String line, String exitPattern)
throws DescriptorParseException {
if (!exitPattern.contains(":")) {
@@ -185,5 +197,96 @@ public class ParseHelper {
return Hex.encodeHexString(Base64.decodeBase64(base64String + "=")).
toUpperCase();
}
+
+ public static SortedMap<String, Integer>
+ parseCommaSeparatedKeyValueList(String line, String[] partsNoOpt,
+ int index, int keyLength) throws DescriptorParseException {
+ SortedMap<String, Integer> result = new TreeMap<String, Integer>();
+ if (partsNoOpt.length < index) {
+ throw new DescriptorParseException("Line '" + line + "' does not "
+ + "contain a key-value list at index " + index + ".");
+ } else if (partsNoOpt.length > index + 1 ) {
+ throw new DescriptorParseException("Line '" + line + "' contains "
+ + "unrecognized values beyond the expected key-value list at "
+ + "index " + index + ".");
+ } else if (partsNoOpt.length > index) {
+ String[] listElements = partsNoOpt[index].split(",");
+ for (String listElement : listElements) {
+ String[] keyAndValue = listElement.split("=");
+ String key = null;
+ int value = -1;
+ if (keyAndValue.length == 2 && (keyLength == 0 ||
+ keyAndValue[0].length() == keyLength)) {
+ try {
+ value = Integer.parseInt(keyAndValue[1]);
+ key = keyAndValue[0];
+ } catch (NumberFormatException e) {
+ /* Handle below. */
+ }
+ }
+ if (key == null) {
+ throw new DescriptorParseException("Line '" + line + "' "
+ + "contains an illegal key or value in list element '"
+ + listElement + "'.");
+ }
+ result.put(key, value);
+ }
+ }
+ return result;
+ }
+
+ public static List<Integer> parseCommaSeparatedIntegerValueList(
+ String line, String[] partsNoOpt, int index)
+ throws DescriptorParseException {
+ List<Integer> result = new ArrayList<Integer>();
+ if (partsNoOpt.length < index) {
+ throw new DescriptorParseException("Line '" + line + "' does not "
+ + "contain a comma-separated value list at index " + index
+ + ".");
+ } else if (partsNoOpt.length > index + 1 ) {
+ throw new DescriptorParseException("Line '" + line + "' contains "
+ + "unrecognized values beyond the expected comma-separated "
+ + "value list at index " + index + ".");
+ } else if (partsNoOpt.length > index) {
+ String[] listElements = partsNoOpt[index].split(",");
+ for (String listElement : listElements) {
+ try {
+ result.add(Integer.parseInt(listElement));
+ } catch (NumberFormatException e) {
+ throw new DescriptorParseException("Line '" + line + "' "
+ + "contains an illegal value in list element '"
+ + listElement + "'.");
+ }
+ }
+ }
+ return result;
+ }
+
+ public static List<Double> parseCommaSeparatedDoubleValueList(
+ String line, String[] partsNoOpt, int index)
+ throws DescriptorParseException {
+ List<Double> result = new ArrayList<Double>();
+ if (partsNoOpt.length < index) {
+ throw new DescriptorParseException("Line '" + line + "' does not "
+ + "contain a comma-separated value list at index " + index
+ + ".");
+ } else if (partsNoOpt.length > index + 1 ) {
+ throw new DescriptorParseException("Line '" + line + "' contains "
+ + "unrecognized values beyond the expected comma-separated "
+ + "value list at index " + index + ".");
+ } else if (partsNoOpt.length > index) {
+ String[] listElements = partsNoOpt[index].split(",");
+ for (String listElement : listElements) {
+ try {
+ result.add(Double.parseDouble(listElement));
+ } catch (NumberFormatException e) {
+ throw new DescriptorParseException("Line '" + line + "' "
+ + "contains an illegal value in list element '"
+ + listElement + "'.");
+ }
+ }
+ }
+ return result;
+ }
}
diff --git a/test/org/torproject/descriptor/impl/ServerDescriptorImplTest.java b/test/org/torproject/descriptor/impl/ServerDescriptorImplTest.java
index 439e5bb..889fce4 100644
--- a/test/org/torproject/descriptor/impl/ServerDescriptorImplTest.java
+++ b/test/org/torproject/descriptor/impl/ServerDescriptorImplTest.java
@@ -487,6 +487,11 @@ public class ServerDescriptorImplTest {
+ "04:03:19");
}
+ @Test(expected = DescriptorParseException.class)
+ public void testPublishedNoTime() throws DescriptorParseException {
+ DescriptorBuilder.createWithPublishedLine("published 2012-01-01");
+ }
+
@Test()
public void testFingerprintNoOpt() throws DescriptorParseException {
ServerDescriptor descriptor = DescriptorBuilder.