[tor-commits] [onionoo/master] Add qualified search terms.

karsten at torproject.org karsten at torproject.org
Fri Oct 17 10:04:08 UTC 2014


commit f82b3425e81280edd2cccae004a63f9b1194191a
Author: Karsten Loesing <karsten.loesing at gmx.net>
Date:   Fri Oct 17 11:34:19 2014 +0200

    Add qualified search terms.
    
    Implements #13422.
---
 .../torproject/onionoo/server/ResourceServlet.java |   55 +++++++++++++++-----
 .../torproject/onionoo/server/ResponseBuilder.java |    2 +-
 .../torproject/onionoo/ResourceServletTest.java    |   45 ++++++++++++++++
 web/protocol.html                                  |   16 ++++--
 4 files changed, 100 insertions(+), 18 deletions(-)

diff --git a/src/main/java/org/torproject/onionoo/server/ResourceServlet.java b/src/main/java/org/torproject/onionoo/server/ResourceServlet.java
index d527962..cd99dcb 100644
--- a/src/main/java/org/torproject/onionoo/server/ResourceServlet.java
+++ b/src/main/java/org/torproject/onionoo/server/ResourceServlet.java
@@ -4,9 +4,11 @@ package org.torproject.onionoo.server;
 
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.regex.Pattern;
@@ -59,6 +61,15 @@ public class ResourceServlet extends HttpServlet {
       CACHE_MAX_TIME = 45L * 60L * 1000L,
       CACHE_INTERVAL = 5L * 60L * 1000L;
 
+  private static Set<String> knownParameters = new HashSet<String>(
+      Arrays.asList(("type,running,search,lookup,fingerprint,country,as,"
+          + "flag,first_seen_days,last_seen_days,contact,order,limit,"
+          + "offset,fields,family").split(",")));
+
+  private static Set<String> illegalSearchQualifiers =
+      new HashSet<String>(Arrays.asList(("search,fingerprint,order,limit,"
+          + "offset,fields").split(",")));
+
   public void doGet(HttpServletRequestWrapper request,
       HttpServletResponseWrapper response) throws IOException {
 
@@ -126,10 +137,6 @@ public class ResourceServlet extends HttpServlet {
 
     /* Make sure that the request doesn't contain any unknown
      * parameters. */
-    Set<String> knownParameters = new HashSet<String>(Arrays.asList((
-        "type,running,search,lookup,fingerprint,country,as,flag,"
-        + "first_seen_days,last_seen_days,contact,order,limit,offset,"
-        + "fields,family").split(",")));
     for (String parameterKey : parameterMap.keySet()) {
       if (!knownParameters.contains(parameterKey)) {
         response.sendError(HttpServletResponse.SC_BAD_REQUEST);
@@ -138,6 +145,34 @@ public class ResourceServlet extends HttpServlet {
     }
 
     /* Filter relays and bridges matching the request. */
+    if (parameterMap.containsKey("search")) {
+      String[] searchTerms = this.parseSearchParameters(
+          parameterMap.get("search"));
+      if (searchTerms == null) {
+        response.sendError(HttpServletResponse.SC_BAD_REQUEST);
+        return;
+      }
+      List<String> unqualifiedSearchTerms = new ArrayList<String>();
+      for (String searchTerm : searchTerms) {
+        if (searchTerm.contains(":") && !searchTerm.startsWith("[")) {
+          String[] parts = searchTerm.split(":", 2);
+          String parameterKey = parts[0];
+          if (!knownParameters.contains(parameterKey) ||
+              illegalSearchQualifiers.contains(parameterKey)) {
+            response.sendError(HttpServletResponse.SC_BAD_REQUEST);
+            return;
+          }
+          if (!parameterMap.containsKey(parameterKey)) {
+            String parameterValue = parts[1];
+            parameterMap.put(parameterKey, parameterValue);
+          }
+        } else {
+          unqualifiedSearchTerms.add(searchTerm);
+        }
+      }
+      rh.setSearch(unqualifiedSearchTerms.toArray(
+          new String[unqualifiedSearchTerms.size()]));
+    }
     if (parameterMap.containsKey("type")) {
       String typeParameterValue = parameterMap.get("type").toLowerCase();
       boolean relaysRequested = true;
@@ -161,15 +196,6 @@ public class ResourceServlet extends HttpServlet {
       }
       rh.setRunning(runningRequested ? "true" : "false");
     }
-    if (parameterMap.containsKey("search")) {
-      String[] searchTerms = this.parseSearchParameters(
-          parameterMap.get("search"));
-      if (searchTerms == null) {
-        response.sendError(HttpServletResponse.SC_BAD_REQUEST);
-        return;
-      }
-      rh.setSearch(searchTerms);
-    }
     if (parameterMap.containsKey("lookup")) {
       String lookupParameter = this.parseFingerprintParameter(
           parameterMap.get("lookup"));
@@ -334,7 +360,8 @@ public class ResourceServlet extends HttpServlet {
   private static Pattern searchParameterPattern =
       Pattern.compile("^\\$?[0-9a-fA-F]{1,40}$|" /* Fingerprint. */
       + "^[0-9a-zA-Z\\.]{1,19}$|" /* Nickname or IPv4 address. */
-      + "^\\[[0-9a-fA-F:\\.]{1,39}\\]?$"); /* IPv6 address. */
+      + "^\\[[0-9a-fA-F:\\.]{1,39}\\]?$|" /* IPv6 address. */
+      + "^[a-zA-Z_]+:[0-9a-zA-Z_,-]+$" /* Qualified search term. */);
   private String[] parseSearchParameters(String parameter) {
     String[] searchParameters;
     if (parameter.contains(" ")) {
diff --git a/src/main/java/org/torproject/onionoo/server/ResponseBuilder.java b/src/main/java/org/torproject/onionoo/server/ResponseBuilder.java
index e7606af..2660c26 100644
--- a/src/main/java/org/torproject/onionoo/server/ResponseBuilder.java
+++ b/src/main/java/org/torproject/onionoo/server/ResponseBuilder.java
@@ -69,7 +69,7 @@ public class ResponseBuilder {
     return this.charsWritten;
   }
 
-  private static final String PROTOCOL_VERSION = "1.1";
+  private static final String PROTOCOL_VERSION = "1.2";
 
   private static final String NEXT_MAJOR_VERSION_SCHEDULED = "2014-11-15";
 
diff --git a/src/test/java/org/torproject/onionoo/ResourceServletTest.java b/src/test/java/org/torproject/onionoo/ResourceServletTest.java
index cf46e9f..11068dc 100644
--- a/src/test/java/org/torproject/onionoo/ResourceServletTest.java
+++ b/src/test/java/org/torproject/onionoo/ResourceServletTest.java
@@ -732,6 +732,51 @@ public class ResourceServletTest {
   }
 
   @Test()
+  public void testSearchTypeRelay() {
+    this.assertSummaryDocument("/summary?search=type:relay", 3, null, 0,
+        null);
+  }
+
+  @Test()
+  public void testSearchTypeRelayTorkaZ() {
+    this.assertSummaryDocument("/summary?search=type:relay TorkaZ", 1,
+        new String[] { "TorkaZ" }, 0, null);
+  }
+
+  @Test()
+  public void testSearchTorkaZTypeRelay() {
+    this.assertSummaryDocument("/summary?search=TorkaZ type:relay", 1,
+        new String[] { "TorkaZ" }, 0, null);
+  }
+
+  @Test()
+  public void testSearchTypeRelayTypeDirectory() {
+    this.assertSummaryDocument(
+        "/summary?search=type:relay type:directory", 3, null, 0, null);
+  }
+
+  @Test()
+  public void testSearchTypeDirectoryTypeRelay() {
+    this.assertErrorStatusCode(
+        "/summary?search=type:directory type:relay", 400);
+  }
+
+  @Test()
+  public void testSearchFooBar() {
+    this.assertErrorStatusCode("/summary?search=foo:bar", 400);
+  }
+
+  @Test()
+  public void testSearchSearchTorkaZ() {
+    this.assertErrorStatusCode("/summary?search=search:TorkaZ", 400);
+  }
+
+  @Test()
+  public void testSearchLimitOne() {
+    this.assertErrorStatusCode("/summary?search=limit:1", 400);
+  }
+
+  @Test()
   public void testLookupFingerprint() {
     this.assertSummaryDocument(
         "/summary?lookup=000C5F55BD4814B917CC474BD537F1A3B33CCE2A", 1,
diff --git a/web/protocol.html b/web/protocol.html
index bac080f..21ab963 100644
--- a/web/protocol.html
+++ b/web/protocol.html
@@ -163,6 +163,8 @@ scheduled to be deployed in the next months:</p>
 2014.</li>
 <li><strong>1.1</strong>: Added optional "next_major_version_scheduled"
 field on September 16, 2014.</li>
+<li><strong>1.2</strong>: Added qualified search terms to "search"
+parameter on October 17, 2014.</li>
 </ul>
 
 </div> <!-- box -->
@@ -266,10 +268,11 @@ Parameter values are case-insensitive.
 <li>
 <b>search</b>
 <p>
-Return only relays with the parameter value
+Return only (1) relays with the parameter value
 matching (part of a) nickname, (possibly $-prefixed) beginning of a
-fingerprint, or beginning of an IP address, and bridges with (part of a)
-nickname or (possibly $-prefixed) beginning of a hashed fingerprint.
+fingerprint, or beginning of an IP address, (2) bridges with (part of a)
+nickname or (possibly $-prefixed) beginning of a hashed fingerprint, and
+(3) relays and/or bridges matching a given qualified search term.
 Searches by relay IP address include all known addresses used for onion
 routing and for exiting to the Internet.
 Searches for beginnings of IP addresses are performed on textual
@@ -281,6 +284,13 @@ of all relays and bridges matching all search terms will be returned.
 Full fingerprints should always be hashed using SHA-1, regardless of
 searching for a relay or a bridge, in order to not accidentally leak
 non-hashed bridge fingerprints in the URL.
+<font color="blue">
+Qualified search terms have the form "key:value" (without double quotes)
+with "key" being one of the parameters listed here except for "search",
+"fingerprint", "order", "limit", "offset", and "fields", and "value" being
+the string that will internally be passed to that parameter.
+(Qualified search terms were added on October 17, 2014.)
+</font>
 </p>
 </li>
 



More information about the tor-commits mailing list