[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[tor-commits] [metrics-web/master] Fetch CollecTor's index.json for our own directory listings.
commit 05f8a74fb1d3b1c074f106c1d2045092545288d2
Author: Karsten Loesing <karsten.loesing@xxxxxxx>
Date: Sun Aug 20 20:19:34 2017 +0200
Fetch CollecTor's index.json for our own directory listings.
Implements #22836.
---
website/build.xml | 6 ++
.../torproject/metrics/web/CollecTorServlet.java | 54 +++++++++--
.../metrics/web/CollectorDirectoryProvider.java | 79 +++++++++++++++
.../torproject/metrics/web/DirectoryListing.java | 90 +++++++++++++++++
website/src/main/resources/etc/web.xml | 1 +
website/src/main/resources/web/WEB-INF/bottom.jsp | 2 +-
.../main/resources/web/WEB-INF/collector-files.jsp | 40 ++++++++
.../src/main/resources/web/WEB-INF/collector.jsp | 108 ++++++++++-----------
website/src/main/resources/web/WEB-INF/top.jsp | 52 +++++-----
.../metrics/web/DirectoryListingTest.java | 81 ++++++++++++++++
10 files changed, 425 insertions(+), 88 deletions(-)
diff --git a/website/build.xml b/website/build.xml
index dfa0b18..07bf84e 100644
--- a/website/build.xml
+++ b/website/build.xml
@@ -1,10 +1,13 @@
<project default="war" name="metrics-web" basedir=".">
+ <property name="metricslibversion" value="2.0.0"/>
+
<property name="libs" value="../shared/lib"/>
<include file="../shared/build-base.xml" as="basetask"/>
<target name="clean" depends="basetask.clean"/>
<target name="compile" depends="basetask.compile"/>
+ <target name="test" depends="basetask.test"/>
<patternset id="compile.libs" >
<include name="postgresql-jdbc3-9.2.jar"/>
@@ -19,6 +22,9 @@
<include name="commons-codec-1.10.jar"/>
<include name="commons-lang-2.6.jar"/>
<include name="gson-2.4.jar"/>
+ <include name="metrics-lib-${metricslibversion}.jar"/>
+ <include name="commons-compress-1.13.jar"/>
+ <include name="slf4j-api-1.7.22.jar"/>
</patternset>
<path id="classpath">
diff --git a/website/src/main/java/org/torproject/metrics/web/CollecTorServlet.java b/website/src/main/java/org/torproject/metrics/web/CollecTorServlet.java
index 74ca163..2b0a13a 100644
--- a/website/src/main/java/org/torproject/metrics/web/CollecTorServlet.java
+++ b/website/src/main/java/org/torproject/metrics/web/CollecTorServlet.java
@@ -4,28 +4,68 @@
package org.torproject.metrics.web;
import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+/** Servlet for CollecTor's "home" page and for CollecTor's directory listings
+ * based on the periodically fetched index.json file. */
public class CollecTorServlet extends AnyServlet {
private static final long serialVersionUID = -7054057945737357463L;
+ /** Host name of the CollecTor host with trailing slash omitted. */
+ private static final String COLLECTOR_HOST
+ = "https://collector.torproject.org";
+
+ /** Parsed and formatted directory listings. */
+ private CollectorDirectoryProvider collectorDirectory;
+
+ /** Initializes this servlet by retrieving the CollecTor host name from the
+ * configuration file and starting the periodic index.json downloader. */
@Override
- public void init() throws ServletException {
- super.init();
+ public void init(ServletConfig config) throws ServletException {
+ super.init(config);
+ this.collectorDirectory = new CollectorDirectoryProvider(COLLECTOR_HOST);
}
+ /** Handles requests for either CollecTor's "home" page or for directory
+ * listings. */
@Override
public void doGet(HttpServletRequest request,
HttpServletResponse response) throws IOException, ServletException {
-
- /* Forward the request to the JSP that does all the hard work. */
- request.setAttribute("categories", this.categories);
- request.getRequestDispatcher("WEB-INF/collector.jsp").forward(request,
- response);
+ String requestedPath = request.getRequestURI();
+ if (requestedPath.contains("/collector")) {
+ /* Possibly truncate any path prefix (like "/metrics") added by deploying
+ * this webapp as metrics.war rather than, say, ROOT.war. */
+ requestedPath = requestedPath.substring(requestedPath.indexOf(
+ "/collector"));
+ }
+ Map<String, List<String[]>> index;
+ if ("/collector.html".equals(requestedPath)) {
+ request.setAttribute("categories", this.categories);
+ request.getRequestDispatcher("WEB-INF/collector.jsp").forward(request,
+ response);
+ } else if (null == (index = this.collectorDirectory.getIndex())) {
+ response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+ "Index of CollecTor files unavailable.");
+ } else if (!requestedPath.endsWith("/")
+ && index.containsKey(requestedPath + "/")) {
+ response.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY);
+ response.setHeader("Location", requestedPath + "/");
+ } else if (index.containsKey(requestedPath)) {
+ request.setAttribute("categories", this.categories);
+ request.setAttribute("files", index.get(requestedPath));
+ request.getRequestDispatcher("/WEB-INF/collector-files.jsp").forward(
+ request, response);
+ } else {
+ response.sendError(HttpServletResponse.SC_NOT_FOUND,
+ "Unknown directory: " + requestedPath);
+ }
}
}
diff --git a/website/src/main/java/org/torproject/metrics/web/CollectorDirectoryProvider.java b/website/src/main/java/org/torproject/metrics/web/CollectorDirectoryProvider.java
new file mode 100644
index 0000000..9cd1e1e
--- /dev/null
+++ b/website/src/main/java/org/torproject/metrics/web/CollectorDirectoryProvider.java
@@ -0,0 +1,79 @@
+/* Copyright 2017 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.metrics.web;
+
+import org.torproject.descriptor.index.DirectoryNode;
+import org.torproject.descriptor.index.FileNode;
+import org.torproject.descriptor.index.IndexNode;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+/** Periodically fetches a remote index.json file and provides formatted
+ * directory listings for all contained directories and subdirectories. */
+public class CollectorDirectoryProvider implements Runnable {
+
+ /** Host name of the host serving the remote index.json with trailing slash
+ * omitted. */
+ private String host;
+
+ /** Scheduler for periodically downloading the remote index.json file. */
+ private final ScheduledExecutorService scheduler =
+ Executors.newScheduledThreadPool(1);
+
+ /** Last known directory listings. */
+ private final AtomicReference<Map<String, List<String[]>>> index
+ = new AtomicReference<>(null);
+
+ CollectorDirectoryProvider(String host) {
+ this.host = host;
+ this.scheduler.scheduleAtFixedRate(this, 0, 1, TimeUnit.MINUTES);
+ }
+
+ /** Returns the index object in a thread-safe way, blocking the invoking
+ * thread at most 10 seconds if no index object is available. */
+ Map<String, List<String[]>> getIndex() {
+ if (null == this.index.get()) {
+ long waitingSinceMillis = System.currentTimeMillis();
+ do {
+ try {
+ this.wait(200L);
+ } catch (InterruptedException e) {
+ /* Ignore. */
+ }
+ } while (null == index.get()
+ && System.currentTimeMillis() < waitingSinceMillis + 10000L);
+ }
+ return this.index.get();
+ }
+
+ /** Fetch the remote index.json and extract all we need to know to later
+ * produce directory listings as requested. */
+ @Override
+ public void run() {
+ IndexNode indexNode;
+ try {
+ indexNode = IndexNode.fetchIndex(this.host + "/index/index.json.gz");
+ } catch (Exception e) {
+ /* If we failed to fetch the remote index.json this time, abort the
+ * update and don't override what we possibly fetched last time. If this
+ * is a temporary problem, one of the next runs will update the index. If
+ * it's a permanent problem, we'll at least serve the last known files.
+ * Unless it's a permanent problem right from when we started in which
+ * case there's nothing we can do other than return 500. */
+ return;
+ }
+ this.index.set(new DirectoryListing(indexNode));
+ }
+
+}
+
diff --git a/website/src/main/java/org/torproject/metrics/web/DirectoryListing.java b/website/src/main/java/org/torproject/metrics/web/DirectoryListing.java
new file mode 100644
index 0000000..6dd09e4
--- /dev/null
+++ b/website/src/main/java/org/torproject/metrics/web/DirectoryListing.java
@@ -0,0 +1,90 @@
+/* Copyright 2017 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.metrics.web;
+
+import org.torproject.descriptor.index.DirectoryNode;
+import org.torproject.descriptor.index.FileNode;
+import org.torproject.descriptor.index.IndexNode;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+/** Map with all directory listings for all directories and subdirectories
+ * contained in an index.json file. */
+public class DirectoryListing extends HashMap<String, List<String[]>>
+ implements Map<String, List<String[]>> {
+
+ private IndexNode index;
+
+ DirectoryListing(IndexNode index) {
+ this.index = index;
+ extractDirectoryListings();
+ }
+
+ /** Extracts directory listing from an index node by visiting all nodes. */
+ private void extractDirectoryListings() {
+ Map<DirectoryNode, String> directoryNodes = new HashMap<>();
+ this.put("/collector/",
+ formatTableEntries("", "/", this.index.directories, this.index.files));
+ for (DirectoryNode directory : this.index.directories) {
+ directoryNodes.put(directory, "/");
+ }
+ while (!directoryNodes.isEmpty()) {
+ DirectoryNode currentDirectoryNode =
+ directoryNodes.keySet().iterator().next();
+ String parentPath = directoryNodes.remove(currentDirectoryNode);
+ if (null != currentDirectoryNode.directories) {
+ for (DirectoryNode subDirectoryNode
+ : currentDirectoryNode.directories) {
+ directoryNodes.put(subDirectoryNode, String.format("%s%s/",
+ parentPath, currentDirectoryNode.path));
+ }
+ }
+ this.put(String
+ .format("/collector%s%s/", parentPath, currentDirectoryNode.path),
+ formatTableEntries(parentPath, currentDirectoryNode.path + "/",
+ currentDirectoryNode.directories, currentDirectoryNode.files));
+ }
+ }
+
+ /** Formats table entries for a given directory. */
+ private List<String[]> formatTableEntries(String parentPath, String path,
+ SortedSet<DirectoryNode> directories, SortedSet<FileNode> files) {
+ List<String[]> tableEntries = new ArrayList<>();
+ tableEntries.add(new String[] { "Parent Directory",
+ String.format("/collector%s",
+ parentPath.isEmpty() ? ".html" : parentPath), "", "" });
+ if (null != directories) {
+ for (DirectoryNode subDirectoryNode : directories) {
+ tableEntries.add(new String[] { subDirectoryNode.path,
+ String.format("/collector%s%s%s/", parentPath, path,
+ subDirectoryNode.path), "", "" });
+ }
+ }
+ if (null != files) {
+ for (FileNode fileNode : new TreeSet<>(files).descendingSet()) {
+ tableEntries.add(new String[] { fileNode.path,
+ String.format("%s%s%s%s", this.index.path, parentPath,
+ path, fileNode.path), fileNode.lastModified,
+ formatBytes(fileNode.size) });
+ }
+ }
+ return tableEntries;
+ }
+
+ /** Formats a number of bytes to units B, KiB, MiB, etc. */
+ static String formatBytes(long bytes) {
+ if (bytes < 1024) {
+ return bytes + " B";
+ }
+ int exp = (int) (Math.log(bytes) / Math.log(1024));
+ char pre = "KMGTPE".charAt(exp - 1);
+ return String.format("%.1f %siB", bytes / Math.pow(1024, exp), pre);
+ }
+}
+
diff --git a/website/src/main/resources/etc/web.xml b/website/src/main/resources/etc/web.xml
index 38a30bb..830e8e7 100644
--- a/website/src/main/resources/etc/web.xml
+++ b/website/src/main/resources/etc/web.xml
@@ -303,6 +303,7 @@
<servlet-mapping>
<servlet-name>CollecTorServlet</servlet-name>
<url-pattern>/collector.html</url-pattern>
+ <url-pattern>/collector/*</url-pattern>
</servlet-mapping>
<servlet>
diff --git a/website/src/main/resources/web/WEB-INF/bottom.jsp b/website/src/main/resources/web/WEB-INF/bottom.jsp
index 835b286..6719adc 100644
--- a/website/src/main/resources/web/WEB-INF/bottom.jsp
+++ b/website/src/main/resources/web/WEB-INF/bottom.jsp
@@ -9,7 +9,7 @@
<div class="col-xs-6">
<p class="pull-right">
- <a href="about.html#contact">Contact</a>
+ <a href="/about.html#contact">Contact</a>
</p>
</div>
diff --git a/website/src/main/resources/web/WEB-INF/collector-files.jsp b/website/src/main/resources/web/WEB-INF/collector-files.jsp
new file mode 100644
index 0000000..da57144
--- /dev/null
+++ b/website/src/main/resources/web/WEB-INF/collector-files.jsp
@@ -0,0 +1,40 @@
+<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
+<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
+<jsp:include page="top.jsp">
+ <jsp:param name="pageTitle" value="Sources – Tor Metrics"/>
+ <jsp:param name="navActive" value="Sources"/>
+</jsp:include>
+
+ <div class="container">
+ <ul class="breadcrumb">
+ <li><a href="/">Home</a></li>
+ <li><a href="/sources.html">Sources</a></li>
+ <li><a href="/collector.html">CollecTor</a></li>
+ </ul>
+ </div>
+
+ <div class="container">
+ <div class="row">
+ <div class="col-xs-12">
+ <table class="table">
+ <thead>
+ <tr>
+ <th>Name</th>
+ <th>Last modified</th>
+ <th>Size</th>
+ </tr>
+ </thead>
+ <tbody>
+ <c:forEach var="file" items="${files}"><tr>
+ <td><a href="${file[1]}">${file[0]}</a></td>
+ <td>${file[2]}</td>
+ <td>${file[3]}</td>
+ </tr></c:forEach>
+ </tbody>
+ </table>
+ </div><!-- col -->
+ </div><!-- row -->
+ </div><!-- container -->
+
+<jsp:include page="bottom.jsp"/>
+
diff --git a/website/src/main/resources/web/WEB-INF/collector.jsp b/website/src/main/resources/web/WEB-INF/collector.jsp
index f57ccb8..871415c 100644
--- a/website/src/main/resources/web/WEB-INF/collector.jsp
+++ b/website/src/main/resources/web/WEB-INF/collector.jsp
@@ -32,8 +32,8 @@
network, or if you're developing an application that uses
Tor network data, this is your place to start.
</p>
-<a class="btn btn-primary btn-lg" style="margin: 10px" href="https://collector.torproject.org/recent/" target="_blank"><i class="fa fa-chevron-right" aria-hidden="true"></i> Browse Recent Descriptors</a>
-<a class="btn btn-primary btn-lg" style="margin: 10px" href="https://collector.torproject.org/archive/" target="_blank"><i class="fa fa-chevron-right" aria-hidden="true"></i> Browse Archived Descriptors</a>
+<a class="btn btn-primary btn-lg" style="margin: 10px" href="/collector/recent/"><i class="fa fa-chevron-right" aria-hidden="true"></i> Browse Recent Descriptors</a>
+<a class="btn btn-primary btn-lg" style="margin: 10px" href="/collector/archive/"><i class="fa fa-chevron-right" aria-hidden="true"></i> Browse Archived Descriptors</a>
</div><!-- text-center -->
@@ -65,53 +65,53 @@
<tr>
<td><a href="#type-server-descriptor">Relay Server Descriptors</a></td>
<td><code>@type server-descriptor 1.0</code></td>
- <td><a href="https://collector.torproject.org/recent/relay-descriptors/server-descriptors/" target="_blank" class="btn btn-primary btn-xs pull-left"><i class="fa fa-chevron-right" aria-hidden="true"></i> recent</a>
- <a href="https://collector.torproject.org/archive/relay-descriptors/server-descriptors/" target="_blank" class="btn btn-primary btn-xs pull-right"><i class="fa fa-chevron-right" aria-hidden="true"></i> archive</a></td>
+ <td><a href="/collector/recent/relay-descriptors/server-descriptors/" class="btn btn-primary btn-xs pull-left"><i class="fa fa-chevron-right" aria-hidden="true"></i> recent</a>
+ <a href="/collector/archive/relay-descriptors/server-descriptors/" class="btn btn-primary btn-xs pull-right"><i class="fa fa-chevron-right" aria-hidden="true"></i> archive</a></td>
</tr>
<tr>
<td><a href="#type-extra-info">Relay Extra-info Descriptors</a></td>
<td><code>@type extra-info 1.0</code></td>
- <td><a href="https://collector.torproject.org/recent/relay-descriptors/extra-infos/" target="_blank" class="btn btn-primary btn-xs pull-left"><i class="fa fa-chevron-right" aria-hidden="true"></i> recent</a>
- <a href="https://collector.torproject.org/archive/relay-descriptors/extra-infos/" target="_blank" class="btn btn-primary btn-xs pull-right"><i class="fa fa-chevron-right" aria-hidden="true"></i> archive</a></td>
+ <td><a href="/collector/recent/relay-descriptors/extra-infos/" class="btn btn-primary btn-xs pull-left"><i class="fa fa-chevron-right" aria-hidden="true"></i> recent</a>
+ <a href="/collector/archive/relay-descriptors/extra-infos/" class="btn btn-primary btn-xs pull-right"><i class="fa fa-chevron-right" aria-hidden="true"></i> archive</a></td>
</tr>
<tr>
<td><a href="#type-network-status-consensus-3">Network Status Consensuses</a></td>
<td><code>@type network-status-consensus-3 1.0</code></td>
- <td><a href="https://collector.torproject.org/recent/relay-descriptors/consensuses/" target="_blank" class="btn btn-primary btn-xs pull-left"><i class="fa fa-chevron-right" aria-hidden="true"></i> recent</a>
- <a href="https://collector.torproject.org/archive/relay-descriptors/consensuses/" target="_blank" class="btn btn-primary btn-xs pull-right"><i class="fa fa-chevron-right" aria-hidden="true"></i> archive</a></td>
+ <td><a href="/collector/recent/relay-descriptors/consensuses/" class="btn btn-primary btn-xs pull-left"><i class="fa fa-chevron-right" aria-hidden="true"></i> recent</a>
+ <a href="/collector/archive/relay-descriptors/consensuses/" class="btn btn-primary btn-xs pull-right"><i class="fa fa-chevron-right" aria-hidden="true"></i> archive</a></td>
</tr>
<tr>
<td><a href="#type-network-status-vote-3">Network Status Votes</a></td>
<td><code>@type network-status-vote-3 1.0</code></td>
- <td><a href="https://collector.torproject.org/recent/relay-descriptors/votes/" target="_blank" class="btn btn-primary btn-xs pull-left"><i class="fa fa-chevron-right" aria-hidden="true"></i> recent</a>
- <a href="https://collector.torproject.org/archive/relay-descriptors/votes/" target="_blank" class="btn btn-primary btn-xs pull-right"><i class="fa fa-chevron-right" aria-hidden="true"></i> archive</a></td>
+ <td><a href="/collector/recent/relay-descriptors/votes/" class="btn btn-primary btn-xs pull-left"><i class="fa fa-chevron-right" aria-hidden="true"></i> recent</a>
+ <a href="/collector/archive/relay-descriptors/votes/" class="btn btn-primary btn-xs pull-right"><i class="fa fa-chevron-right" aria-hidden="true"></i> archive</a></td>
</tr>
<tr>
<td><a href="#type-dir-key-certificate-3">Directory Key Certificates</a></td>
<td><code>@type dir-key-certificate-3 1.0</code></td>
- <td><a href="https://collector.torproject.org/archive/relay-descriptors/" target="_blank" class="btn btn-primary btn-xs pull-right"><i class="fa fa-chevron-right" aria-hidden="true"></i> archive</a></td>
+ <td><a href="/collector/archive/relay-descriptors/" class="btn btn-primary btn-xs pull-right"><i class="fa fa-chevron-right" aria-hidden="true"></i> archive</a></td>
</tr>
<tr>
<td><a href="#type-network-status-microdesc-consensus-3">Microdescriptor Consensuses</a></td>
<td><code>@type network-status-microdesc-consensus-3 1.0</code></td>
- <td><a href="https://collector.torproject.org/recent/relay-descriptors/microdescs/consensus-microdesc/" target="_blank" class="btn btn-primary btn-xs pull-left"><i class="fa fa-chevron-right" aria-hidden="true"></i> recent</a>
- <a href="https://collector.torproject.org/archive/relay-descriptors/microdescs/" target="_blank" class="btn btn-primary btn-xs pull-right"><i class="fa fa-chevron-right" aria-hidden="true"></i> archive</a></td>
+ <td><a href="/collector/recent/relay-descriptors/microdescs/consensus-microdesc/" class="btn btn-primary btn-xs pull-left"><i class="fa fa-chevron-right" aria-hidden="true"></i> recent</a>
+ <a href="/collector/archive/relay-descriptors/microdescs/" class="btn btn-primary btn-xs pull-right"><i class="fa fa-chevron-right" aria-hidden="true"></i> archive</a></td>
</tr>
<tr>
<td><a href="#type-microdescriptor">Microdescriptors</a></td>
<td><code>@type microdescriptor 1.0</code></td>
- <td><a href="https://collector.torproject.org/recent/relay-descriptors/microdescs/micro/" target="_blank" class="btn btn-primary btn-xs pull-left"><i class="fa fa-chevron-right" aria-hidden="true"></i> recent</a>
- <a href="https://collector.torproject.org/archive/relay-descriptors/microdescs/" target="_blank" class="btn btn-primary btn-xs pull-right"><i class="fa fa-chevron-right" aria-hidden="true"></i> archive</a></td>
+ <td><a href="/collector/recent/relay-descriptors/microdescs/micro/" class="btn btn-primary btn-xs pull-left"><i class="fa fa-chevron-right" aria-hidden="true"></i> recent</a>
+ <a href="/collector/archive/relay-descriptors/microdescs/" class="btn btn-primary btn-xs pull-right"><i class="fa fa-chevron-right" aria-hidden="true"></i> archive</a></td>
</tr>
<tr>
<td><a href="#type-network-status-2">Version 2 Network Statuses</a></td>
<td><code>@type network-status-2 1.0</code></td>
- <td><a href="https://collector.torproject.org/archive/relay-descriptors/statuses/" target="_blank" class="btn btn-primary btn-xs pull-right"><i class="fa fa-chevron-right" aria-hidden="true"></i> archive</a></td>
+ <td><a href="/collector/archive/relay-descriptors/statuses/" class="btn btn-primary btn-xs pull-right"><i class="fa fa-chevron-right" aria-hidden="true"></i> archive</a></td>
</tr>
<tr>
<td><a href="#type-directory">Version 1 Directories</a></td>
<td><code>@type directory 1.0</code></td>
- <td><a href="https://collector.torproject.org/archive/relay-descriptors/tor/" target="_blank" class="btn btn-primary btn-xs pull-right"><i class="fa fa-chevron-right" aria-hidden="true"></i> archive</a></td>
+ <td><a href="/collector/archive/relay-descriptors/tor/" class="btn btn-primary btn-xs pull-right"><i class="fa fa-chevron-right" aria-hidden="true"></i> archive</a></td>
</tr>
<tr class="tableHeadline">
<td colspan="3"><b><a href="#bridge-descriptors">Tor Bridge Descriptors</a></b></td>
@@ -119,20 +119,20 @@
<tr>
<td><a href="#type-bridge-network-status">Bridge Network Statuses</a></td>
<td><code>@type bridge-network-status 1.2</code></td>
- <td><a href="https://collector.torproject.org/recent/bridge-descriptors/statuses/" target="_blank" class="btn btn-primary btn-xs pull-left"><i class="fa fa-chevron-right" aria-hidden="true"></i> recent</a>
- <a href="https://collector.torproject.org/archive/bridge-descriptors/statuses/" target="_blank" class="btn btn-primary btn-xs pull-right"><i class="fa fa-chevron-right" aria-hidden="true"></i> archive</a></td>
+ <td><a href="/collector/recent/bridge-descriptors/statuses/" class="btn btn-primary btn-xs pull-left"><i class="fa fa-chevron-right" aria-hidden="true"></i> recent</a>
+ <a href="/collector/archive/bridge-descriptors/statuses/" class="btn btn-primary btn-xs pull-right"><i class="fa fa-chevron-right" aria-hidden="true"></i> archive</a></td>
</tr>
<tr>
<td><a href="#type-bridge-server-descriptor">Bridge Server Descriptors</a></td>
<td><code>@type bridge-server-descriptor 1.2</code></td>
- <td><a href="https://collector.torproject.org/recent/bridge-descriptors/server-descriptors/" target="_blank" class="btn btn-primary btn-xs pull-left"><i class="fa fa-chevron-right" aria-hidden="true"></i> recent</a>
- <a href="https://collector.torproject.org/archive/bridge-descriptors/server-descriptors/" target="_blank" class="btn btn-primary btn-xs pull-right"><i class="fa fa-chevron-right" aria-hidden="true"></i> archive</a></td>
+ <td><a href="/collector/recent/bridge-descriptors/server-descriptors/" class="btn btn-primary btn-xs pull-left"><i class="fa fa-chevron-right" aria-hidden="true"></i> recent</a>
+ <a href="/collector/archive/bridge-descriptors/server-descriptors/" class="btn btn-primary btn-xs pull-right"><i class="fa fa-chevron-right" aria-hidden="true"></i> archive</a></td>
</tr>
<tr>
<td><a href="#type-bridge-extra-info">Bridge Extra-info Descriptors</a></td>
<td><code>@type bridge-extra-info 1.3</code></td>
- <td><a href="https://collector.torproject.org/recent/bridge-descriptors/extra-infos/" target="_blank" class="btn btn-primary btn-xs pull-left"><i class="fa fa-chevron-right" aria-hidden="true"></i> recent</a>
- <a href="https://collector.torproject.org/archive/bridge-descriptors/extra-infos/" target="_blank" class="btn btn-primary btn-xs pull-right"><i class="fa fa-chevron-right" aria-hidden="true"></i> archive</a></td>
+ <td><a href="/collector/recent/bridge-descriptors/extra-infos/" class="btn btn-primary btn-xs pull-left"><i class="fa fa-chevron-right" aria-hidden="true"></i> recent</a>
+ <a href="/collector/archive/bridge-descriptors/extra-infos/" class="btn btn-primary btn-xs pull-right"><i class="fa fa-chevron-right" aria-hidden="true"></i> archive</a></td>
</tr>
<tr class="tableHeadline">
<td colspan="3"><b><a href="#tor-hidden-service-descriptors">Tor Hidden Service Descriptors</a></b></td>
@@ -148,7 +148,7 @@
<tr>
<td><a href="#type-bridge-pool-assignment">Bridge Pool Assignments</a></td>
<td><code>@type bridge-pool-assignment 1.0</code></td>
- <td><a href="https://collector.torproject.org/archive/bridge-pool-assignments/" target="_blank" class="btn btn-primary btn-xs pull-right"><i class="fa fa-chevron-right" aria-hidden="true"></i> archive</a></td>
+ <td><a href="/collector/archive/bridge-pool-assignments/" class="btn btn-primary btn-xs pull-right"><i class="fa fa-chevron-right" aria-hidden="true"></i> archive</a></td>
</tr>
<tr class="tableHeadline">
<td colspan="3"><b><a href="#exit-lists">TorDNSEL's Exit Lists</a></b></td>
@@ -156,8 +156,8 @@
<tr>
<td><a href="#type-tordnsel">Exit Lists</a></td>
<td><code>@type tordnsel 1.0</code></td>
- <td><a href="https://collector.torproject.org/recent/exit-lists/" target="_blank" class="btn btn-primary btn-xs pull-left"><i class="fa fa-chevron-right" aria-hidden="true"></i> recent</a>
- <a href="https://collector.torproject.org/archive/exit-lists/" target="_blank" class="btn btn-primary btn-xs pull-right"><i class="fa fa-chevron-right" aria-hidden="true"></i> archive</a></td>
+ <td><a href="/collector/recent/exit-lists/" class="btn btn-primary btn-xs pull-left"><i class="fa fa-chevron-right" aria-hidden="true"></i> recent</a>
+ <a href="/collector/archive/exit-lists/" class="btn btn-primary btn-xs pull-right"><i class="fa fa-chevron-right" aria-hidden="true"></i> archive</a></td>
</tr>
<tr class="tableHeadline">
<td colspan="3"><b><a href="#torperf">Torperf's and OnionPerf's Performance Data</a></b></td>
@@ -165,8 +165,8 @@
<tr>
<td><a href="#type-torperf">Torperf Measurement Results</a></td>
<td><code>@type torperf 1.1</code></td>
- <td><a href="https://collector.torproject.org/recent/torperf/" target="_blank" class="btn btn-primary btn-xs pull-left"><i class="fa fa-chevron-right" aria-hidden="true"></i> recent</a>
- <a href="https://collector.torproject.org/archive/torperf/" target="_blank" class="btn btn-primary btn-xs pull-right"><i class="fa fa-chevron-right" aria-hidden="true"></i> archive</a></td>
+ <td><a href="/collector/recent/torperf/" class="btn btn-primary btn-xs pull-left"><i class="fa fa-chevron-right" aria-hidden="true"></i> recent</a>
+ <a href="/collector/archive/torperf/" class="btn btn-primary btn-xs pull-right"><i class="fa fa-chevron-right" aria-hidden="true"></i> archive</a></td>
</tr>
</tbody>
</table>
@@ -213,8 +213,8 @@ earlier protocol
<h3 id="type-server-descriptor" class="hover">Relay Server Descriptors
<small><code>@type server-descriptor 1.0</code></small>
-<a href="https://collector.torproject.org/recent/relay-descriptors/server-descriptors/" target="_blank" class="btn btn-primary btn-xs"><i class="fa fa-chevron-right" aria-hidden="true"></i> recent</a>
-<a href="https://collector.torproject.org/archive/relay-descriptors/server-descriptors/" target="_blank" class="btn btn-primary btn-xs"><i class="fa fa-chevron-right" aria-hidden="true"></i> archive</a>
+<a href="/collector/recent/relay-descriptors/server-descriptors/" class="btn btn-primary btn-xs"><i class="fa fa-chevron-right" aria-hidden="true"></i> recent</a>
+<a href="/collector/archive/relay-descriptors/server-descriptors/" class="btn btn-primary btn-xs"><i class="fa fa-chevron-right" aria-hidden="true"></i> archive</a>
<a href="#type-server-descriptor" class="anchor">#</a>
</h3>
@@ -231,8 +231,8 @@ file.
<h3 id="type-extra-info" class="hover">Relay Extra-info Descriptors
<small><code>@type extra-info 1.0</code></small>
-<a href="https://collector.torproject.org/recent/relay-descriptors/extra-infos/" target="_blank" class="btn btn-primary btn-xs"><i class="fa fa-chevron-right" aria-hidden="true"></i> recent</a>
-<a href="https://collector.torproject.org/archive/relay-descriptors/extra-infos/" target="_blank" class="btn btn-primary btn-xs"><i class="fa fa-chevron-right" aria-hidden="true"></i> archive</a>
+<a href="/collector/recent/relay-descriptors/extra-infos/" class="btn btn-primary btn-xs"><i class="fa fa-chevron-right" aria-hidden="true"></i> recent</a>
+<a href="/collector/archive/relay-descriptors/extra-infos/" class="btn btn-primary btn-xs"><i class="fa fa-chevron-right" aria-hidden="true"></i> archive</a>
<a href="#type-extra-info" class="anchor">#</a>
</h3>
@@ -249,8 +249,8 @@ file.
<h3 id="type-network-status-consensus-3" class="hover">Network Status Consensuses
<small><code>@type network-status-consensus-3 1.0</code></small>
-<a href="https://collector.torproject.org/recent/relay-descriptors/consensuses/" target="_blank" class="btn btn-primary btn-xs"><i class="fa fa-chevron-right" aria-hidden="true"></i> recent</a>
-<a href="https://collector.torproject.org/archive/relay-descriptors/consensuses/" target="_blank" class="btn btn-primary btn-xs"><i class="fa fa-chevron-right" aria-hidden="true"></i> archive</a>
+<a href="/collector/recent/relay-descriptors/consensuses/" class="btn btn-primary btn-xs"><i class="fa fa-chevron-right" aria-hidden="true"></i> recent</a>
+<a href="/collector/archive/relay-descriptors/consensuses/" class="btn btn-primary btn-xs"><i class="fa fa-chevron-right" aria-hidden="true"></i> archive</a>
<a href="#type-network-status-consensus-3" class="anchor">#</a>
</h3>
@@ -265,8 +265,8 @@ flags, heuristics used for relay selection, etc.
<h3 id="type-network-status-vote-3" class="hover">Network Status Votes
<small><code>@type network-status-vote-3 1.0</code></small>
-<a href="https://collector.torproject.org/recent/relay-descriptors/votes/" target="_blank" class="btn btn-primary btn-xs"><i class="fa fa-chevron-right" aria-hidden="true"></i> recent</a>
-<a href="https://collector.torproject.org/archive/relay-descriptors/votes/" target="_blank" class="btn btn-primary btn-xs"><i class="fa fa-chevron-right" aria-hidden="true"></i> archive</a>
+<a href="/collector/recent/relay-descriptors/votes/" class="btn btn-primary btn-xs"><i class="fa fa-chevron-right" aria-hidden="true"></i> recent</a>
+<a href="/collector/archive/relay-descriptors/votes/" class="btn btn-primary btn-xs"><i class="fa fa-chevron-right" aria-hidden="true"></i> archive</a>
<a href="#type-network-status-vote-3" class="anchor">#</a>
</h3>
@@ -278,7 +278,7 @@ Vote documents are by far the largest documents provided here.
<h3 id="type-dir-key-certificate-3" class="hover">Directory Key Certificates
<small><code>@type dir-key-certificate-3 1.0</code></small>
-<a href="https://collector.torproject.org/archive/relay-descriptors/" target="_blank" class="btn btn-primary btn-xs"><i class="fa fa-chevron-right" aria-hidden="true"></i> archive</a>
+<a href="/collector/archive/relay-descriptors/" class="btn btn-primary btn-xs"><i class="fa fa-chevron-right" aria-hidden="true"></i> archive</a>
<a href="#type-dir-key-certificate-3" class="anchor">#</a>
</h3>
@@ -291,8 +291,8 @@ available in a single descriptor archive tarball.
<h3 id="type-network-status-microdesc-consensus-3" class="hover">Microdescriptor Consensuses
<small><code>@type network-status-microdesc-consensus-3 1.0</code></small>
-<a href="https://collector.torproject.org/recent/relay-descriptors/microdescs/" target="_blank" class="btn btn-primary btn-xs"><i class="fa fa-chevron-right" aria-hidden="true"></i> recent</a>
-<a href="https://collector.torproject.org/archive/relay-descriptors/microdescs/" target="_blank" class="btn btn-primary btn-xs"><i class="fa fa-chevron-right" aria-hidden="true"></i> archive</a>
+<a href="/collector/recent/relay-descriptors/microdescs/" class="btn btn-primary btn-xs"><i class="fa fa-chevron-right" aria-hidden="true"></i> recent</a>
+<a href="/collector/archive/relay-descriptors/microdescs/" class="btn btn-primary btn-xs"><i class="fa fa-chevron-right" aria-hidden="true"></i> archive</a>
<a href="#type-network-status-microdesc-consensus-3" class="anchor">#</a>
</h3>
@@ -309,8 +309,8 @@ together.
<h3 id="type-microdescriptor" class="hover">Microdescriptors
<small><code>@type microdescriptor 1.0</code></small>
-<a href="https://collector.torproject.org/recent/relay-descriptors/microdescs/" target="_blank" class="btn btn-primary btn-xs"><i class="fa fa-chevron-right" aria-hidden="true"></i> recent</a>
-<a href="https://collector.torproject.org/archive/relay-descriptors/microdescs/" target="_blank" class="btn btn-primary btn-xs"><i class="fa fa-chevron-right" aria-hidden="true"></i> archive</a>
+<a href="/collector/recent/relay-descriptors/microdescs/" class="btn btn-primary btn-xs"><i class="fa fa-chevron-right" aria-hidden="true"></i> recent</a>
+<a href="/collector/archive/relay-descriptors/microdescs/" class="btn btn-primary btn-xs"><i class="fa fa-chevron-right" aria-hidden="true"></i> archive</a>
<a href="#type-microdescriptor" class="anchor">#</a>
</h3>
@@ -328,7 +328,7 @@ file.
<h3 id="type-network-status-2" class="hover">Version 2 Network Statuses
<small><code>@type network-status-2 1.0</code></small>
-<a href="https://collector.torproject.org/archive/relay-descriptors/statuses/" target="_blank" class="btn btn-primary btn-xs"><i class="fa fa-chevron-right" aria-hidden="true"></i> archive</a>
+<a href="/collector/archive/relay-descriptors/statuses/" class="btn btn-primary btn-xs"><i class="fa fa-chevron-right" aria-hidden="true"></i> archive</a>
<a href="#type-network-status-2" class="anchor">#</a>
</h3>
@@ -343,7 +343,7 @@ We stopped archiving version 2 network statuses in 2012.
<h3 id="type-directory" class="hover">Version 1 Directories
<small><code>@type directory 1.0</code></small>
-<a href="https://collector.torproject.org/archive/relay-descriptors/tor/" target="_blank" class="btn btn-primary btn-xs"><i class="fa fa-chevron-right" aria-hidden="true"></i> archive</a>
+<a href="/collector/archive/relay-descriptors/tor/" class="btn btn-primary btn-xs"><i class="fa fa-chevron-right" aria-hidden="true"></i> archive</a>
<a href="#type-directory" class="anchor">#</a>
</h3>
@@ -372,8 +372,8 @@ The sanitizing steps are specified in detail on a separate
<h3 id="type-bridge-network-status" class="hover">Bridge Network Statuses
<small><code>@type bridge-network-status 1.2</code></small>
-<a href="https://collector.torproject.org/recent/bridge-descriptors/statuses/" target="_blank" class="btn btn-primary btn-xs"><i class="fa fa-chevron-right" aria-hidden="true"></i> recent</a>
-<a href="https://collector.torproject.org/archive/bridge-descriptors/" target="_blank" class="btn btn-primary btn-xs"><i class="fa fa-chevron-right" aria-hidden="true"></i> archive</a>
+<a href="/collector/recent/bridge-descriptors/statuses/" class="btn btn-primary btn-xs"><i class="fa fa-chevron-right" aria-hidden="true"></i> recent</a>
+<a href="/collector/archive/bridge-descriptors/" class="btn btn-primary btn-xs"><i class="fa fa-chevron-right" aria-hidden="true"></i> archive</a>
<a href="#type-bridge-network-status" class="anchor">#</a>
</h3>
@@ -397,8 +397,8 @@ authority which produced the document, to the header.</li>
<h3 id="type-bridge-server-descriptor" class="hover">Bridge Server descriptors
<small><code>@type bridge-server-descriptor 1.2</code></small>
-<a href="https://collector.torproject.org/recent/bridge-descriptors/server-descriptors/" target="_blank" class="btn btn-primary btn-xs"><i class="fa fa-chevron-right" aria-hidden="true"></i> recent</a>
-<a href="https://collector.torproject.org/archive/bridge-descriptors/" target="_blank" class="btn btn-primary btn-xs"><i class="fa fa-chevron-right" aria-hidden="true"></i> archive</a>
+<a href="/collector/recent/bridge-descriptors/server-descriptors/" class="btn btn-primary btn-xs"><i class="fa fa-chevron-right" aria-hidden="true"></i> recent</a>
+<a href="/collector/archive/bridge-descriptors/" class="btn btn-primary btn-xs"><i class="fa fa-chevron-right" aria-hidden="true"></i> archive</a>
<a href="#type-bridge-server-descriptor" class="anchor">#</a>
</h3>
@@ -431,8 +431,8 @@ ports.</li>
<h3 id="type-bridge-extra-info" class="hover">Bridge Extra-info Descriptors
<small><code>@type bridge-extra-info 1.3</code></small>
-<a href="https://collector.torproject.org/recent/bridge-descriptors/extra-infos/" target="_blank" class="btn btn-primary btn-xs"><i class="fa fa-chevron-right" aria-hidden="true"></i> recent</a>
-<a href="https://collector.torproject.org/archive/bridge-descriptors/" target="_blank" class="btn btn-primary btn-xs"><i class="fa fa-chevron-right" aria-hidden="true"></i> archive</a>
+<a href="/collector/recent/bridge-descriptors/extra-infos/" class="btn btn-primary btn-xs"><i class="fa fa-chevron-right" aria-hidden="true"></i> recent</a>
+<a href="/collector/archive/bridge-descriptors/" class="btn btn-primary btn-xs"><i class="fa fa-chevron-right" aria-hidden="true"></i> archive</a>
<a href="#type-bridge-extra-info" class="anchor">#</a>
</h3>
@@ -514,7 +514,7 @@ statistical analysis.
<h3 id="type-bridge-pool-assignment" class="hover">Bridge Pool Assignments
<small><code>@type bridge-pool-assignment 1.0</code></small>
-<a href="https://collector.torproject.org/archive/bridge-pool-assignments/" target="_blank" class="btn btn-primary btn-xs"><i class="fa fa-chevron-right" aria-hidden="true"></i> archive</a>
+<a href="/collector/archive/bridge-pool-assignments/" class="btn btn-primary btn-xs"><i class="fa fa-chevron-right" aria-hidden="true"></i> archive</a>
<a href="#type-bridge-pool-assignment" class="anchor">#</a>
</h3>
@@ -573,8 +573,8 @@ when exiting through them.
<h3 id="type-tordnsel" class="hover">Exit Lists
<small><code>@type tordnsel 1.0</code></small>
-<a href="https://collector.torproject.org/recent/exit-lists/" target="_blank" class="btn btn-primary btn-xs"><i class="fa fa-chevron-right" aria-hidden="true"></i> recent</a>
-<a href="https://collector.torproject.org/archive/exit-lists/" target="_blank" class="btn btn-primary btn-xs"><i class="fa fa-chevron-right" aria-hidden="true"></i> archive</a>
+<a href="/collector/recent/exit-lists/" class="btn btn-primary btn-xs"><i class="fa fa-chevron-right" aria-hidden="true"></i> recent</a>
+<a href="/collector/archive/exit-lists/" class="btn btn-primary btn-xs"><i class="fa fa-chevron-right" aria-hidden="true"></i> archive</a>
<a href="#type-tordnsel" class="anchor">#</a>
</h3>
@@ -617,8 +617,8 @@ over the Tor network and notes how long substeps take.
<h3 id="type-torperf" class="hover">Torperf and OnionPerf Measurement Results
<small><code>@type torperf 1.1</code></small>
-<a href="https://collector.torproject.org/recent/torperf/" target="_blank" class="btn btn-primary btn-xs"><i class="fa fa-chevron-right" aria-hidden="true"></i> recent</a>
-<a href="https://collector.torproject.org/archive/torperf/" target="_blank" class="btn btn-primary btn-xs"><i class="fa fa-chevron-right" aria-hidden="true"></i> archive</a>
+<a href="/collector/recent/torperf/" class="btn btn-primary btn-xs"><i class="fa fa-chevron-right" aria-hidden="true"></i> recent</a>
+<a href="/collector/archive/torperf/" class="btn btn-primary btn-xs"><i class="fa fa-chevron-right" aria-hidden="true"></i> archive</a>
<a href="#type-torperf" class="anchor">#</a>
</h3>
diff --git a/website/src/main/resources/web/WEB-INF/top.jsp b/website/src/main/resources/web/WEB-INF/top.jsp
index c013780..e5e95eb 100644
--- a/website/src/main/resources/web/WEB-INF/top.jsp
+++ b/website/src/main/resources/web/WEB-INF/top.jsp
@@ -7,7 +7,7 @@
<title>${param.pageTitle}</title>
<meta charset="utf-8">
- <link href="images/favicon.ico" type="image/x-icon" rel="shortcut icon">
+ <link href="/images/favicon.ico" type="image/x-icon" rel="shortcut icon">
<!-- yes, we are handheld friendly :) -->
<meta name="HandheldFriendly" content="True">
@@ -15,31 +15,31 @@
<meta name="apple-mobile-web-app-capable" content="yes">
<!-- icons for mobile devices -->
- <link rel="apple-touch-icon" href="images/apple-touch-icon-152x152.png">
- <link rel="shortcut icon" href="images/android-icon.png" sizes="196x196">
- <meta name="msapplication-square70x70logo" content="images/smalltile.png">
- <meta name="msapplication-square150x150logo" content="images/mediumtile.png">
- <meta name="msapplication-wide310x150logo" content="images/widetile.png">
- <meta name="msapplication-square310x310logo" content="images/largetile.png">
+ <link rel="apple-touch-icon" href="/images/apple-touch-icon-152x152.png">
+ <link rel="shortcut icon" href="/images/android-icon.png" sizes="196x196">
+ <meta name="msapplication-square70x70logo" content="/images/smalltile.png">
+ <meta name="msapplication-square150x150logo" content="/images/mediumtile.png">
+ <meta name="msapplication-wide310x150logo" content="/images/widetile.png">
+ <meta name="msapplication-square310x310logo" content="/images/largetile.png">
<!-- jQuery -->
- <script src="js/jquery-3.2.1.min.js"></script>
+ <script src="/js/jquery-3.2.1.min.js"></script>
<!-- Bootstrap -->
- <link rel="stylesheet" href="css/bootstrap.min.css">
- <script src="js/bootstrap.min.js"></script>
+ <link rel="stylesheet" href="/css/bootstrap.min.css">
+ <script src="/js/bootstrap.min.js"></script>
<!-- Fonts -->
- <link rel="stylesheet" href="css/font-awesome.min.css">
- <link rel="stylesheet" href="fonts/source-sans-pro.css">
+ <link rel="stylesheet" href="/css/font-awesome.min.css">
+ <link rel="stylesheet" href="/fonts/source-sans-pro.css">
<!-- Prism -->
- <link rel="stylesheet" href="css/prism.css">
- <script src="js/prism.js"></script>
+ <link rel="stylesheet" href="/css/prism.css">
+ <script src="/js/prism.js"></script>
<!-- custom styles and javascript -->
- <link rel="stylesheet" href="css/style.css">
- <script src="js/script.js"></script>
+ <link rel="stylesheet" href="/css/style.css">
+ <script src="/js/script.js"></script>
</head>
@@ -69,7 +69,7 @@ document.write('<div class="topButton" style="display:none;"><a href="#top"><i c
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</label>
- <a class="navbar-brand visible-xs" href="/"><img src="images/tor-metrics-white.png" width="232" height="50" alt="Tor Metrics"></a>
+ <a class="navbar-brand visible-xs" href="/"><img src="/images/tor-metrics-white.png" width="232" height="50" alt="Tor Metrics"></a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
@@ -80,18 +80,18 @@ document.write('<div class="topButton" style="display:none;"><a href="#top"><i c
<li class="visible-xs section-header">Metrics</li>
<li class="visible-xs<c:if test="${'Home'.equals(param.navActive)}"> active</c:if>"><a href="/"><i class="fa fa-home fa-fw" aria-hidden="true"></i> Home</a></li>
<c:forEach var="category" items="${categories}">
- <li class="visible-xs<c:if test="${category[1].equals(param.navActive)}"> active</c:if><c:if test="${fn:length(category[0]) == 0}"> disabled</c:if>"><a<c:if test="${fn:length(category[0]) > 0}"> href="${category[0]}.html"</c:if>><i class="fa ${category[3]} fa-fw" aria-hidden="true"></i> ${category[1]}</a></li>
+ <li class="visible-xs<c:if test="${category[1].equals(param.navActive)}"> active</c:if><c:if test="${fn:length(category[0]) == 0}"> disabled</c:if>"><a<c:if test="${fn:length(category[0]) > 0}"> href="/${category[0]}.html"</c:if>><i class="fa ${category[3]} fa-fw" aria-hidden="true"></i> ${category[1]}</a></li>
</c:forEach>
<!-- /end of primary copy -->
<!-- secondary navigation items -->
<li class="visible-xs section-header">More</li>
- <li <c:if test="${'News'.equals(param.navActive)}"> class="active"</c:if>><a href="news.html"><i class="fa fa-newspaper-o fa-fw hidden-sm" aria-hidden="true"></i> News</a></li>
- <li <c:if test="${'Sources'.equals(param.navActive)}"> class="active"</c:if>><a href="sources.html"><i class="fa fa-archive fa-fw hidden-sm" aria-hidden="true"></i> Sources</a></li>
- <li <c:if test="${'Operation'.equals(param.navActive)}"> class="active"</c:if>><a href="operation.html"><i class="fa fa-cogs fa-fw hidden-sm" aria-hidden="true"></i> Operation</a></li>
- <li <c:if test="${'Development'.equals(param.navActive)}"> class="active"</c:if>><a href="development.html"><i class="fa fa-code fa-fw hidden-sm" aria-hidden="true"></i> Development</a></li>
- <li <c:if test="${'Research'.equals(param.navActive)}"> class="active"</c:if>><a href="research.html"><i class="fa fa-university fa-fw hidden-sm" aria-hidden="true"></i> Research</a></li>
- <li <c:if test="${'About'.equals(param.navActive)}"> class="active"</c:if>><a href="about.html"><i class="fa fa-lightbulb-o fa-fw hidden-sm" aria-hidden="true"></i> About</a></li>
+ <li <c:if test="${'News'.equals(param.navActive)}"> class="active"</c:if>><a href="/news.html"><i class="fa fa-newspaper-o fa-fw hidden-sm" aria-hidden="true"></i> News</a></li>
+ <li <c:if test="${'Sources'.equals(param.navActive)}"> class="active"</c:if>><a href="/sources.html"><i class="fa fa-archive fa-fw hidden-sm" aria-hidden="true"></i> Sources</a></li>
+ <li <c:if test="${'Operation'.equals(param.navActive)}"> class="active"</c:if>><a href="/operation.html"><i class="fa fa-cogs fa-fw hidden-sm" aria-hidden="true"></i> Operation</a></li>
+ <li <c:if test="${'Development'.equals(param.navActive)}"> class="active"</c:if>><a href="/development.html"><i class="fa fa-code fa-fw hidden-sm" aria-hidden="true"></i> Development</a></li>
+ <li <c:if test="${'Research'.equals(param.navActive)}"> class="active"</c:if>><a href="/research.html"><i class="fa fa-university fa-fw hidden-sm" aria-hidden="true"></i> Research</a></li>
+ <li <c:if test="${'About'.equals(param.navActive)}"> class="active"</c:if>><a href="/about.html"><i class="fa fa-lightbulb-o fa-fw hidden-sm" aria-hidden="true"></i> About</a></li>
<!-- /secondary navigation items -->
</ul>
@@ -102,7 +102,7 @@ document.write('<div class="topButton" style="display:none;"><a href="#top"><i c
<!-- page header for every single page -->
<div class="page-header hidden-xs">
- <a href="/"><img src="images/tor-metrics-white@xxxxxx" width="232" height="50" alt="Tor Metrics" id="metrics-wordmark"></a>
+ <a href="/"><img src="/images/tor-metrics-white@xxxxxx" width="232" height="50" alt="Tor Metrics" id="metrics-wordmark"></a>
<div>
<p>
<i>“Tor metrics are the ammunition that lets Tor and other security advocates argue for a more private and secure Internet from a position of data, rather than just dogma or perspective.”<br><small>— Bruce Schneier (June 1, 2016)</small></i>
@@ -119,7 +119,7 @@ document.write('<div class="topButton" style="display:none;"><a href="#top"><i c
<ul class="nav navbar-nav">
<li <c:if test="${'Home'.equals(param.navActive)}"> class="active"</c:if>><a href="/"><i class="fa fa-home fa-fw hidden-sm" aria-hidden="true"></i> Home</a></li>
<c:forEach var="category" items="${categories}">
- <li class="<c:if test="${category[1].equals(param.navActive)}"> active</c:if><c:if test="${fn:length(category[0]) == 0}"> disabled</c:if>"><a<c:if test="${fn:length(category[0]) > 0}"> href="${category[0]}.html"</c:if>><i class="fa ${category[3]} fa-fw hidden-sm" aria-hidden="true"></i> ${category[1]}</a></li>
+ <li class="<c:if test="${category[1].equals(param.navActive)}"> active</c:if><c:if test="${fn:length(category[0]) == 0}"> disabled</c:if>"><a<c:if test="${fn:length(category[0]) > 0}"> href="/${category[0]}.html"</c:if>><i class="fa ${category[3]} fa-fw hidden-sm" aria-hidden="true"></i> ${category[1]}</a></li>
</c:forEach>
</ul>
</div><!-- /.navbar-collapse -->
diff --git a/website/src/test/java/org/torproject/metrics/web/DirectoryListingTest.java b/website/src/test/java/org/torproject/metrics/web/DirectoryListingTest.java
new file mode 100644
index 0000000..5c6b37a
--- /dev/null
+++ b/website/src/test/java/org/torproject/metrics/web/DirectoryListingTest.java
@@ -0,0 +1,81 @@
+/* Copyright 2017 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.metrics.web;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.torproject.descriptor.index.IndexNode;
+
+import org.junit.Test;
+
+import java.io.ByteArrayInputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+public class DirectoryListingTest {
+
+ @Test
+ public void testFormatBytes() {
+ long[] input = new long[] { -1024L, -1L, 0L, 1L, 1023L, // B
+ 1024L, 1025L, 1048575L, // KiB
+ 1048576L, 1048577L, 1073741823L, // MiB
+ 1073741824L, 1073741825L, 1099511627775L, // GiB
+ 32099511627776L, 1099511627777L, 1125899906842623L, // TiB
+ 1125899906842624L, 1125899906842625L }; // PiB
+ String[] expectedOutput = new String[] { "-1024 B", "-1 B", "0 B", "1 B",
+ "1023 B", "1.0 KiB", "1.0 KiB", "1024.0 KiB", "1.0 MiB", "1.0 MiB",
+ "1024.0 MiB", "1.0 GiB", "1.0 GiB", "1024.0 GiB", "29.2 TiB", "1.0 TiB",
+ "1.0 PiB", // <- Would have expected 1024.0 TiB, but who cares?
+ "1.0 PiB", "1.0 PiB" };
+ assertEquals(expectedOutput.length, input.length);
+ for (int i = 0; i < input.length; i++) {
+ assertEquals("Mismatch for input " + input[i], expectedOutput[i],
+ DirectoryListing.formatBytes(input[i]));
+ }
+ }
+
+ private static final String jsonIndex
+ = "{\"index_created\":\"2016-02-02 00:02\","
+ + "\"path\":\"https://some.collector.url\","
+ + "\"directories\":[{\"path\":\"a1\","
+ + "\"directories\":[{\"path\":\"p1\","
+ + "\"files\":[{\"path\":\"file1\",\"size\":624156,"
+ + "\"last_modified\":\"2012-01-01 13:13\"},{\"path\":\"file2\","
+ + "\"size\":1010648,"
+ + "\"last_modified\":\"2012-02-02 14:14\"}]},{\"path\":\"p2\","
+ + "\"files\":[{\"path\":\"file3\",\"size\":624156,"
+ + "\"last_modified\":\"2012-03-03 15:15\"}]}]}]}";
+
+ @Test
+ public void testListing() throws Exception {
+ DirectoryListing dl = new DirectoryListing(IndexNode
+ .fetchIndex(new ByteArrayInputStream(jsonIndex.getBytes())));
+ assertEquals(4, dl.size());
+ for (String key : new String[]{"/collector/a1/", "/collector/",
+ "/collector/a1/p2/", "/collector/a1/p1/"}) {
+ assertTrue("Missing: " + key, dl.keySet().contains(key));
+ }
+ assertEquals("[Parent Directory, /collector.html, , ]",
+ Arrays.toString(dl.get("/collector/").get(0)));
+ assertEquals(3, dl.get("/collector/a1/").size());
+ assertEquals("[Parent Directory, /collector/, , ]",
+ Arrays.toString(dl.get("/collector/a1/").get(0)));
+ assertEquals("[p1, /collector/a1/p1/, , ]",
+ Arrays.toString(dl.get("/collector/a1/").get(1)));
+ assertEquals("[p2, /collector/a1/p2/, , ]",
+ Arrays.toString(dl.get("/collector/a1/").get(2)));
+ assertEquals("[Parent Directory, /collector/a1/, , ]",
+ Arrays.toString(dl.get("/collector/a1/p1/").get(0)));
+ assertEquals(2, dl.get("/collector/a1/p2/").size());
+ assertEquals("[file3, https://some.collector.url/a1/p2/file3, "
+ + "2012-03-03 15:15, 609.5 KiB]",
+ Arrays.toString(dl.get("/collector/a1/p2/").get(1)));
+ }
+}
+
_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits