commit a3d89ee7886a18f3bddc1efc66bf47e37c082d53
Author: Karsten Loesing <karsten.loesing(a)gmx.net>
Date: Fri Jun 1 11:45:06 2012 +0200
Support parsing GetTor statistics files.
---
.../torproject/descriptor/GetTorStatistics.java | 13 +++
.../torproject/descriptor/impl/DescriptorImpl.java | 3 +
.../descriptor/impl/GetTorStatisticsImpl.java | 78 ++++++++++++++++++++
.../descriptor/impl/NetworkStatusEntryImpl.java | 2 +-
.../torproject/descriptor/impl/ParseHelper.java | 27 ++++++-
.../impl/RelayNetworkStatusConsensusImpl.java | 7 +-
.../impl/RelayNetworkStatusVoteImpl.java | 3 +-
7 files changed, 126 insertions(+), 7 deletions(-)
diff --git a/src/org/torproject/descriptor/GetTorStatistics.java b/src/org/torproject/descriptor/GetTorStatistics.java
new file mode 100644
index 0000000..b6ba1e7
--- /dev/null
+++ b/src/org/torproject/descriptor/GetTorStatistics.java
@@ -0,0 +1,13 @@
+package org.torproject.descriptor;
+
+import java.util.SortedMap;
+
+public interface GetTorStatistics {
+
+ /* Return the date of these GetTor statistics in milliseconds since
+ * 1970-01-01 00:00:00. */
+ public long getDateMillis();
+
+ /* Return the number of downloaded packages. */
+ public SortedMap<String, Integer> getDownloadedPackages();
+}
diff --git a/src/org/torproject/descriptor/impl/DescriptorImpl.java b/src/org/torproject/descriptor/impl/DescriptorImpl.java
index c1c4a9a..98be2d8 100644
--- a/src/org/torproject/descriptor/impl/DescriptorImpl.java
+++ b/src/org/torproject/descriptor/impl/DescriptorImpl.java
@@ -88,6 +88,9 @@ public abstract class DescriptorImpl implements Descriptor {
} else if (firstLines.startsWith("@type torperf 1.0\n")) {
parsedDescriptors.addAll(TorperfResultImpl.parseTorperfResults(
rawDescriptorBytes, failUnrecognizedDescriptorLines));
+ } else if (firstLines.startsWith("@type gettor 1.0\n")) {
+ parsedDescriptors.addAll(GetTorStatisticsImpl.parseGetTorStatistics(
+ rawDescriptorBytes, failUnrecognizedDescriptorLines));
} else {
throw new DescriptorParseException("Could not detect descriptor "
+ "type in descriptor starting with '" + firstLines + "'.");
diff --git a/src/org/torproject/descriptor/impl/GetTorStatisticsImpl.java b/src/org/torproject/descriptor/impl/GetTorStatisticsImpl.java
new file mode 100644
index 0000000..202cf03
--- /dev/null
+++ b/src/org/torproject/descriptor/impl/GetTorStatisticsImpl.java
@@ -0,0 +1,78 @@
+/* Copyright 2012 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Scanner;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import org.torproject.descriptor.Descriptor;
+import org.torproject.descriptor.GetTorStatistics;
+
+public class GetTorStatisticsImpl extends DescriptorImpl
+ implements GetTorStatistics {
+
+ public static List<Descriptor> parseGetTorStatistics(
+ byte[] rawDescriptorBytes, boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ if (rawDescriptorBytes.length == 0) {
+ throw new DescriptorParseException("Descriptor is empty.");
+ }
+ List<Descriptor> parsedDescriptors = new ArrayList<Descriptor>();
+ String descriptorString = new String(rawDescriptorBytes);
+ Scanner s = new Scanner(descriptorString).useDelimiter("\n");
+ while (s.hasNext()) {
+ String line = s.next();
+ if (line.startsWith("@type gettor ")) {
+ String[] parts = line.split(" ");
+ if (parts.length != 3) {
+ throw new DescriptorParseException("Illegal line '" + line
+ + "'.");
+ }
+ String version = parts[2];
+ if (!version.startsWith("1.")) {
+ throw new DescriptorParseException("Unsupported version in "
+ + " line '" + line + "'.");
+ }
+ } else {
+ parsedDescriptors.add(new GetTorStatisticsImpl(line.getBytes(),
+ failUnrecognizedDescriptorLines));
+ }
+ }
+ return parsedDescriptors;
+ }
+
+ protected GetTorStatisticsImpl(byte[] rawDescriptorBytes,
+ boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ super(rawDescriptorBytes, failUnrecognizedDescriptorLines, false);
+ this.parseGetTorStatisticsLine(new String(rawDescriptorBytes));
+ }
+
+ private void parseGetTorStatisticsLine(String line)
+ throws DescriptorParseException {
+ if (line.isEmpty()) {
+ throw new DescriptorParseException("Blank lines are not allowed.");
+ }
+ String[] parts = line.split(" ");
+ if (parts.length < 3) {
+ throw new DescriptorParseException("Illegal GetTor statistics line "
+ + "'" + line + "'.");
+ }
+ this.dateMillis = ParseHelper.parseDateAtIndex(line, parts, 0);
+ this.downloadedPackages = ParseHelper.parseKeyValuePairs(line, parts,
+ 2, ":");
+ }
+
+ private long dateMillis;
+ public long getDateMillis() {
+ return this.dateMillis;
+ }
+
+ private SortedMap<String, Integer> downloadedPackages;
+ public SortedMap<String, Integer> getDownloadedPackages() {
+ return new TreeMap<String, Integer>(this.downloadedPackages);
+ }
+}
diff --git a/src/org/torproject/descriptor/impl/NetworkStatusEntryImpl.java b/src/org/torproject/descriptor/impl/NetworkStatusEntryImpl.java
index 108d563..423e21e 100644
--- a/src/org/torproject/descriptor/impl/NetworkStatusEntryImpl.java
+++ b/src/org/torproject/descriptor/impl/NetworkStatusEntryImpl.java
@@ -151,7 +151,7 @@ public class NetworkStatusEntryImpl implements NetworkStatusEntry {
throws DescriptorParseException {
this.parsedAtMostOnceKeyword("w");
SortedMap<String, Integer> pairs = ParseHelper.parseKeyValuePairs(
- line, parts, 1);
+ line, parts, 1, "=");
if (pairs.isEmpty()) {
throw new DescriptorParseException("Illegal line '" + line + "'.");
}
diff --git a/src/org/torproject/descriptor/impl/ParseHelper.java b/src/org/torproject/descriptor/impl/ParseHelper.java
index a3f907d..61d1eb0 100644
--- a/src/org/torproject/descriptor/impl/ParseHelper.java
+++ b/src/org/torproject/descriptor/impl/ParseHelper.java
@@ -149,6 +149,28 @@ public class ParseHelper {
return result;
}
+ public static long parseDateAtIndex(String line, String[] parts,
+ int dateIndex) throws DescriptorParseException {
+ if (dateIndex >= parts.length) {
+ throw new DescriptorParseException("Line '" + line + "' does not "
+ + "contain a date at the expected position.");
+ }
+ long result = -1L;
+ try {
+ SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
+ dateFormat.setLenient(false);
+ dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ result = dateFormat.parse(parts[dateIndex]).getTime();
+ } catch (ParseException e) {
+ /* Leave result at -1L. */
+ }
+ if (result < 0L || result / 1000L > (long) Integer.MAX_VALUE) {
+ throw new DescriptorParseException("Illegal date format in line '"
+ + line + "'.");
+ }
+ return result;
+ }
+
private static Pattern twentyByteHexPattern =
Pattern.compile("^[0-9a-fA-F]{40}$");
public static String parseTwentyByteHexString(String line,
@@ -161,11 +183,12 @@ public class ParseHelper {
}
public static SortedMap<String, Integer> parseKeyValuePairs(String line,
- String[] parts, int startIndex) throws DescriptorParseException {
+ String[] parts, int startIndex, String separatorString)
+ throws DescriptorParseException {
SortedMap<String, Integer> result = new TreeMap<String, Integer>();
for (int i = startIndex; i < parts.length; i++) {
String pair = parts[i];
- String[] pairParts = pair.split("=");
+ String[] pairParts = pair.split(separatorString);
if (pairParts.length != 2) {
throw new DescriptorParseException("Illegal key-value pair in "
+ "line '" + line + "'.");
diff --git a/src/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImpl.java b/src/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImpl.java
index f296fa6..35cb316 100644
--- a/src/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImpl.java
+++ b/src/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImpl.java
@@ -257,13 +257,14 @@ public class RelayNetworkStatusConsensusImpl extends NetworkStatusImpl
private void parseParamsLine(String line, String[] parts)
throws DescriptorParseException {
- this.consensusParams = ParseHelper.parseKeyValuePairs(line, parts, 1);
+ this.consensusParams = ParseHelper.parseKeyValuePairs(line, parts, 1,
+ "=");
}
private void parseBandwidthWeightsLine(String line, String[] parts)
throws DescriptorParseException {
- this.bandwidthWeights = ParseHelper.parseKeyValuePairs(line, parts,
- 1);
+ this.bandwidthWeights = ParseHelper.parseKeyValuePairs(line, parts, 1,
+ "=");
}
private String consensusDigest;
diff --git a/src/org/torproject/descriptor/impl/RelayNetworkStatusVoteImpl.java b/src/org/torproject/descriptor/impl/RelayNetworkStatusVoteImpl.java
index d5b87c1..3695074 100644
--- a/src/org/torproject/descriptor/impl/RelayNetworkStatusVoteImpl.java
+++ b/src/org/torproject/descriptor/impl/RelayNetworkStatusVoteImpl.java
@@ -249,7 +249,8 @@ public class RelayNetworkStatusVoteImpl extends NetworkStatusImpl
private void parseParamsLine(String line, String[] parts)
throws DescriptorParseException {
- this.consensusParams = ParseHelper.parseKeyValuePairs(line, parts, 1);
+ this.consensusParams = ParseHelper.parseKeyValuePairs(line, parts, 1,
+ "=");
}
private void parseDirSourceLine(String line, String[] parts)