commit f3f83f527718aaa2a721c27670e81f79090e7561 Author: Karsten Loesing karsten.loesing@gmx.net Date: Wed Aug 16 14:21:33 2017 +0200
Separate servlet into three steps.
1: Parse the request. 2: Query the database. 3: Write the response.
This doesn't just clean up code, it's also a prerequisite for changing queries towards making a single query per request (#16596). --- .../torproject/exonerator/ExoneraTorServlet.java | 225 ++++++++++++--------- 1 file changed, 127 insertions(+), 98 deletions(-)
diff --git a/src/main/java/org/torproject/exonerator/ExoneraTorServlet.java b/src/main/java/org/torproject/exonerator/ExoneraTorServlet.java index 2785bef..ca5d34b 100644 --- a/src/main/java/org/torproject/exonerator/ExoneraTorServlet.java +++ b/src/main/java/org/torproject/exonerator/ExoneraTorServlet.java @@ -82,74 +82,133 @@ public class ExoneraTorServlet extends HttpServlet { HttpServletResponse response) throws IOException, ServletException {
- /* Set content type, or the page doesn't render in Chrome. */ - response.setContentType("text/html"); - response.setCharacterEncoding("utf-8"); + /* Step 1: Parse the request. */
- /* Find the right resource bundle for the user's requested language. */ + /* Parse ip parameter. */ + String ipParameter = request.getParameter("ip"); + String relayIp = this.parseIpParameter(ipParameter); + final boolean relayIpHasError = relayIp == null; + + /* Parse timestamp parameter. */ + String timestampParameter = request.getParameter("timestamp"); + String timestampStr = this.parseTimestampParameter( + timestampParameter); + final boolean timestampHasError = timestampStr == null; + + /* Parse lang parameter. */ String langParameter = request.getParameter("lang"); String langStr = "en"; if (null != langParameter && this.availableLanguages.contains(langParameter)) { langStr = langParameter; } - ResourceBundle rb = ResourceBundle.getBundle("ExoneraTor", - Locale.forLanguageTag(langStr));
- /* Start writing response. */ - PrintWriter out = response.getWriter(); - this.writeHeader(out, rb, langStr); + /* Step 2: Query the database. */
- /* Open a database connection that we'll use to handle the whole - * request. */ - long requestedConnection = System.currentTimeMillis(); - Connection conn = this.connectToDatabase(); - if (conn == null) { - this.writeSummaryUnableToConnectToDatabase(out, rb); - this.writeFooter(out, rb, null, null); - return; - } + /* Query the following data. */ + boolean successfullyConnectedToDatabase = false; + String firstDate = null; + String lastDate = null; + boolean noRelevantConsensuses = true; + List<String[]> statusEntries = null; + List<String> addressesInSameNetwork = null;
- /* Look up first and last date in the database. */ - long[] firstAndLastDates = this.queryFirstAndLastDatesFromDatabase( - conn); - if (firstAndLastDates == null) { - this.writeSummaryNoData(out, rb); - this.writeFooter(out, rb, null, null); - this.closeDatabaseConnection(conn, requestedConnection); + /* 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) { + 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); + } + } + } + } + } + + /* Close the database connection. */ + this.closeDatabaseConnection(conn, requestedConnection); + } } - SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); - dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); - String firstDate = dateFormat.format(firstAndLastDates[0]); - String lastDate = dateFormat.format(firstAndLastDates[1]);
- /* Parse parameters. */ - String ipParameter = request.getParameter("ip"); - String relayIp = this.parseIpParameter(ipParameter); - boolean relayIpHasError = relayIp == null; + /* Step 3: Write the response. */
- /* Parse timestamp parameter. */ - String timestampParameter = request.getParameter("timestamp"); - String timestampStr = this.parseTimestampParameter( - timestampParameter); - boolean timestampHasError = timestampStr == null; + /* Set content type, or the page doesn't render in Chrome. */ + response.setContentType("text/html"); + response.setCharacterEncoding("utf-8");
- /* Check that timestamp is within range. */ - long timestamp = 0L; - boolean timestampOutOfRange = false; - if (timestampStr != null && timestampStr.length() > 0) { - try { - timestamp = dateFormat.parse(timestampParameter).getTime(); - if (timestamp < firstAndLastDates[0] - || timestamp > firstAndLastDates[1]) { - timestampOutOfRange = true; - } - } catch (ParseException e) { - /* Already checked in parseTimestamp(). */ - } - } + /* Find the right resource bundle for the user's requested language. */ + ResourceBundle rb = ResourceBundle.getBundle("ExoneraTor", + Locale.forLanguageTag(langStr)); + + /* Start writing response. */ + PrintWriter out = response.getWriter(); + this.writeHeader(out, rb, langStr);
/* Write form. */ + boolean timestampOutOfRange = null != timestampStr + && (null != firstDate && timestampStr.compareTo(firstDate) < 0 + || (null != lastDate && timestampStr.compareTo(lastDate) > 0)); this.writeForm(out, rb, relayIp, relayIpHasError || ("".equals(relayIp) && !"".equals(timestampStr)), timestampStr, !relayIpHasError @@ -161,7 +220,21 @@ public class ExoneraTorServlet extends HttpServlet { * This is the start page. */ if ("".equals(relayIp) && "".equals(timestampStr)) { this.writeFooter(out, rb, null, null); - this.closeDatabaseConnection(conn, requestedConnection); + return; + } + + /* If we were unable to connect to the database, write an error message. */ + if (!successfullyConnectedToDatabase) { + this.writeSummaryUnableToConnectToDatabase(out, rb); + this.writeFooter(out, rb, null, null); + return; + } + + /* Similarly, if we found the database to be empty, write an error message, + * too. */ + if (null == firstDate || null == lastDate) { + this.writeSummaryNoData(out, rb); + this.writeFooter(out, rb, null, null); return; }
@@ -174,7 +247,6 @@ public class ExoneraTorServlet extends HttpServlet { writeSummaryNoTimestamp(out, rb); } this.writeFooter(out, rb, null, null); - this.closeDatabaseConnection(conn, requestedConnection); return; }
@@ -190,57 +262,15 @@ public class ExoneraTorServlet extends HttpServlet { firstDate, lastDate); } this.writeFooter(out, rb, relayIp, timestampStr); - this.closeDatabaseConnection(conn, requestedConnection); return; }
- /* 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; - 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 (relevantConsensuses == null || relevantConsensuses.isEmpty()) { + if (noRelevantConsensuses) { this.writeSummaryNoDataForThisInterval(out, rb); this.writeFooter(out, rb, relayIp, timestampStr); - this.closeDatabaseConnection(conn, requestedConnection); return; }
- /* 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<String[]> 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. */ - List<String> addressesInSameNetwork = null; - 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); - } - } - } - /* Print out result. */ if (!statusEntries.isEmpty()) { this.writeSummaryPositive(out, rb, relayIp, timestampStr); @@ -256,7 +286,6 @@ public class ExoneraTorServlet extends HttpServlet {
this.writePermanentLink(out, rb, relayIp, timestampStr, langStr);
- this.closeDatabaseConnection(conn, requestedConnection); this.writeFooter(out, rb, relayIp, timestampStr); }