commit a3d89ee7886a18f3bddc1efc66bf47e37c082d53 Author: Karsten Loesing karsten.loesing@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)