commit cfd767ed67d3bbc71d18e1bd3117d4a0faf97fac Author: Karsten Loesing karsten.loesing@gmx.net Date: Mon Feb 27 09:34:17 2012 +0100
Discontinue TorStatus-like pages.
These pages put more load on the database than they should. Good thing we finally have a good TorStatus website. --- .../torproject/ernie/web/NetworkStatusServlet.java | 145 ------- src/org/torproject/ernie/web/RelayServlet.java | 393 +------------------- web/WEB-INF/banner.jsp | 4 - web/WEB-INF/networkstatus.jsp | 22 +- web/WEB-INF/relay.jsp | 23 ++ 5 files changed, 29 insertions(+), 558 deletions(-)
diff --git a/src/org/torproject/ernie/web/NetworkStatusServlet.java b/src/org/torproject/ernie/web/NetworkStatusServlet.java index c206a19..63d7895 100644 --- a/src/org/torproject/ernie/web/NetworkStatusServlet.java +++ b/src/org/torproject/ernie/web/NetworkStatusServlet.java @@ -1,162 +1,17 @@ package org.torproject.ernie.web;
import java.io.*; -import java.sql.*; -import java.text.*; -import java.util.*; -import java.util.logging.*;
-import javax.naming.*; import javax.servlet.*; import javax.servlet.http.*; -import javax.sql.*;
import org.apache.commons.lang.time.*;
public class NetworkStatusServlet extends HttpServlet {
- private DataSource ds; - - private Logger logger; - - /** Known parameter values for the sort parameter. */ - private Set<String> validSortParameterValues; - - /** Known parameter values for the order parameter. */ - private Set<String> validOrderParameterValues; - - public void init() { - - /* Initialize logger. */ - this.logger = Logger.getLogger(NetworkStatusServlet.class.toString()); - - /* Look up data source. */ - try { - Context cxt = new InitialContext(); - this.ds = (DataSource) cxt.lookup("java:comp/env/jdbc/tordir"); - this.logger.info("Successfully looked up data source."); - } catch (NamingException e) { - this.logger.log(Level.WARNING, "Could not look up data source", e); - } - - /* Initialize known parameter values. */ - this.validSortParameterValues = new HashSet<String>(Arrays.asList(( - "nickname,bandwidth,orport,dirport,isbadexit,uptime"). - split(","))); - this.validOrderParameterValues = new HashSet<String>( - Arrays.asList(("desc,asc").split(","))); - - } - public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
- /* Try to parse parameters and override them with defaults if we - * cannot parse them. */ - String sortParameter = request.getParameter("sort"); - if (sortParameter != null) { - sortParameter = sortParameter.toLowerCase(); - } - if (sortParameter == null || sortParameter.length() < 1 || - !validSortParameterValues.contains(sortParameter)) { - sortParameter = "nickname"; - } - String orderParameter = request.getParameter("order"); - if (orderParameter != null) { - orderParameter = orderParameter.toLowerCase(); - } - if (orderParameter == null || orderParameter.length() < 1 || - !validOrderParameterValues.contains(orderParameter)) { - orderParameter = "asc"; - } - - /* Initialize list containing the results. */ - List<Map<String, Object>> status = - new ArrayList<Map<String, Object>>(); - - /* Connect to the database and retrieve data set. */ - try { - long requestedConnection = System.currentTimeMillis(); - Connection conn = this.ds.getConnection(); - Statement statement = conn.createStatement(); - - String orderBy = ((sortParameter.equals("uptime") || - sortParameter.equals("platform")) - ? "descriptor." : "statusentry.") + sortParameter; - String query = "SELECT statusentry.validafter, " - + "statusentry.nickname, statusentry.fingerprint, " - + "statusentry.descriptor, statusentry.published, " - + "statusentry.address, statusentry.orport, " - + "statusentry.dirport, statusentry.isauthority, " - + "statusentry.isbadexit, statusentry.isbaddirectory, " - + "statusentry.isexit, statusentry.isfast, " - + "statusentry.isguard, statusentry.ishsdir, " - + "statusentry.isnamed, statusentry.isstable, " - + "statusentry.isrunning, statusentry.isunnamed, " - + "statusentry.isvalid, statusentry.isv2dir, " - + "statusentry.isv3dir, statusentry.version, " - + "statusentry.bandwidth, statusentry.ports, " - + "statusentry.rawdesc, descriptor.uptime, " - + "descriptor.platform FROM statusentry JOIN descriptor " - + "ON descriptor.descriptor = statusentry.descriptor " - + "WHERE statusentry.validafter = " - + "(SELECT MAX(validafter) FROM consensus) " - + "ORDER BY " + orderBy + " " + orderParameter.toUpperCase(); - - ResultSet rs = statement.executeQuery(query); - - while (rs.next()) { - Map<String, Object> row = new HashMap<String, Object>(); - row.put("validafter", rs.getTimestamp(1)); - row.put("nickname", rs.getString(2)); - row.put("fingerprint", rs.getString(3)); - row.put("descriptor", rs.getString(4)); - row.put("published", rs.getTimestamp(5)); - row.put("address", rs.getString(6)); - row.put("orport", rs.getInt(7)); - row.put("dirport", rs.getInt(8)); - row.put("isauthority", rs.getBoolean(9)); - row.put("isbadexit", rs.getBoolean(10)); - row.put("isbaddirectory", rs.getBoolean(11)); - row.put("isexit", rs.getBoolean(12)); - row.put("isfast", rs.getBoolean(13)); - row.put("isguard", rs.getBoolean(14)); - row.put("ishsdir", rs.getBoolean(15)); - row.put("isnamed", rs.getBoolean(16)); - row.put("isstable", rs.getBoolean(17)); - row.put("isrunning", rs.getBoolean(18)); - row.put("isunnamed", rs.getBoolean(19)); - row.put("isvalid", rs.getBoolean(20)); - row.put("isv2dir", rs.getBoolean(21)); - row.put("isv3dir", rs.getBoolean(22)); - row.put("version", rs.getString(23)); - row.put("bandwidth", rs.getBigDecimal(24)); - row.put("ports", rs.getString(25)); - row.put("rawdesc", rs.getBytes(26)); - row.put("uptime", DurationFormatUtils.formatDuration( - rs.getBigDecimal(27).longValue() * 1000L, "d'd' HH:mm:ss")); - row.put("platform", rs.getString(28)); - row.put("validafterts", rs.getTimestamp(1).getTime()); - - status.add(row); - } - rs.close(); - statement.close(); - conn.close(); - this.logger.info("Returned a database connection to the pool after " - + (System.currentTimeMillis() - requestedConnection) - + " millis."); - request.setAttribute("status", status); - request.setAttribute("sort", sortParameter); - request.setAttribute("order", (orderParameter.equals("desc")) - ? "asc" : "desc"); - - } catch (SQLException e) { - response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); - this.logger.log(Level.WARNING, "Database error", e); - return; - } - /* Forward the request to the JSP. */ request.getRequestDispatcher("WEB-INF/networkstatus.jsp").forward( request, response); diff --git a/src/org/torproject/ernie/web/RelayServlet.java b/src/org/torproject/ernie/web/RelayServlet.java index 64f343d..79afac3 100644 --- a/src/org/torproject/ernie/web/RelayServlet.java +++ b/src/org/torproject/ernie/web/RelayServlet.java @@ -1,403 +1,18 @@ package org.torproject.ernie.web;
import java.io.*; -import java.math.*; -import java.sql.*; -import java.text.*; -import java.util.*; -import java.util.logging.*; -import java.util.regex.*;
-import javax.naming.*; import javax.servlet.*; import javax.servlet.http.*; -import javax.sql.*; - -import org.apache.commons.codec.*; -import org.apache.commons.codec.binary.*; - -import org.apache.commons.lang.*;
public class RelayServlet extends HttpServlet {
- private SimpleDateFormat dayFormat = - new SimpleDateFormat("yyyy-MM-dd"); - - private DataSource ds; - - private Logger logger; - - public void init() { - - /* Initialize logger. */ - this.logger = Logger.getLogger(RelayServlet.class.toString()); - - /* Initialize date format parser. */ - this.dayFormat.setTimeZone(TimeZone.getTimeZone("UTC")); - - /* Look up data source. */ - try { - Context cxt = new InitialContext(); - this.ds = (DataSource) cxt.lookup("java:comp/env/jdbc/tordir"); - this.logger.info("Successfully looked up data source."); - } catch (NamingException e) { - this.logger.log(Level.WARNING, "Could not look up data source", e); - } - } - - private void writeHeader(PrintWriter out) throws IOException { - out.println("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 " - + "Transitional//EN\"\n" - + "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n" - + "<html xmlns="http://www.w3.org/1999/xhtml%5C%22%3E%5Cn" - + " <head>\n" - + " <meta content="text/html; charset=ISO-8859-1"\n" - + " http-equiv="content-type" />\n" - + " <title>Relay</title>\n" - + " <meta http-equiv=Content-Type content="text/html; " - + "charset=iso-8859-1">\n" - + " <link href="/css/stylesheet-ltr.css" type=text/css " - + "rel=stylesheet>\n" - + " <link href="/images/favicon.ico" " - + "type=image/x-icon rel="shortcut icon">\n" - + " </head>\n" - + " <body>\n" - + " <div class="center">\n" - + " <table class="banner" border="0" cellpadding="0" " - + "cellspacing="0" summary="">\n" - + " <tr>\n" - + " <td class="banner-left"><a " - + "href="/index.html"><img src="/images/top-left.png" " - + "alt="Click to go to home page" width="193" " - + "height="79"></a></td>\n" - + " <td class="banner-middle">\n" - + " <a href="/">Home</a>\n" - + " <a href="graphs.html">Graphs</a>\n" - + " <a href="research.html">Research</a>\n" - + " <a href="status.html">Status</a>\n" - + " <br/>\n" - + " <font size="2">\n" - + " <a href="exonerator.html">ExoneraTor</a>\n" - + " <a class="current">Relay Search</a>\n" - + " <a href="consensus-health.html">Consensus " - + "Health</a>\n" - + " </font>\n" - + " </td>\n" - + " <td class="banner-right"></td>\n" - + " </tr>\n" - + " </table>\n" - + " <div class="main-column" style="margin:5; " - + "Padding:0;">\n" - + " <h2>Relay</h2>\n"); - } - - private void writeFooter(PrintWriter out) throws IOException { - out.println(" <br/>\n" - + " </div>\n" - + " </div>\n" - + " <div class="bottom" id="bottom">\n" - + " <p>This material is supported in part by the National " - + "Science Foundation under Grant No. CNS-0959138. Any " - + "opinions, finding, and conclusions or recommendations " - + "expressed in this material are those of the author(s) and " - + "do not necessarily reflect the views of the National " - + "Science Foundation.</p>\n" - + " <p>"Tor" and the "Onion Logo" are <a " - + "href="https://www.torproject.org/docs/trademark-faq.html.en%5C%22%3E" - + "registered trademarks</a> of The Tor Project, Inc.</p>\n" - + " <p>Data on this site is freely available under a <a " - + "href="http://creativecommons.org/publicdomain/zero/1.0/%5C%22%3E" - + "CC0 no copyright declaration</a>: To the extent possible " - + "under law, the Tor Project has waived all copyright and " - + "related or neighboring rights in the data. Graphs are " - + "licensed under a <a " - + "href="http://creativecommons.org/licenses/by/3.0/us/%5C%22%3E" - + "Creative Commons Attribution 3.0 United States " - + "License</a>.</p>\n" - + " </div>\n" - + " </body>\n" - + "</html>"); - out.close(); - } - public void doGet(HttpServletRequest request, - HttpServletResponse response) throws IOException, - ServletException { - - /* Measure how long it takes to process this request. */ - long started = System.currentTimeMillis(); - - /* Get print writer and start writing response. */ - PrintWriter out = response.getWriter(); - writeHeader(out); - - /* Check fingerprint parameter. */ - String fingerprintParameter = request.getParameter("fingerprint"); - boolean validParameter = true; - if (fingerprintParameter == null || - fingerprintParameter.length() < 8 || - fingerprintParameter.length() > 40) { - validParameter = false; - } else { - Pattern fingerprintPattern = Pattern.compile("^[0-9a-f]{8,40}$"); - if (!fingerprintPattern.matcher(fingerprintParameter.toLowerCase()). - matches()) { - validParameter = false; - } - } - if (!validParameter) { - out.write(" <br/><p>Sorry, "" - + StringEscapeUtils.escapeHtml(fingerprintParameter) - + "" is not a valid relay fingerprint. Please provide at " - + "least the first 8 hex characters of a relay " - + "fingerprint.</p>\n"); - writeFooter(out); - return; - } - - /* If we were only given a partial fingerprint, look up all - * fingerprints starting with that part to see if it's unique in the - * last 30 days. */ - String fingerprint = fingerprintParameter.toLowerCase(); - if (fingerprint.length() < 40) { - SortedSet<String> allFingerprints = new TreeSet<String>(); - try { - long requestedConnection = System.currentTimeMillis(); - Connection conn = this.ds.getConnection(); - Statement statement = conn.createStatement(); - String query = "SELECT DISTINCT fingerprint FROM statusentry " - + "WHERE validafter >= '" - + this.dayFormat.format(started - - 30L * 24L * 60L * 60L * 1000L) - + " 00:00:00' AND fingerprint LIKE '" + fingerprint + "%'"; - ResultSet rs = statement.executeQuery(query); - while (rs.next()) { - allFingerprints.add(rs.getString(1)); - } - rs.close(); - statement.close(); - conn.close(); - this.logger.info("Returned a database connection to the pool " - + "after " + (System.currentTimeMillis() - - requestedConnection) + " millis."); - } catch (SQLException e) { - out.println("<p><font color="red"><b>Warning: </b></font>We " - + "experienced an unknown database problem while looking up " - + "the relay with fingerprint starting with " - + fingerprintParameter + ". Unfortunately, this is very " - + "likely a <a href="https://trac.torproject.org/projects/" - + "tor/ticket/4406">known problem</a>. If you don't think " - + "these problems are related, please " - + "<a href="mailto:tor-assistants@torproject.org">let us " - + "know</a>!</p>\n"); - writeFooter(out); - return; - } - if (allFingerprints.size() == 0) { - out.write("<p>No relay found with fingerprint starting with " - + fingerprintParameter + " in the last 30 days.</p>"); - writeFooter(out); - return; - } else if (allFingerprints.size() > 1) { - out.println("<p>The fingerprint part " + fingerprintParameter - + " is not unique for relays running in the last 30 days. " - + "Please choose one of the following fingerprints:</p><ul>"); - for (String f : allFingerprints) { - out.println("<li><a href="relay.html?fingerprint=" + f + "">" - + f + "</a></li>"); - } - out.write("</ul><br/>"); - writeFooter(out); - return; - } else { - fingerprint = allFingerprints.first(); - } - } - - /* Print out in which consensuses this relay was last contained. */ - boolean foundRelay = false; - String lastDescriptor = null; - try { - long requestedConnection = System.currentTimeMillis(); - Connection conn = this.ds.getConnection(); - Statement statement = conn.createStatement(); - String query = "SELECT validafter, rawdesc FROM statusentry WHERE " - + "validafter >= '" - + this.dayFormat.format(started - 30L * 24L * 60L * 60L * 1000L) - + " 00:00:00' AND fingerprint = '" + fingerprint - + "' ORDER BY validafter DESC LIMIT 3"; - ResultSet rs = statement.executeQuery(query); - boolean printedDescription = false; - while (rs.next()) { - foundRelay = true; - if (!printedDescription) { - out.println("<p>The relay with fingerprint " - + (fingerprintParameter.length() < 40 ? "starting " : "") - + "with " + fingerprintParameter + " was last " - + "referenced in the following relay lists:</p>"); - printedDescription = true; - } - String validAfter = rs.getTimestamp(1).toString(). - substring(0, 19); - out.println(" <br/><tt>valid-after " - + "<a href="consensus?valid-after=" - + validAfter.replaceAll(":", "-").replaceAll(" ", "-") - + "" target="_blank">" + validAfter + "</a></tt><br/>"); - byte[] rawStatusEntry = rs.getBytes(2); - try { - String statusEntryLines = new String(rawStatusEntry, - "US-ASCII"); - String[] lines = statusEntryLines.split("\n"); - for (String line : lines) { - if (line.startsWith("r ")) { - String[] parts = line.split(" "); - String descriptor = String.format("%040x", - new BigInteger(1, Base64.decodeBase64(parts[3] - + "=="))); - if (lastDescriptor == null) { - lastDescriptor = descriptor; - } - out.println(" <tt>r " + parts[1] + " " + parts[2] + " " - + "<a href="descriptor.html?desc-id=" + descriptor - + "" target="_blank">" + parts[3] + "</a> " - + parts[4] + " " + parts[5] + " " + parts[6] + " " - + parts[7] + " " + parts[8] + "</tt><br/>"); - } else { - out.println(" <tt>" + line + "</tt><br/>"); - } - } - } catch (UnsupportedEncodingException e) { - /* This shouldn't happen, because we know that ASCII is - * supported. */ - } - } - rs.close(); - statement.close(); - conn.close(); - this.logger.info("Returned a database connection to the pool after " - + (System.currentTimeMillis() - requestedConnection) - + " millis."); - } catch (SQLException e) { - out.println("<p><font color="red"><b>Warning: </b></font>We " - + "experienced an unknown database problem while looking up " - + "the relay with fingerprint " - + (fingerprintParameter.length() < 40 ? "starting with " : "") - + fingerprintParameter + ". Unfortunately, this is very likely " - + "a <a href="https://trac.torproject.org/projects/tor/ticket" - + "/4406">known problem</a>. If you don't think these " - + "problems are related, please " - + "<a href="mailto:tor-assistants@torproject.org">let us " - + "know</a>!</p>\n"); - writeFooter(out); - return; - } - - /* If we didn't find this relay, stop here. */ - if (!foundRelay) { - out.write("<p>No relay found with fingerprint " - + (fingerprintParameter.length() < 40 ? "starting with " : "") - + fingerprintParameter + " in the last 30 days.</p>"); - writeFooter(out); - return; - } - - /* Look up last server and extra-info descriptor in the database. */ - String query = null, descriptor = null, nickname = null, - published = null, extrainfo = null; - byte[] rawDescriptor = null, rawExtrainfo = null; - if (lastDescriptor != null) { - try { - long requestedConnection = System.currentTimeMillis(); - Connection conn = this.ds.getConnection(); - Statement statement = conn.createStatement(); - query = "SELECT descriptor, nickname, published, extrainfo, " - + "rawdesc FROM descriptor WHERE descriptor = '" - + lastDescriptor + "'"; - ResultSet rs = statement.executeQuery(query); - if (rs.next()) { - descriptor = rs.getString(1); - nickname = rs.getString(2); - published = rs.getTimestamp(3).toString().substring(0, 19); - extrainfo = rs.getString(4); - rawDescriptor = rs.getBytes(5); - query = "SELECT rawdesc FROM extrainfo WHERE extrainfo = '" - + extrainfo + "'"; - rs = statement.executeQuery(query); - if (rs.next()) { - rawExtrainfo = rs.getBytes(1); - } - } - rs.close(); - statement.close(); - conn.close(); - this.logger.info("Returned a database connection to the pool " - + "after " + (System.currentTimeMillis() - - requestedConnection) + " millis."); - } catch (SQLException e) { - out.write("<br/><p><font color="red"><b>Warning: </b></font>" - + "Internal server error when looking up descriptor. The " - + "query was '" + query + "'. Unfortunately, this is very " - + "likely a <a href="https://trac.torproject.org/projects/" - + "tor/ticket/4406">known problem</a>. If you don't think " - + "these problems are related, please " - + "<a href="mailto:tor-assistants@torproject.org">let us " - + "know</a>!</p>\n"); - writeFooter(out); - return; - } - } - - /* If no descriptor was found, stop here. */ - if (descriptor == null) { - out.write("<p>No descriptor found with identifier " + descriptor - + " which was referenced in the last relay list.</p>"); - writeFooter(out); - return; - } - - /* Print out both server and extra-info descriptor. */ - out.write("<br/><p>The last referenced server descriptor published " - + "by this relay is:</p>"); - String descriptorString = new String(rawDescriptor, "US-ASCII"); - String escapedDescriptorString = StringEscapeUtils.escapeHtml( - descriptorString); - BufferedReader br = new BufferedReader(new StringReader( - escapedDescriptorString)); - String line = null; - while ((line = br.readLine()) != null) { - out.println(" <tt>" + line + "</tt><br/>"); - } - br.close(); - if (rawExtrainfo != null) { - out.println("<br/><p>Together with this server descriptor, the " - + "relay published the following extra-info descriptor:</p>"); - br = new BufferedReader(new StringReader(new String(rawExtrainfo, - "US-ASCII"))); - line = null; - while ((line = br.readLine()) != null) { - out.println(" <tt>" + line + "</tt><br/>"); - } - } - - /* Provide links to raw descriptors, too. */ - out.println("<br/><p>Note that the descriptor" + (rawExtrainfo != null - ? "s have" : " has") + " been converted to ASCII and reformatted " - + "for display purposes. You may also download the raw " - + "<a href="serverdesc?desc-id=" + descriptor - + "" target="_blank">server " + "descriptor</a>" - + (extrainfo != null ? " and <a href="extrainfodesc?desc-id=" - + extrainfo + "" target="_blank">extra-info descriptor</a>" - : "") + " as " + (extrainfo != null ? "they were" : "it was") - + " published to the directory authorities.</p>"); - - /* Display total lookup time on the results page. */ - long searchTime = System.currentTimeMillis() - started; - out.write(" <br/><p>Looking up this relay took us " - + String.format("%d.%03d", searchTime / 1000, searchTime % 1000) - + " seconds.</p>\n"); + HttpServletResponse response) throws IOException, ServletException {
- /* Finish writing response. */ - writeFooter(out); + /* Forward the request to the JSP. */ + request.getRequestDispatcher("WEB-INF/relay.jsp").forward( + request, response); } }
diff --git a/web/WEB-INF/banner.jsp b/web/WEB-INF/banner.jsp index da3b705..a081423 100644 --- a/web/WEB-INF/banner.jsp +++ b/web/WEB-INF/banner.jsp @@ -42,10 +42,6 @@ currentPage.endsWith("consensus-health.jsp")) { %><br> <font size="2"> - <a <%if (currentPage.endsWith("networkstatus.jsp") || - currentPage.endsWith("routerdetail.jsp")){ - %>class="current"<%} else {%>href="/networkstatus.html"<%} - %>>Network Status</a> <a <%if (currentPage.endsWith("exonerator.jsp")){ %>class="current"<%} else {%>href="/exonerator.html"<%} %>>ExoneraTor</a> diff --git a/web/WEB-INF/networkstatus.jsp b/web/WEB-INF/networkstatus.jsp index 4f47e2a..8f7b379 100644 --- a/web/WEB-INF/networkstatus.jsp +++ b/web/WEB-INF/networkstatus.jsp @@ -12,26 +12,8 @@ <%@ include file="banner.jsp"%> <div class="main-column"> <h2>Tor Metrics Portal: Network Status</h2> - <table> - <tr> - <th><a href="/networkstatus.html?sort=nickname&order=${sort=='nickname'?order:'desc'}">nickname</a></th> - <th><a href="/networkstatus.html?sort=bandwidth&order=${sort=='bandwidth'?order:'desc'}">bandwidth</a></th> - <th><a href="/networkstatus.html?sort=orport&order=${sort=='orport'?order:'desc'}">orport</a></th> - <th><a href="/networkstatus.html?sort=dirport&order=${sort=='dirport'?order:'desc'}">dirport</a></th> - <th><a href="/networkstatus.html?sort=isbadexit&order=${sort=='isbadexit'?order:'desc'}">isbadexit</a></th> - <th><a href="/networkstatus.html?sort=uptime&order=${sort=='uptime'?order:'desc'}">uptime</a></th> - </tr> - <c:forEach var="row" items="${status}"> - <tr> - <td><a href="/routerdetail.html?fingerprint=${row['fingerprint']}">${row['nickname']}</a></td> - <td>${row['bandwidth']}</td> - <td>${row['orport']}</td> - <td>${row['dirport']}</td> - <td>${row['isbadexit']}</td> - <td>${row['uptime']}</td> - </tr> - </c:forEach> - </table> + <br> + <p>Sorry, this page has been discontinued.</p> </div> </div> <div class="bottom" id="bottom"> diff --git a/web/WEB-INF/relay.jsp b/web/WEB-INF/relay.jsp new file mode 100644 index 0000000..952133a --- /dev/null +++ b/web/WEB-INF/relay.jsp @@ -0,0 +1,23 @@ +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> +<html> +<head> + <title>Tor Metrics Portal: Relay</title> + <meta http-equiv="content-type" content="text/html; charset=ISO-8859-1"> + <link href="/css/stylesheet-ltr.css" type="text/css" rel="stylesheet"> + <link href="/images/favicon.ico" type="image/x-icon" rel="shortcut icon"> +</head> +<body> + <div class="center"> + <%@ include file="banner.jsp"%> + <div class="main-column"> + <h2>Tor Metrics Portal: Relay</h2> + <br> + <p>Sorry, this page has been discontinued.</p> + </div> + </div> + <div class="bottom" id="bottom"> + <%@ include file="footer.jsp"%> + </div> +</body> +</html>