[tor-commits] [exonerator/master] Start using a query response object.

karsten at torproject.org karsten at torproject.org
Fri Sep 15 12:18:16 UTC 2017


commit b68c556e1743420c1f12a33d5f727f3972f9bb45
Author: Karsten Loesing <karsten.loesing at gmx.net>
Date:   Wed Aug 16 11:28:59 2017 +0200

    Start using a query response object.
    
    This is another prerequisite for changing queries towards making a
    single query per request (#16596) and towards splitting up ExoneraTor
    into front-end and back-end.
---
 .../torproject/exonerator/ExoneraTorServlet.java   | 207 ++++++++++++---------
 .../org/torproject/exonerator/QueryResponse.java   |  63 +++++++
 2 files changed, 186 insertions(+), 84 deletions(-)

diff --git a/src/main/java/org/torproject/exonerator/ExoneraTorServlet.java b/src/main/java/org/torproject/exonerator/ExoneraTorServlet.java
index ca5d34b..b7d0497 100644
--- a/src/main/java/org/torproject/exonerator/ExoneraTorServlet.java
+++ b/src/main/java/org/torproject/exonerator/ExoneraTorServlet.java
@@ -105,89 +105,41 @@ public class ExoneraTorServlet extends HttpServlet {
 
     /* Step 2: Query the database. */
 
-    /* Query the following data. */
     boolean successfullyConnectedToDatabase = false;
     String firstDate = null;
     String lastDate = null;
     boolean noRelevantConsensuses = true;
-    List<String[]> statusEntries = null;
+    List<String[]> statusEntries = new ArrayList<>();
     List<String> addressesInSameNetwork = null;
 
     /* Only query the database if we received valid user input. */
     if (null != relayIp && !relayIp.isEmpty() && null != timestampStr
         && !timestampStr.isEmpty()) {
-
-      /* Open a database connection that we'll use to handle the whole
-       * request. */
-      long requestedConnection = System.currentTimeMillis();
-      Connection conn = this.connectToDatabase();
-      if (null != conn) {
+      QueryResponse queryResponse = this.queryDatabase(relayIp, timestampStr);
+      if (null != queryResponse) {
         successfullyConnectedToDatabase = true;
-
-        /* Look up first and last date in the database. */
-        long[] firstAndLastDates = this.queryFirstAndLastDatesFromDatabase(
-            conn);
-        if (null != firstAndLastDates) {
-          SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
-          dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
-          firstDate = dateFormat.format(firstAndLastDates[0]);
-          lastDate = dateFormat.format(firstAndLastDates[1]);
-
-          /* Consider all consensuses published on or within a day of the given
-           * date. */
-          long timestamp = 0L;
-          if (timestampStr != null && timestampStr.length() > 0) {
-            try {
-              timestamp = dateFormat.parse(timestampParameter).getTime();
-            } catch (ParseException e) {
-              /* Already checked in parseTimestamp(). */
-            }
-          }
-          long timestampFrom = timestamp - 24L * 60L * 60L * 1000L;
-          long timestampTo = timestamp + 2 * 24L * 60L * 60L * 1000L - 1L;
-          SimpleDateFormat validAfterTimeFormat = new SimpleDateFormat(
-              "yyyy-MM-dd HH:mm:ss");
-          validAfterTimeFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
-          String fromValidAfter = validAfterTimeFormat.format(timestampFrom);
-          String toValidAfter = validAfterTimeFormat.format(timestampTo);
-          SortedSet<Long> relevantConsensuses
-              = this.queryKnownConsensusValidAfterTimes(conn,
-              fromValidAfter, toValidAfter);
-          if (null != relevantConsensuses && !relevantConsensuses.isEmpty()) {
-            noRelevantConsensuses = false;
-
-            /* Search for status entries with the given IP address as onion
-             * routing address, plus status entries of relays having an exit
-             * list entry with the given IP address as exit address. */
-            statusEntries = this.queryStatusEntries(conn, relayIp,
-                timestamp, validAfterTimeFormat);
-
-            /* If we didn't find anything, run another query to find out if
-             * there are relays running on other IP addresses in the same /24 or
-             * /48 network and tell the user about it. */
-            if (statusEntries.isEmpty()) {
-              addressesInSameNetwork = new ArrayList<>();
-              if (!relayIp.contains(":")) {
-                String address24 = this.convertIpV4ToHex(relayIp)
-                    .substring(0, 6);
-                if (address24 != null) {
-                  addressesInSameNetwork = this.queryAddressesInSame24(conn,
-                      address24, timestamp);
-                }
-              } else {
-                String address48 = this.convertIpV6ToHex(relayIp)
-                    .substring(0, 12);
-                if (address48 != null) {
-                  addressesInSameNetwork = this.queryAddressesInSame48(conn,
-                      address48, timestamp);
-                }
-              }
+        firstDate = queryResponse.firstDateInDatabase;
+        lastDate = queryResponse.lastDateInDatabase;
+        if (null != queryResponse.relevantStatuses
+            && queryResponse.relevantStatuses) {
+          noRelevantConsensuses = false;
+        }
+        if (null != queryResponse.matches) {
+          for (QueryResponse.Match match : queryResponse.matches) {
+            StringBuilder sb = new StringBuilder();
+            int writtenAddresses = 0;
+            for (String address : match.addresses) {
+              sb.append((writtenAddresses++ > 0 ? ", " : "") + address);
             }
+            String[] statusEntry = new String[]{match.timestamp,
+                sb.toString(), match.fingerprint, match.nickname,
+                null == match.exit ? "U" : (match.exit ? "Y" : "N")};
+            statusEntries.add(statusEntry);
           }
         }
-
-        /* Close the database connection. */
-        this.closeDatabaseConnection(conn, requestedConnection);
+        if (null != queryResponse.nearbyAddresses) {
+          addressesInSameNetwork = Arrays.asList(queryResponse.nearbyAddresses);
+        }
       }
     }
 
@@ -411,6 +363,94 @@ public class ExoneraTorServlet extends HttpServlet {
 
   /* Helper methods for querying the database. */
 
+  private QueryResponse queryDatabase(String relayIp, String timestampStr) {
+
+    QueryResponse response = null;
+    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
+    dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+    SimpleDateFormat validAfterTimeFormat = new SimpleDateFormat(
+        "yyyy-MM-dd HH:mm:ss");
+    validAfterTimeFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+    long timestamp = 0L;
+    if (timestampStr != null && timestampStr.length() > 0) {
+      try {
+        timestamp = dateFormat.parse(timestampStr).getTime();
+      } catch (ParseException e) {
+        /* Already checked in parseTimestamp(). */
+      }
+    }
+
+    /* Only query the database if we received valid user input. */
+    if (!"".equals(relayIp) && !"".equals(timestampStr)) {
+
+      /* Open a database connection that we'll use to handle the whole
+       * request. */
+      long requestedConnection = System.currentTimeMillis();
+      Connection conn = this.connectToDatabase();
+      if (null != conn) {
+
+        response = new QueryResponse();
+        response.queryAddress = relayIp;
+        response.queryDate = timestampStr;
+
+        /* Look up first and last date in the database. */
+        long[] firstAndLastDates = this.queryFirstAndLastDatesFromDatabase(
+            conn);
+        if (null != firstAndLastDates) {
+          response.firstDateInDatabase = dateFormat.format(
+              firstAndLastDates[0]);
+          response.lastDateInDatabase = dateFormat.format(firstAndLastDates[1]);
+
+          /* Consider all consensuses published on or within a day of the given
+           * date. */
+          long timestampFrom = timestamp - 24L * 60L * 60L * 1000L;
+          long timestampTo = timestamp + 2 * 24L * 60L * 60L * 1000L - 1L;
+          String fromValidAfter = validAfterTimeFormat.format(timestampFrom);
+          String toValidAfter = validAfterTimeFormat.format(timestampTo);
+          SortedSet<Long> relevantConsensuses =
+              this.queryKnownConsensusValidAfterTimes(conn, fromValidAfter,
+              toValidAfter);
+          if (null != relevantConsensuses && !relevantConsensuses.isEmpty()) {
+            response.relevantStatuses = true;
+
+            /* Search for status entries with the given IP address as onion
+             * routing address, plus status entries of relays having an exit
+             * list entry with the given IP address as exit address. */
+            List<QueryResponse.Match> matches = this.queryStatusEntries(conn,
+                relayIp, timestamp, validAfterTimeFormat);
+            if (!matches.isEmpty()) {
+              response.matches = matches.toArray(new QueryResponse.Match[0]);
+
+            /* If we didn't find anything, run another query to find out if
+             * there are relays running on other IP addresses in the same /24 or
+             * /48 network and tell the user about it. */
+            } else {
+              if (!relayIp.contains(":")) {
+                String address24 = this.convertIpV4ToHex(relayIp)
+                    .substring(0, 6);
+                if (address24 != null) {
+                  response.nearbyAddresses = this.queryAddressesInSame24(conn,
+                      address24, timestamp).toArray(new String[0]);
+                }
+              } else {
+                String address48 = this.convertIpV6ToHex(relayIp)
+                    .substring(0, 12);
+                if (address48 != null) {
+                  response.nearbyAddresses = this.queryAddressesInSame48(conn,
+                      address48, timestamp).toArray(new String[0]);
+                }
+              }
+            }
+          }
+        }
+
+        /* Close the database connection. */
+        this.closeDatabaseConnection(conn, requestedConnection);
+      }
+    }
+    return response;
+  }
+
   private Connection connectToDatabase() {
     Connection conn = null;
     try {
@@ -468,10 +508,10 @@ public class ExoneraTorServlet extends HttpServlet {
     return relevantConsensuses;
   }
 
-  private List<String[]> queryStatusEntries(Connection conn,
+  private List<QueryResponse.Match> queryStatusEntries(Connection conn,
       String relayIp, long timestamp,
       SimpleDateFormat validAfterTimeFormat) {
-    List<String[]> statusEntries = new ArrayList<>();
+    List<QueryResponse.Match> matches = new ArrayList<>();
     String addressHex = !relayIp.contains(":")
         ? this.convertIpV4ToHex(relayIp) : this.convertIpV6ToHex(relayIp);
     if (addressHex == null) {
@@ -496,7 +536,7 @@ public class ExoneraTorServlet extends HttpServlet {
         SortedSet<String> addresses = new TreeSet<>();
         SortedSet<String> addressesHex = new TreeSet<>();
         String nickname = null;
-        String exit = "U";
+        Boolean exit = null;
         for (String line : new String(rawstatusentry).split("\n")) {
           if (line.startsWith("r ")) {
             String[] parts = line.split(" ");
@@ -512,7 +552,7 @@ public class ExoneraTorServlet extends HttpServlet {
                 : this.convertIpV6ToHex(address);
             addressesHex.add(orAddressHex);
           } else if (line.startsWith("p ")) {
-            exit = line.equals("p reject 1-65535") ? "N" : "Y";
+            exit = !line.equals("p reject 1-65535");
           }
         }
         String exitaddress = rs.getString(4);
@@ -523,25 +563,24 @@ public class ExoneraTorServlet extends HttpServlet {
         if (!addressesHex.contains(addressHex)) {
           continue;
         }
-        StringBuilder sb = new StringBuilder();
-        int writtenAddresses = 0;
-        for (String address : addresses) {
-          sb.append((writtenAddresses++ > 0 ? ", " : "") + address);
-        }
         long validafter = rs.getTimestamp(2, utcCalendar).getTime();
         String validAfterString = validAfterTimeFormat.format(validafter);
         String fingerprint = rs.getString(3).toUpperCase();
-        String[] statusEntry = new String[] { validAfterString,
-            sb.toString(), fingerprint, nickname, exit };
-        statusEntries.add(statusEntry);
+        QueryResponse.Match match = new QueryResponse.Match();
+        match.timestamp = validAfterString;
+        match.addresses = addresses.toArray(new String[0]);
+        match.fingerprint = fingerprint;
+        match.nickname = nickname;
+        match.exit = exit;
+        matches.add(match);
       }
       rs.close();
       cs.close();
     } catch (SQLException e) {
       /* Nothing found. */
-      statusEntries = null;
+      matches.clear();
     }
-    return statusEntries;
+    return matches;
   }
 
   private List<String> queryAddressesInSame24(Connection conn,
diff --git a/src/main/java/org/torproject/exonerator/QueryResponse.java b/src/main/java/org/torproject/exonerator/QueryResponse.java
new file mode 100644
index 0000000..d86efb0
--- /dev/null
+++ b/src/main/java/org/torproject/exonerator/QueryResponse.java
@@ -0,0 +1,63 @@
+/* Copyright 2017 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.exonerator;
+
+import com.google.gson.annotations.SerializedName;
+
+/** Query response from the ExoneraTor database. */
+public class QueryResponse {
+
+  /** Query IP address passed in the request; never <code>null</code>. */
+  @SerializedName("query_address")
+  String queryAddress;
+
+  /** Query date passed in the request; never <code>null</code>. */
+  @SerializedName("query_date")
+  String queryDate;
+
+  /** ISO-formatted valid-after time of the first status contained in the
+   * database; only <code>null</code> if the database is empty. */
+  @SerializedName("first_date_in_database")
+  String firstDateInDatabase;
+
+  /** ISO-formatted valid-after time of the last status contained in the
+   * database; only <code>null</code> if the database is empty. */
+  @SerializedName("last_date_in_database")
+  String lastDateInDatabase;
+
+  /** Whether there is at least one relevant status in the database on or within
+   * a day of the requested date; <code>null</code> if the database is empty. */
+  @SerializedName("relevant_statuses")
+  Boolean relevantStatuses;
+
+  /** All matches for the given IP address and date; <code>null</code> if there
+   * were no matches at all. */
+  Match[] matches;
+
+  /** Match details. */
+  static class Match {
+
+    /** ISO-formatted valid-after time of the status containing the match. */
+    String timestamp;
+
+    /** All known IP addresses of the relay at the time. */
+    String[] addresses;
+
+    /** Relay fingerprint. */
+    String fingerprint;
+
+    /** Relay nickname. */
+    String nickname;
+
+    /** Whether this relay permitted exiting or not; <code>null</code> if
+     * unknown. */
+    Boolean exit;
+  }
+
+  /** All known IP addresses in the same /24 or /48 network; <code>null</code>
+   * if there were direct matches for the given IP address. */
+  @SerializedName("nearby_addresses")
+  String[] nearbyAddresses;
+}
+





More information about the tor-commits mailing list