[tor-commits] [onionoo/master] Use Gson to format JSON details documents.
karsten at torproject.org
karsten at torproject.org
Fri May 9 06:35:54 UTC 2014
commit 368e81088f736598337a5c4c19ebaac3f925023d
Author: Karsten Loesing <karsten.loesing at gmx.net>
Date: Sun Apr 20 20:23:40 2014 +0200
Use Gson to format JSON details documents.
---
src/org/torproject/onionoo/DetailsDocument.java | 347 +++++++++++++++++++-
.../torproject/onionoo/DetailsDocumentWriter.java | 293 ++++++-----------
src/org/torproject/onionoo/ResponseBuilder.java | 162 ++++++---
3 files changed, 562 insertions(+), 240 deletions(-)
diff --git a/src/org/torproject/onionoo/DetailsDocument.java b/src/org/torproject/onionoo/DetailsDocument.java
index b51b6cd..6c7e0ae 100644
--- a/src/org/torproject/onionoo/DetailsDocument.java
+++ b/src/org/torproject/onionoo/DetailsDocument.java
@@ -1,8 +1,353 @@
-/* Copyright 2013 The Tor Project
+/* Copyright 2013--2014 The Tor Project
* See LICENSE for licensing information */
package org.torproject.onionoo;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang.StringEscapeUtils;
+
class DetailsDocument extends Document {
+ /* We must ensure that details files only contain ASCII characters
+ * and no UTF-8 characters. While UTF-8 characters are perfectly
+ * valid in JSON, this would break compatibility with existing files
+ * pretty badly. We do this by escaping non-ASCII characters, e.g.,
+ * \u00F2. Gson won't treat this as UTF-8, but will think that we want
+ * to write six characters '\', 'u', '0', '0', 'F', '2'. The only thing
+ * we'll have to do is to change back the '\\' that Gson writes for the
+ * '\'. */
+ private static String escapeJSON(String s) {
+ return s == null ? null :
+ StringEscapeUtils.escapeJavaScript(s).replaceAll("\\\\'", "'");
+ }
+ private static String unescapeJSON(String s) {
+ return s == null ? null :
+ StringEscapeUtils.unescapeJavaScript(s.replaceAll("'", "\\'"));
+ }
+
+ private String nickname;
+ public void setNickname(String nickname) {
+ this.nickname = nickname;
+ }
+ public String getNickname() {
+ return this.nickname;
+ }
+
+ private String fingerprint;
+ public void setFingerprint(String fingerprint) {
+ this.fingerprint = fingerprint;
+ }
+ public String getFingerprint() {
+ return this.fingerprint;
+ }
+
+ private String hashed_fingerprint;
+ public void setHashedFingerprint(String hashedFingerprint) {
+ this.hashed_fingerprint = hashedFingerprint;
+ }
+ public String getHashedFingerprint() {
+ return this.hashed_fingerprint;
+ }
+
+ private List<String> or_addresses;
+ public void setOrAddresses(List<String> orAddresses) {
+ this.or_addresses = orAddresses;
+ }
+ public List<String> getOrAddresses() {
+ return this.or_addresses;
+ }
+
+ private List<String> exit_addresses;
+ public void setExitAddresses(List<String> exitAddresses) {
+ this.exit_addresses = exitAddresses;
+ }
+ public List<String> getExitAddresses() {
+ return this.exit_addresses;
+ }
+
+ private String dir_address;
+ public void setDirAddress(String dirAddress) {
+ this.dir_address = dirAddress;
+ }
+ public String getDirAddress() {
+ return this.dir_address;
+ }
+
+ private String last_seen;
+ public void setLastSeen(String lastSeen) {
+ this.last_seen = lastSeen;
+ }
+ public String getLastSeen() {
+ return this.last_seen;
+ }
+
+ private String last_changed_address_or_port;
+ public void setLastChangedAddressOrPort(
+ String lastChangedAddressOrPort) {
+ this.last_changed_address_or_port = lastChangedAddressOrPort;
+ }
+ public String getLastChangedAddressOrPort() {
+ return this.last_changed_address_or_port;
+ }
+
+ private String first_seen;
+ public void setFirstSeen(String firstSeen) {
+ this.first_seen = firstSeen;
+ }
+ public String getFirstSeen() {
+ return this.first_seen;
+ }
+
+ private Boolean running;
+ public void setRunning(Boolean running) {
+ this.running = running;
+ }
+ public Boolean getRunning() {
+ return this.running;
+ }
+
+ private List<String> flags;
+ public void setFlags(List<String> flags) {
+ this.flags = flags;
+ }
+ public List<String> getFlags() {
+ return this.flags;
+ }
+
+ private String country;
+ public void setCountry(String country) {
+ this.country = country;
+ }
+ public String getCountry() {
+ return this.country;
+ }
+
+ private String country_name;
+ public void setCountryName(String countryName) {
+ this.country_name = escapeJSON(countryName);
+ }
+ public String getCountryName() {
+ return unescapeJSON(this.country_name);
+ }
+
+ private String region_name;
+ public void setRegionName(String regionName) {
+ this.region_name = escapeJSON(regionName);
+ }
+ public String getRegionName() {
+ return unescapeJSON(this.region_name);
+ }
+
+ private String city_name;
+ public void setCityName(String cityName) {
+ this.city_name = escapeJSON(cityName);
+ }
+ public String getCityName() {
+ return unescapeJSON(this.city_name);
+ }
+
+ private Float latitude;
+ public void setLatitude(Float latitude) {
+ this.latitude = latitude;
+ }
+ public Float getLatitude() {
+ return this.latitude;
+ }
+
+ private Float longitude;
+ public void setLongitude(Float longitude) {
+ this.longitude = longitude;
+ }
+ public Float getLongitude() {
+ return this.longitude;
+ }
+
+ private String as_number;
+ public void setAsNumber(String asNumber) {
+ this.as_number = escapeJSON(asNumber);
+ }
+ public String getAsNumber() {
+ return unescapeJSON(this.as_number);
+ }
+
+ private String as_name;
+ public void setAsName(String asName) {
+ this.as_name = escapeJSON(asName);
+ }
+ public String getAsName() {
+ return unescapeJSON(this.as_name);
+ }
+
+ private Long consensus_weight;
+ public void setConsensusWeight(Long consensusWeight) {
+ this.consensus_weight = consensusWeight;
+ }
+ public Long getConsensusWeight() {
+ return this.consensus_weight;
+ }
+
+ private String host_name;
+ public void setHostName(String hostName) {
+ this.host_name = escapeJSON(hostName);
+ }
+ public String getHostName() {
+ return unescapeJSON(this.host_name);
+ }
+
+ private String last_restarted;
+ public void setLastRestarted(String lastRestarted) {
+ this.last_restarted = lastRestarted;
+ }
+ public String getLastRestarted() {
+ return this.last_restarted;
+ }
+
+ private Integer bandwidth_rate;
+ public void setBandwidthRate(Integer bandwidthRate) {
+ this.bandwidth_rate = bandwidthRate;
+ }
+ public Integer getBandwidthRate() {
+ return this.bandwidth_rate;
+ }
+
+ private Integer bandwidth_burst;
+ public void setBandwidthBurst(Integer bandwidthBurst) {
+ this.bandwidth_burst = bandwidthBurst;
+ }
+ public Integer getBandwidthBurst() {
+ return this.bandwidth_burst;
+ }
+
+ private Integer observed_bandwidth;
+ public void setObservedBandwidth(Integer observedBandwidth) {
+ this.observed_bandwidth = observedBandwidth;
+ }
+ public Integer getObservedBandwidth() {
+ return this.observed_bandwidth;
+ }
+
+ private Integer advertised_bandwidth;
+ public void setAdvertisedBandwidth(Integer advertisedBandwidth) {
+ this.advertised_bandwidth = advertisedBandwidth;
+ }
+ public Integer getAdvertisedBandwidth() {
+ return this.advertised_bandwidth;
+ }
+
+ private List<String> exit_policy;
+ public void setExitPolicy(List<String> exitPolicy) {
+ this.exit_policy = exitPolicy;
+ }
+ public List<String> getExitPolicy() {
+ return this.exit_policy;
+ }
+
+ private Map<String, List<String>> exit_policy_summary;
+ public void setExitPolicySummary(
+ Map<String, List<String>> exitPolicySummary) {
+ this.exit_policy_summary = exitPolicySummary;
+ }
+ public Map<String, List<String>> getExitPolicySummary() {
+ return this.exit_policy_summary;
+ }
+
+ private Map<String, List<String>> exit_policy_v6_summary;
+ public void setExitPolicyV6Summary(
+ Map<String, List<String>> exitPolicyV6Summary) {
+ this.exit_policy_v6_summary = exitPolicyV6Summary;
+ }
+ public Map<String, List<String>> getExitPolicyV6Summary() {
+ return this.exit_policy_v6_summary;
+ }
+
+ private String contact;
+ public void setContact(String contact) {
+ this.contact = escapeJSON(contact);
+ }
+ public String getContact() {
+ return unescapeJSON(this.contact);
+ }
+
+ private String platform;
+ public void setPlatform(String platform) {
+ this.platform = escapeJSON(platform);
+ }
+ public String getPlatform() {
+ return unescapeJSON(this.platform);
+ }
+
+ private List<String> family;
+ public void setFamily(List<String> family) {
+ this.family = family;
+ }
+ public List<String> getFamily() {
+ return this.family;
+ }
+
+ private Float advertised_bandwidth_fraction;
+ public void setAdvertisedBandwidthFraction(
+ Float advertisedBandwidthFraction) {
+ this.advertised_bandwidth_fraction = advertisedBandwidthFraction;
+ }
+ public Float getAdvertisedBandwidthFraction() {
+ return this.advertised_bandwidth_fraction;
+ }
+
+ private Float consensus_weight_fraction;
+ public void setConsensusWeightFraction(Float consensusWeightFraction) {
+ this.consensus_weight_fraction = consensusWeightFraction;
+ }
+ public Float getConsensusWeightFraction() {
+ return this.consensus_weight_fraction;
+ }
+
+ private Float guard_probability;
+ public void setGuardProbability(Float guardProbability) {
+ this.guard_probability = guardProbability;
+ }
+ public Float getGuardProbability() {
+ return this.guard_probability;
+ }
+
+ private Float middle_probability;
+ public void setMiddleProbability(Float middleProbability) {
+ this.middle_probability = middleProbability;
+ }
+ public Float getMiddleProbability() {
+ return this.middle_probability;
+ }
+
+ private Float exit_probability;
+ public void setExitProbability(Float exitProbability) {
+ this.exit_probability = exitProbability;
+ }
+ public Float getExitProbability() {
+ return this.exit_probability;
+ }
+
+ private Boolean recommended_version;
+ public void setRecommendedVersion(Boolean recommendedVersion) {
+ this.recommended_version = recommendedVersion;
+ }
+ public Boolean getRecommendedVersion() {
+ return this.recommended_version;
+ }
+
+ private Boolean hibernating;
+ public void setHibernating(Boolean hibernating) {
+ this.hibernating = hibernating;
+ }
+ public Boolean getHibernating() {
+ return this.hibernating;
+ }
+
+ private String pool_assignment;
+ public void setPoolAssignment(String poolAssignment) {
+ this.pool_assignment = poolAssignment;
+ }
+ public String getPoolAssignment() {
+ return this.pool_assignment;
+ }
}
diff --git a/src/org/torproject/onionoo/DetailsDocumentWriter.java b/src/org/torproject/onionoo/DetailsDocumentWriter.java
index 950a332..73caf24 100644
--- a/src/org/torproject/onionoo/DetailsDocumentWriter.java
+++ b/src/org/torproject/onionoo/DetailsDocumentWriter.java
@@ -1,6 +1,7 @@
package org.torproject.onionoo;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -10,7 +11,6 @@ import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
-import org.apache.commons.lang.StringEscapeUtils;
import org.torproject.descriptor.Descriptor;
import org.torproject.descriptor.ExitList;
import org.torproject.descriptor.ExitListEntry;
@@ -106,141 +106,63 @@ public class DetailsDocumentWriter implements DescriptorListener,
if (entry == null) {
continue;
}
- String nickname = entry.getNickname();
- String address = entry.getAddress();
+ DetailsDocument detailsDocument = new DetailsDocument();
+ detailsDocument.setNickname(entry.getNickname());
+ detailsDocument.setFingerprint(fingerprint);
List<String> orAddresses = new ArrayList<String>();
- orAddresses.add(address + ":" + entry.getOrPort());
- orAddresses.addAll(entry.getOrAddressesAndPorts());
- StringBuilder orAddressesAndPortsBuilder = new StringBuilder();
- int addressesWritten = 0;
- for (String orAddress : orAddresses) {
- orAddressesAndPortsBuilder.append(
- (addressesWritten++ > 0 ? "," : "") + "\""
- + orAddress.toLowerCase() + "\"");
- }
- String lastSeen = DateTimeHelper.format(entry.getLastSeenMillis());
- String firstSeen = DateTimeHelper.format(
- entry.getFirstSeenMillis());
- String lastChangedOrAddress = DateTimeHelper.format(
- entry.getLastChangedOrAddress());
- String running = entry.getRunning() ? "true" : "false";
- int dirPort = entry.getDirPort();
- String countryCode = entry.getCountryCode();
- Float latitude = entry.getLatitude();
- Float longitude = entry.getLongitude();
- String countryName = entry.getCountryName();
- String regionName = entry.getRegionName();
- String cityName = entry.getCityName();
- String aSNumber = entry.getASNumber();
- String aSName = entry.getASName();
- long consensusWeight = entry.getConsensusWeight();
- String hostName = entry.getHostName();
- double advertisedBandwidthFraction =
- entry.getAdvertisedBandwidthFraction();
- double consensusWeightFraction = entry.getConsensusWeightFraction();
- double guardProbability = entry.getGuardProbability();
- double middleProbability = entry.getMiddleProbability();
- double exitProbability = entry.getExitProbability();
- String defaultPolicy = entry.getDefaultPolicy();
- String portList = entry.getPortList();
- Boolean recommendedVersion = entry.getRecommendedVersion();
- StringBuilder sb = new StringBuilder();
- sb.append("{\n"
- + "\"nickname\":\"" + nickname + "\",\n"
- + "\"fingerprint\":\"" + fingerprint + "\",\n"
- + "\"or_addresses\":[" + orAddressesAndPortsBuilder.toString()
- + "]");
- if (dirPort != 0) {
- sb.append(",\n\"dir_address\":\"" + address + ":" + dirPort
- + "\"");
- }
- sb.append(",\n\"last_seen\":\"" + lastSeen + "\"");
- sb.append(",\n\"first_seen\":\"" + firstSeen + "\"");
- sb.append(",\n\"last_changed_address_or_port\":\""
- + lastChangedOrAddress + "\"");
- sb.append(",\n\"running\":" + running);
- SortedSet<String> relayFlags = entry.getRelayFlags();
- if (!relayFlags.isEmpty()) {
- sb.append(",\n\"flags\":[");
- int written = 0;
- for (String relayFlag : relayFlags) {
- sb.append((written++ > 0 ? "," : "") + "\"" + relayFlag + "\"");
- }
- sb.append("]");
- }
- if (countryCode != null) {
- sb.append(",\n\"country\":\"" + countryCode + "\"");
- }
- if (latitude != null) {
- sb.append(String.format(",\n\"latitude\":%.4f", latitude));
- }
- if (longitude != null) {
- sb.append(String.format(",\n\"longitude\":%.4f", longitude));
- }
- if (countryName != null) {
- sb.append(",\n\"country_name\":\""
- + escapeJSON(countryName) + "\"");
- }
- if (regionName != null) {
- sb.append(",\n\"region_name\":\""
- + escapeJSON(regionName) + "\"");
- }
- if (cityName != null) {
- sb.append(",\n\"city_name\":\""
- + escapeJSON(cityName) + "\"");
- }
- if (aSNumber != null) {
- sb.append(",\n\"as_number\":\""
- + escapeJSON(aSNumber) + "\"");
- }
- if (aSName != null) {
- sb.append(",\n\"as_name\":\""
- + escapeJSON(aSName) + "\"");
- }
- if (consensusWeight >= 0L) {
- sb.append(",\n\"consensus_weight\":"
- + String.valueOf(consensusWeight));
- }
- if (hostName != null) {
- sb.append(",\n\"host_name\":\""
- + escapeJSON(hostName) + "\"");
- }
- if (advertisedBandwidthFraction >= 0.0) {
- sb.append(String.format(
- ",\n\"advertised_bandwidth_fraction\":%.9f",
- advertisedBandwidthFraction));
+ orAddresses.add(entry.getAddress() + ":" + entry.getOrPort());
+ for (String orAddress : entry.getOrAddressesAndPorts()) {
+ orAddresses.add(orAddress.toLowerCase());
}
- if (consensusWeightFraction >= 0.0) {
- sb.append(String.format(",\n\"consensus_weight_fraction\":%.9f",
- consensusWeightFraction));
+ detailsDocument.setOrAddresses(orAddresses);
+ if (entry.getDirPort() != 0) {
+ detailsDocument.setDirAddress(entry.getAddress() + ":"
+ + entry.getDirPort());
}
- if (guardProbability >= 0.0) {
- sb.append(String.format(",\n\"guard_probability\":%.9f",
- guardProbability));
- }
- if (middleProbability >= 0.0) {
- sb.append(String.format(",\n\"middle_probability\":%.9f",
- middleProbability));
- }
- if (exitProbability >= 0.0) {
- sb.append(String.format(",\n\"exit_probability\":%.9f",
- exitProbability));
+ detailsDocument.setLastSeen(DateTimeHelper.format(
+ entry.getLastSeenMillis()));
+ detailsDocument.setFirstSeen(DateTimeHelper.format(
+ entry.getFirstSeenMillis()));
+ detailsDocument.setLastChangedAddressOrPort(
+ DateTimeHelper.format(entry.getLastChangedOrAddress()));
+ detailsDocument.setRunning(entry.getRunning());
+ if (!entry.getRelayFlags().isEmpty()) {
+ detailsDocument.setFlags(new ArrayList<String>(
+ entry.getRelayFlags()));
}
+ detailsDocument.setCountry(entry.getCountryCode());
+ detailsDocument.setLatitude(entry.getLatitude());
+ detailsDocument.setLongitude(entry.getLongitude());
+ detailsDocument.setCountryName(entry.getCountryName());
+ detailsDocument.setRegionName(entry.getRegionName());
+ detailsDocument.setCityName(entry.getCityName());
+ detailsDocument.setAsNumber(entry.getASNumber());
+ detailsDocument.setAsName(entry.getASName());
+ detailsDocument.setConsensusWeight(entry.getConsensusWeight());
+ detailsDocument.setHostName(entry.getHostName());
+ detailsDocument.setAdvertisedBandwidthFraction(
+ (float) entry.getAdvertisedBandwidthFraction());
+ detailsDocument.setConsensusWeightFraction(
+ (float) entry.getConsensusWeightFraction());
+ detailsDocument.setGuardProbability(
+ (float) entry.getGuardProbability());
+ detailsDocument.setMiddleProbability(
+ (float) entry.getMiddleProbability());
+ detailsDocument.setExitProbability(
+ (float) entry.getExitProbability());
+ String defaultPolicy = entry.getDefaultPolicy();
+ String portList = entry.getPortList();
if (defaultPolicy != null && (defaultPolicy.equals("accept") ||
defaultPolicy.equals("reject")) && portList != null) {
- sb.append(",\n\"exit_policy_summary\":{\"" + defaultPolicy
- + "\":[");
- int portsWritten = 0;
- for (String portOrPortRange : portList.split(",")) {
- sb.append((portsWritten++ > 0 ? "," : "")
- + "\"" + portOrPortRange + "\"");
- }
- sb.append("]}");
- }
- if (recommendedVersion != null) {
- sb.append(",\n\"recommended_version\":" + (recommendedVersion ?
- "true" : "false"));
+ Map<String, List<String>> exitPolicySummary =
+ new HashMap<String, List<String>>();
+ List<String> portsOrPortRanges = Arrays.asList(
+ portList.split(","));
+ exitPolicySummary.put(defaultPolicy, portsOrPortRanges);
+ detailsDocument.setExitPolicySummary(exitPolicySummary);
}
+ detailsDocument.setRecommendedVersion(
+ entry.getRecommendedVersion());
/* Add exit addresses if at least one of them is distinct from the
* onion-routing addresses. */
@@ -252,44 +174,39 @@ public class DetailsDocumentWriter implements DescriptorListener,
if (exitAddress.length() > 0 &&
!entry.getAddress().equals(exitAddress) &&
!entry.getOrAddresses().contains(exitAddress)) {
- exitAddresses.add(exitAddress);
+ exitAddresses.add(exitAddress.toLowerCase());
}
}
}
if (!exitAddresses.isEmpty()) {
- sb.append(",\n\"exit_addresses\":[");
- int written = 0;
- for (String exitAddress : exitAddresses) {
- sb.append((written++ > 0 ? "," : "") + "\""
- + exitAddress.toLowerCase() + "\"");
- }
- sb.append("]");
+ detailsDocument.setExitAddresses(new ArrayList<String>(
+ exitAddresses));
}
/* Append descriptor-specific part from details status file. */
DetailsStatus detailsStatus = this.documentStore.retrieve(
- DetailsStatus.class, false, fingerprint);
- if (detailsStatus != null &&
- detailsStatus.getDocumentString().length() > 0) {
- sb.append(",");
- String contact = null;
- Scanner s = new Scanner(detailsStatus.getDocumentString());
- while (s.hasNextLine()) {
- String line = s.nextLine();
- if (line.startsWith("\"desc_published\":")) {
- continue;
- }
- sb.append("\n" + line);
- }
- s.close();
+ DetailsStatus.class, true, fingerprint);
+ if (detailsStatus != null) {
+ detailsDocument.setLastRestarted(
+ detailsStatus.getLastRestarted());
+ detailsDocument.setBandwidthRate(
+ detailsStatus.getBandwidthRate());
+ detailsDocument.setBandwidthBurst(
+ detailsStatus.getBandwidthBurst());
+ detailsDocument.setObservedBandwidth(
+ detailsStatus.getObservedBandwidth());
+ detailsDocument.setAdvertisedBandwidth(
+ detailsStatus.getAdvertisedBandwidth());
+ detailsDocument.setExitPolicy(detailsStatus.getExitPolicy());
+ detailsDocument.setContact(detailsStatus.getContact());
+ detailsDocument.setPlatform(detailsStatus.getPlatform());
+ detailsDocument.setFamily(detailsStatus.getFamily());
+ detailsDocument.setExitPolicyV6Summary(
+ detailsStatus.getExitPolicyV6Summary());
+ detailsDocument.setHibernating(detailsStatus.getHibernating());
}
- /* Finish details string. */
- sb.append("\n}\n");
-
/* Write details file to disk. */
- DetailsDocument detailsDocument = new DetailsDocument();
- detailsDocument.setDocumentString(sb.toString());
this.documentStore.store(detailsDocument, fingerprint);
}
}
@@ -303,62 +220,42 @@ public class DetailsDocumentWriter implements DescriptorListener,
if (entry == null) {
continue;
}
- String nickname = entry.getNickname();
- String lastSeen = DateTimeHelper.format(entry.getLastSeenMillis());
- String firstSeen = DateTimeHelper.format(
- entry.getFirstSeenMillis());
- String running = entry.getRunning() ? "true" : "false";
+ DetailsDocument detailsDocument = new DetailsDocument();
+ detailsDocument.setNickname(entry.getNickname());
+ detailsDocument.setHashedFingerprint(fingerprint);
String address = entry.getAddress();
List<String> orAddresses = new ArrayList<String>();
orAddresses.add(address + ":" + entry.getOrPort());
- orAddresses.addAll(entry.getOrAddressesAndPorts());
- StringBuilder orAddressesAndPortsBuilder = new StringBuilder();
- int addressesWritten = 0;
- for (String orAddress : orAddresses) {
- orAddressesAndPortsBuilder.append(
- (addressesWritten++ > 0 ? "," : "") + "\""
- + orAddress.toLowerCase() + "\"");
- }
- StringBuilder sb = new StringBuilder();
- sb.append("{\"version\":1,\n"
- + "\"nickname\":\"" + nickname + "\",\n"
- + "\"hashed_fingerprint\":\"" + fingerprint + "\",\n"
- + "\"or_addresses\":[" + orAddressesAndPortsBuilder.toString()
- + "],\n\"last_seen\":\"" + lastSeen + "\",\n\"first_seen\":\""
- + firstSeen + "\",\n\"running\":" + running);
-
- SortedSet<String> relayFlags = entry.getRelayFlags();
- if (!relayFlags.isEmpty()) {
- sb.append(",\n\"flags\":[");
- int written = 0;
- for (String relayFlag : relayFlags) {
- sb.append((written++ > 0 ? "," : "") + "\"" + relayFlag + "\"");
- }
- sb.append("]");
+ for (String orAddress : entry.getOrAddressesAndPorts()) {
+ orAddresses.add(orAddress.toLowerCase());
}
+ detailsDocument.setOrAddresses(orAddresses);
+ detailsDocument.setLastSeen(DateTimeHelper.format(
+ entry.getLastSeenMillis()));
+ detailsDocument.setFirstSeen(DateTimeHelper.format(
+ entry.getFirstSeenMillis()));
+ detailsDocument.setRunning(entry.getRunning());
+ detailsDocument.setFlags(new ArrayList<String>(
+ entry.getRelayFlags()));
/* Append descriptor-specific part from details status file. */
DetailsStatus detailsStatus = this.documentStore.retrieve(
- DetailsStatus.class, false, fingerprint);
- if (detailsStatus != null &&
- detailsStatus.getDocumentString().length() > 0) {
- sb.append(",\n" + detailsStatus.getDocumentString());
+ DetailsStatus.class, true, fingerprint);
+ if (detailsStatus != null) {
+ detailsDocument.setLastRestarted(
+ detailsStatus.getLastRestarted());
+ detailsDocument.setAdvertisedBandwidth(
+ detailsStatus.getAdvertisedBandwidth());
+ detailsDocument.setPlatform(detailsStatus.getPlatform());
+ detailsDocument.setPoolAssignment(
+ detailsStatus.getPoolAssignment());
}
- /* Finish details string. */
- sb.append("\n}\n");
-
/* Write details file to disk. */
- DetailsDocument detailsDocument = new DetailsDocument();
- detailsDocument.setDocumentString(sb.toString());
this.documentStore.store(detailsDocument, fingerprint);
}
}
- private static String escapeJSON(String s) {
- return StringEscapeUtils.escapeJavaScript(s).replaceAll("\\\\'", "'");
- }
-
public String getStatsString() {
/* TODO Add statistics string. */
return null;
diff --git a/src/org/torproject/onionoo/ResponseBuilder.java b/src/org/torproject/onionoo/ResponseBuilder.java
index 1d3cef0..77c4b6c 100644
--- a/src/org/torproject/onionoo/ResponseBuilder.java
+++ b/src/org/torproject/onionoo/ResponseBuilder.java
@@ -5,7 +5,9 @@ package org.torproject.onionoo;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
-import java.util.Scanner;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
public class ResponseBuilder {
@@ -140,50 +142,128 @@ public class ResponseBuilder {
private String writeDetailsLines(NodeStatus entry) {
String fingerprint = entry.getFingerprint();
- DetailsDocument detailsDocument = this.documentStore.retrieve(
- DetailsDocument.class, false, fingerprint);
- if (detailsDocument != null &&
- detailsDocument.getDocumentString() != null) {
- StringBuilder sb = new StringBuilder();
- Scanner s = new Scanner(detailsDocument.getDocumentString());
- boolean includeLine = true;
- while (s.hasNextLine()) {
- String line = s.nextLine();
- if (line.equals("{")) {
- /* Omit newline after opening bracket. */
- sb.append("{");
- } else if (line.equals("}")) {
- /* Omit newline after closing bracket. */
- sb.append("}");
- break;
- } else if (this.fields != null) {
- if (line.startsWith("\"")) {
- includeLine = false;
- for (String field : this.fields) {
- if (line.startsWith("\"" + field + "\":")) {
- sb.append(line + "\n");
- includeLine = true;
- }
- }
- } else if (includeLine) {
- sb.append(line + "\n");
+ if (this.fields != null) {
+ /* TODO Maybe there's a more elegant way (more maintainable, more
+ * efficient, etc.) to implement this? */
+ DetailsDocument detailsDocument = documentStore.retrieve(
+ DetailsDocument.class, true, fingerprint);
+ if (detailsDocument != null) {
+ DetailsDocument dd = new DetailsDocument();
+ for (String field : this.fields) {
+ if (field.equals("nickname")) {
+ dd.setNickname(detailsDocument.getNickname());
+ } else if (field.equals("fingerprint")) {
+ dd.setFingerprint(detailsDocument.getFingerprint());
+ } else if (field.equals("hashed_fingerprint")) {
+ dd.setHashedFingerprint(
+ detailsDocument.getHashedFingerprint());
+ } else if (field.equals("or_addresses")) {
+ dd.setOrAddresses(detailsDocument.getOrAddresses());
+ } else if (field.equals("exit_addresses")) {
+ dd.setExitAddresses(detailsDocument.getExitAddresses());
+ } else if (field.equals("dir_address")) {
+ dd.setDirAddress(detailsDocument.getDirAddress());
+ } else if (field.equals("last_seen")) {
+ dd.setLastSeen(detailsDocument.getLastSeen());
+ } else if (field.equals("last_changed_address_or_port")) {
+ dd.setLastChangedAddressOrPort(
+ detailsDocument.getLastChangedAddressOrPort());
+ } else if (field.equals("first_seen")) {
+ dd.setFirstSeen(detailsDocument.getFirstSeen());
+ } else if (field.equals("running")) {
+ dd.setRunning(detailsDocument.getRunning());
+ } else if (field.equals("flags")) {
+ dd.setFlags(detailsDocument.getFlags());
+ } else if (field.equals("country")) {
+ dd.setCountry(detailsDocument.getCountry());
+ } else if (field.equals("country_name")) {
+ dd.setCountryName(detailsDocument.getCountryName());
+ } else if (field.equals("region_name")) {
+ dd.setRegionName(detailsDocument.getRegionName());
+ } else if (field.equals("city_name")) {
+ dd.setCityName(detailsDocument.getCityName());
+ } else if (field.equals("latitude")) {
+ dd.setLatitude(detailsDocument.getLatitude());
+ } else if (field.equals("longitude")) {
+ dd.setLongitude(detailsDocument.getLongitude());
+ } else if (field.equals("as_number")) {
+ dd.setAsNumber(detailsDocument.getAsNumber());
+ } else if (field.equals("as_name")) {
+ dd.setAsName(detailsDocument.getAsName());
+ } else if (field.equals("consensus_weight")) {
+ dd.setConsensusWeight(detailsDocument.getConsensusWeight());
+ } else if (field.equals("host_name")) {
+ dd.setHostName(detailsDocument.getHostName());
+ } else if (field.equals("last_restarted")) {
+ dd.setLastRestarted(detailsDocument.getLastRestarted());
+ } else if (field.equals("bandwidth_rate")) {
+ dd.setBandwidthRate(detailsDocument.getBandwidthRate());
+ } else if (field.equals("bandwidth_burst")) {
+ dd.setBandwidthBurst(detailsDocument.getBandwidthBurst());
+ } else if (field.equals("observed_bandwidth")) {
+ dd.setObservedBandwidth(
+ detailsDocument.getObservedBandwidth());
+ } else if (field.equals("advertised_bandwidth")) {
+ dd.setAdvertisedBandwidth(
+ detailsDocument.getAdvertisedBandwidth());
+ } else if (field.equals("exit_policy")) {
+ dd.setExitPolicy(detailsDocument.getExitPolicy());
+ } else if (field.equals("exit_policy_summary")) {
+ dd.setExitPolicySummary(
+ detailsDocument.getExitPolicySummary());
+ } else if (field.equals("exit_policy_v6_summary")) {
+ dd.setExitPolicyV6Summary(
+ detailsDocument.getExitPolicyV6Summary());
+ } else if (field.equals("contact")) {
+ dd.setContact(detailsDocument.getContact());
+ } else if (field.equals("platform")) {
+ dd.setPlatform(detailsDocument.getPlatform());
+ } else if (field.equals("family")) {
+ dd.setFamily(detailsDocument.getFamily());
+ } else if (field.equals("advertised_bandwidth_fraction")) {
+ dd.setAdvertisedBandwidthFraction(
+ detailsDocument.getAdvertisedBandwidthFraction());
+ } else if (field.equals("consensus_weight_fraction")) {
+ dd.setConsensusWeightFraction(
+ detailsDocument.getConsensusWeightFraction());
+ } else if (field.equals("guard_probability")) {
+ dd.setGuardProbability(detailsDocument.getGuardProbability());
+ } else if (field.equals("middle_probability")) {
+ dd.setMiddleProbability(
+ detailsDocument.getMiddleProbability());
+ } else if (field.equals("exit_probability")) {
+ dd.setExitProbability(detailsDocument.getExitProbability());
+ } else if (field.equals("recommended_version")) {
+ dd.setRecommendedVersion(
+ detailsDocument.getRecommendedVersion());
+ } else if (field.equals("hibernating")) {
+ dd.setHibernating(detailsDocument.getHibernating());
+ } else if (field.equals("pool_assignment")) {
+ dd.setPoolAssignment(detailsDocument.getPoolAssignment());
}
- } else {
- sb.append(line + "\n");
}
+ /* Don't escape HTML characters, like < and >, contained in
+ * strings. */
+ Gson gson = new GsonBuilder().disableHtmlEscaping().create();
+ /* Whenever we provide Gson with a string containing an escaped
+ * non-ASCII character like \u00F2, it escapes the \ to \\, which
+ * we need to undo before including the string in a response. */
+ return gson.toJson(dd).replaceAll("\\\\\\\\u", "\\\\u");
+ } else {
+ // TODO We should probably log that we didn't find a details
+ // document that we expected to exist.
+ return "";
}
- s.close();
- String detailsLines = sb.toString();
- if (detailsLines.endsWith(",\n}")) {
- /* Fix broken JSON if we omitted lines above. */
- detailsLines = detailsLines.substring(0,
- detailsLines.length() - 3) + "\n}";
- }
- return detailsLines;
} else {
- // TODO We should probably log that we didn't find a details
- // document that we expected to exist.
- return "";
+ DetailsDocument detailsDocument = documentStore.retrieve(
+ DetailsDocument.class, false, fingerprint);
+ if (detailsDocument != null) {
+ return detailsDocument.getDocumentString();
+ } else {
+ // TODO We should probably log that we didn't find a details
+ // document that we expected to exist.
+ return "";
+ }
}
}
More information about the tor-commits
mailing list