[tor-commits] [tor/master] Add tests for directory_handle_command_get

nickm at torproject.org nickm at torproject.org
Fri Oct 2 13:13:30 UTC 2015


commit 4ff08bb5811ddfe554e597d129ec48a774364480
Author: Reinaldo de Souza Jr <juniorz at gmail.com>
Date:   Mon Sep 7 12:22:33 2015 -0500

    Add tests for directory_handle_command_get
---
 src/or/config.c                |    4 +-
 src/or/config.h                |    4 +-
 src/or/directory.c             |    6 +-
 src/or/directory.h             |    4 +
 src/or/dirvote.c               |    8 +-
 src/or/dirvote.h               |    6 +-
 src/or/geoip.c                 |    9 +-
 src/or/geoip.h                 |    1 +
 src/or/router.c                |    4 +-
 src/or/router.h                |    2 +-
 src/or/routerlist.c            |    4 +-
 src/or/routerlist.h            |    2 +-
 src/test/include.am            |    4 +-
 src/test/rend_test_helpers.c   |   66 ++
 src/test/rend_test_helpers.h   |   15 +
 src/test/test.c                |    2 +
 src/test/test_dir_handle_get.c | 2528 ++++++++++++++++++++++++++++++++++++++++
 src/test/vote_descriptors.inc  |   94 ++
 18 files changed, 2738 insertions(+), 25 deletions(-)

diff --git a/src/or/config.c b/src/or/config.c
index 98d9d83..4a28886 100644
--- a/src/or/config.c
+++ b/src/or/config.c
@@ -623,8 +623,8 @@ static char *global_dirfrontpagecontents = NULL;
 static smartlist_t *configured_ports = NULL;
 
 /** Return the contents of our frontpage string, or NULL if not configured. */
-const char *
-get_dirportfrontpage(void)
+MOCK_IMPL(const char*,
+get_dirportfrontpage, (void))
 {
   return global_dirfrontpagecontents;
 }
diff --git a/src/or/config.h b/src/or/config.h
index 0ee1e1a..51f7e90 100644
--- a/src/or/config.h
+++ b/src/or/config.h
@@ -14,8 +14,8 @@
 
 #include "testsupport.h"
 
-const char *get_dirportfrontpage(void);
-MOCK_DECL(const or_options_t *,get_options,(void));
+MOCK_DECL(const char*, get_dirportfrontpage, (void));
+MOCK_DECL(const or_options_t *, get_options, (void));
 or_options_t *get_options_mutable(void);
 int set_options(or_options_t *new_val, char **msg);
 void config_free_all(void);
diff --git a/src/or/directory.c b/src/or/directory.c
index 9461606..6f8edb5 100644
--- a/src/or/directory.c
+++ b/src/or/directory.c
@@ -2614,7 +2614,7 @@ choose_compression_level(ssize_t n_bytes)
  * service descriptor.  On finding one, write a response into
  * conn-\>outbuf.  If the request is unrecognized, send a 400.
  * Always return 0. */
-static int
+STATIC int
 directory_handle_command_get(dir_connection_t *conn, const char *headers,
                              const char *req_body, size_t req_body_len)
 {
@@ -2874,7 +2874,7 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers,
       });
 
     if (global_write_bucket_low(TO_CONN(conn), estimated_len, 2)) {
-      write_http_status_line(conn, 503, "Directory busy, try again later.");
+      write_http_status_line(conn, 503, "Directory busy, try again later");
       goto vote_done;
     }
     write_http_response_header(conn, body_len ? body_len : -1, compressed,
@@ -3071,7 +3071,7 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers,
                       len += c->cache_info.signed_descriptor_len);
 
     if (global_write_bucket_low(TO_CONN(conn), compressed?len/2:len, 2)) {
-      write_http_status_line(conn, 503, "Directory busy, try again later.");
+      write_http_status_line(conn, 503, "Directory busy, try again later");
       goto keys_done;
     }
 
diff --git a/src/or/directory.h b/src/or/directory.h
index 4899eb5..427183c 100644
--- a/src/or/directory.h
+++ b/src/or/directory.h
@@ -127,6 +127,10 @@ STATIC int purpose_needs_anonymity(uint8_t dir_purpose,
                                    uint8_t router_purpose);
 STATIC dirinfo_type_t dir_fetch_type(int dir_purpose, int router_purpose,
                                      const char *resource);
+STATIC int directory_handle_command_get(dir_connection_t *conn,
+                                        const char *headers,
+                                        const char *req_body,
+                                        size_t req_body_len);
 #endif
 
 #endif
diff --git a/src/or/dirvote.c b/src/or/dirvote.c
index d8e6ee2..0449e9d 100644
--- a/src/or/dirvote.c
+++ b/src/or/dirvote.c
@@ -3373,8 +3373,8 @@ dirvote_free_all(void)
  * ==== */
 
 /** Return the body of the consensus that we're currently trying to build. */
-const char *
-dirvote_get_pending_consensus(consensus_flavor_t flav)
+MOCK_IMPL(const char *,
+dirvote_get_pending_consensus, (consensus_flavor_t flav))
 {
   tor_assert(((int)flav) >= 0 && (int)flav < N_CONSENSUS_FLAVORS);
   return pending_consensuses[flav].body;
@@ -3382,8 +3382,8 @@ dirvote_get_pending_consensus(consensus_flavor_t flav)
 
 /** Return the signatures that we know for the consensus that we're currently
  * trying to build. */
-const char *
-dirvote_get_pending_detached_signatures(void)
+MOCK_IMPL(const char *,
+dirvote_get_pending_detached_signatures, (void))
 {
   return pending_consensus_signatures;
 }
diff --git a/src/or/dirvote.h b/src/or/dirvote.h
index dca8540..966d163 100644
--- a/src/or/dirvote.h
+++ b/src/or/dirvote.h
@@ -136,8 +136,10 @@ int dirvote_add_signatures(const char *detached_signatures_body,
                            const char **msg_out);
 
 /* Item access */
-const char *dirvote_get_pending_consensus(consensus_flavor_t flav);
-const char *dirvote_get_pending_detached_signatures(void);
+MOCK_DECL(const char*, dirvote_get_pending_consensus,
+          (consensus_flavor_t flav));
+MOCK_DECL(const char*, dirvote_get_pending_detached_signatures, (void));
+
 #define DGV_BY_ID 1
 #define DGV_INCLUDE_PENDING 2
 #define DGV_INCLUDE_PREVIOUS 4
diff --git a/src/or/geoip.c b/src/or/geoip.c
index 120ce47..e04c248 100644
--- a/src/or/geoip.c
+++ b/src/or/geoip.c
@@ -18,7 +18,6 @@
 #include "geoip.h"
 #include "routerlist.h"
 
-static void clear_geoip_db(void);
 static void init_geoip_countries(void);
 
 /** An entry from the GeoIP IPv4 file: maps an IPv4 range to a country. */
@@ -1207,9 +1206,9 @@ geoip_format_dirreq_stats(time_t now)
 {
   char t[ISO_TIME_LEN+1];
   int i;
-  char *v3_ips_string, *v3_reqs_string, *v3_direct_dl_string,
-       *v3_tunneled_dl_string;
-  char *result;
+  char *v3_ips_string = NULL, *v3_reqs_string = NULL,
+       *v3_direct_dl_string = NULL, *v3_tunneled_dl_string = NULL;
+  char *result = NULL;
 
   if (!start_of_dirreq_stats_interval)
     return NULL; /* Not initialized. */
@@ -1666,7 +1665,7 @@ getinfo_helper_geoip(control_connection_t *control_conn,
 }
 
 /** Release all storage held by the GeoIP databases and country list. */
-static void
+STATIC void
 clear_geoip_db(void)
 {
   if (geoip_countries) {
diff --git a/src/or/geoip.h b/src/or/geoip.h
index 8a3486c..3f1bba0 100644
--- a/src/or/geoip.h
+++ b/src/or/geoip.h
@@ -18,6 +18,7 @@
 STATIC int geoip_parse_entry(const char *line, sa_family_t family);
 STATIC int geoip_get_country_by_ipv4(uint32_t ipaddr);
 STATIC int geoip_get_country_by_ipv6(const struct in6_addr *addr);
+STATIC void clear_geoip_db(void);
 #endif
 int should_record_bridge_info(const or_options_t *options);
 int geoip_load_file(sa_family_t family, const char *filename);
diff --git a/src/or/router.c b/src/or/router.c
index 03973ae..d96e573 100644
--- a/src/or/router.c
+++ b/src/or/router.c
@@ -269,8 +269,8 @@ client_identity_key_is_set(void)
 
 /** Return the key certificate for this v3 (voting) authority, or NULL
  * if we have no such certificate. */
-authority_cert_t *
-get_my_v3_authority_cert(void)
+MOCK_IMPL(authority_cert_t *,
+get_my_v3_authority_cert, (void))
 {
   return authority_key_certificate;
 }
diff --git a/src/or/router.h b/src/or/router.h
index d8fcf0a..2e8c025 100644
--- a/src/or/router.h
+++ b/src/or/router.h
@@ -22,7 +22,7 @@ int server_identity_key_is_set(void);
 void set_client_identity_key(crypto_pk_t *k);
 crypto_pk_t *get_tlsclient_identity_key(void);
 int client_identity_key_is_set(void);
-authority_cert_t *get_my_v3_authority_cert(void);
+MOCK_DECL(authority_cert_t *, get_my_v3_authority_cert, (void));
 crypto_pk_t *get_my_v3_authority_signing_key(void);
 authority_cert_t *get_my_v3_legacy_cert(void);
 crypto_pk_t *get_my_v3_legacy_signing_key(void);
diff --git a/src/or/routerlist.c b/src/or/routerlist.c
index 8bd8039..9c90540 100644
--- a/src/or/routerlist.c
+++ b/src/or/routerlist.c
@@ -5184,8 +5184,8 @@ hid_serv_acting_as_directory(void)
 
 /** Return true if this node is responsible for storing the descriptor ID
  * in <b>query</b> and false otherwise. */
-int
-hid_serv_responsible_for_desc_id(const char *query)
+MOCK_IMPL(int, hid_serv_responsible_for_desc_id,
+          (const char *query))
 {
   const routerinfo_t *me;
   routerstatus_t *last_rs;
diff --git a/src/or/routerlist.h b/src/or/routerlist.h
index 200533f..100ab58 100644
--- a/src/or/routerlist.h
+++ b/src/or/routerlist.h
@@ -201,7 +201,7 @@ void refresh_all_country_info(void);
 int hid_serv_get_responsible_directories(smartlist_t *responsible_dirs,
                                          const char *id);
 int hid_serv_acting_as_directory(void);
-int hid_serv_responsible_for_desc_id(const char *id);
+MOCK_DECL(int, hid_serv_responsible_for_desc_id, (const char *id));
 
 void list_pending_microdesc_downloads(digest256map_t *result);
 void launch_descriptor_downloads(int purpose,
diff --git a/src/test/include.am b/src/test/include.am
index f7c0204..1a55394 100644
--- a/src/test/include.am
+++ b/src/test/include.am
@@ -70,6 +70,7 @@ src_test_test_SOURCES = \
 	src/test/test_crypto.c \
 	src/test/test_data.c \
 	src/test/test_dir.c \
+	src/test/test_dir_handle_get.c \
 	src/test/test_entryconn.c \
 	src/test/test_entrynodes.c \
 	src/test/test_guardfraction.c \
@@ -97,7 +98,8 @@ src_test_test_SOURCES = \
 	src/test/test_threads.c \
 	src/test/test_util.c \
 	src/test/test_helpers.c \
-        src/test/test_dns.c \
+	src/test/rend_test_helpers.c \
+	src/test/test_dns.c \
 	src/test/testing_common.c \
 	src/ext/tinytest.c
 
diff --git a/src/test/rend_test_helpers.c b/src/test/rend_test_helpers.c
new file mode 100644
index 0000000..0a1b1c8
--- /dev/null
+++ b/src/test/rend_test_helpers.c
@@ -0,0 +1,66 @@
+#include "rend_test_helpers.h"
+
+#include "test.h"
+#include "rendcommon.h"
+
+void
+generate_desc(int time_diff, rend_encoded_v2_service_descriptor_t **desc,
+              char **service_id, int intro_points)
+{
+  rend_service_descriptor_t *generated = NULL;
+  smartlist_t *descs = smartlist_new();
+  time_t now;
+
+  now = time(NULL) + time_diff;
+  create_descriptor(&generated, service_id, intro_points);
+  generated->timestamp = now;
+
+  rend_encode_v2_descriptors(descs, generated, now, 0, REND_NO_AUTH, NULL,
+                             NULL);
+  *desc = ((rend_encoded_v2_service_descriptor_t *)smartlist_get(descs, 0));
+
+  smartlist_free(descs);
+  rend_service_descriptor_free(generated);
+}
+
+void
+create_descriptor(rend_service_descriptor_t **generated, char **service_id,
+                  int intro_points)
+{
+  crypto_pk_t *pk1 = NULL;
+  crypto_pk_t *pk2 = NULL;
+  int i;
+
+  *service_id = tor_malloc(REND_SERVICE_ID_LEN_BASE32+1);
+  pk1 = pk_generate(0);
+  pk2 = pk_generate(1);
+
+  *generated = tor_malloc_zero(sizeof(rend_service_descriptor_t));
+  (*generated)->pk = crypto_pk_dup_key(pk1);
+  rend_get_service_id((*generated)->pk, *service_id);
+
+  (*generated)->version = 2;
+  (*generated)->protocols = 42;
+  (*generated)->intro_nodes = smartlist_new();
+
+  for (i = 0; i < intro_points; i++) {
+    rend_intro_point_t *intro = tor_malloc_zero(sizeof(rend_intro_point_t));
+    crypto_pk_t *okey = pk_generate(2 + i);
+    intro->extend_info = tor_malloc_zero(sizeof(extend_info_t));
+    intro->extend_info->onion_key = okey;
+    crypto_pk_get_digest(intro->extend_info->onion_key,
+                         intro->extend_info->identity_digest);
+    intro->extend_info->nickname[0] = '$';
+    base16_encode(intro->extend_info->nickname + 1,
+                  sizeof(intro->extend_info->nickname) - 1,
+                  intro->extend_info->identity_digest, DIGEST_LEN);
+    tor_addr_from_ipv4h(&intro->extend_info->addr, crypto_rand_int(65536));
+    intro->extend_info->port = 1 + crypto_rand_int(65535);
+    intro->intro_key = crypto_pk_dup_key(pk2);
+    smartlist_add((*generated)->intro_nodes, intro);
+  }
+
+  crypto_pk_free(pk1);
+  crypto_pk_free(pk2);
+}
+
diff --git a/src/test/rend_test_helpers.h b/src/test/rend_test_helpers.h
new file mode 100644
index 0000000..1ef0374
--- /dev/null
+++ b/src/test/rend_test_helpers.h
@@ -0,0 +1,15 @@
+/* Copyright (c) 2014-2015, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "or.h"
+
+#ifndef TOR_REND_TEST_HELPERS_H
+#define TOR_REND_TEST_HELPERS_H
+
+void generate_desc(int time_diff, rend_encoded_v2_service_descriptor_t **desc,
+                   char **service_id, int intro_points);
+void create_descriptor(rend_service_descriptor_t **generated,
+                       char **service_id, int intro_points);
+
+#endif
+
diff --git a/src/test/test.c b/src/test/test.c
index e10e260..2c4eedd 100644
--- a/src/test/test.c
+++ b/src/test/test.c
@@ -1131,6 +1131,7 @@ extern struct testcase_t controller_tests[];
 extern struct testcase_t controller_event_tests[];
 extern struct testcase_t crypto_tests[];
 extern struct testcase_t dir_tests[];
+extern struct testcase_t dir_handle_get_tests[];
 extern struct testcase_t entryconn_tests[];
 extern struct testcase_t entrynodes_tests[];
 extern struct testcase_t guardfraction_tests[];
@@ -1179,6 +1180,7 @@ struct testgroup_t testgroups[] = {
   { "control/event/", controller_event_tests },
   { "crypto/", crypto_tests },
   { "dir/", dir_tests },
+  { "dir_handle_get/", dir_handle_get_tests },
   { "dir/md/", microdesc_tests },
   { "entryconn/", entryconn_tests },
   { "entrynodes/", entrynodes_tests },
diff --git a/src/test/test_dir_handle_get.c b/src/test/test_dir_handle_get.c
new file mode 100644
index 0000000..67b4149
--- /dev/null
+++ b/src/test/test_dir_handle_get.c
@@ -0,0 +1,2528 @@
+/* Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2015, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#define RENDCOMMON_PRIVATE
+#define GEOIP_PRIVATE
+
+#include "or.h"
+#include "config.h"
+#include "directory.h"
+#include "test.h"
+#include "connection.h"
+#include "rendcommon.h"
+#include "rendcache.h"
+#include "router.h"
+#include "routerlist.h"
+#include "rend_test_helpers.h"
+#include "microdesc.h"
+#include "test_helpers.h"
+#include "nodelist.h"
+#include "entrynodes.h"
+#include "routerparse.h"
+#include "networkstatus.h"
+#include "geoip.h"
+#include "dirserv.h"
+#include "torgzip.h"
+#include "dirvote.h"
+
+#ifdef _WIN32
+/* For mkdir() */
+#include <direct.h>
+#else
+#include <dirent.h>
+#endif
+
+#include "vote_descriptors.inc"
+
+#define NS_MODULE dir_handle_get
+
+static void
+connection_write_to_buf_mock(const char *string, size_t len,
+                             connection_t *conn, int zlib)
+{
+  (void) zlib;
+
+  tor_assert(string);
+  tor_assert(conn);
+
+  write_to_buf(string, len, conn->outbuf);
+}
+
+#define GET(path) "GET " path " HTTP/1.0\r\n\r\n"
+#define NOT_FOUND "HTTP/1.0 404 Not found\r\n\r\n"
+#define BAD_REQUEST "HTTP/1.0 400 Bad request\r\n\r\n"
+#define SERVER_BUSY "HTTP/1.0 503 Directory busy, try again later\r\n\r\n"
+#define NOT_ENOUGH_CONSENSUS_SIGNATURES "HTTP/1.0 404 " \
+  "Consensus not signed by sufficient number of requested authorities\r\n\r\n"
+
+static tor_addr_t MOCK_TOR_ADDR;
+
+static void
+test_dir_handle_get_bad_request(void *data)
+{
+  dir_connection_t *conn = NULL;
+  char *header = NULL;
+  (void) data;
+
+  MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+  conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+  tt_int_op(directory_handle_command_get(conn, "", NULL, 0), OP_EQ, 0);
+
+  fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+                      NULL, NULL, 1, 0);
+
+  tt_str_op(header, OP_EQ, BAD_REQUEST);
+
+  done:
+    UNMOCK(connection_write_to_buf_impl_);
+    tor_free(conn);
+    tor_free(header);
+}
+
+static void
+test_dir_handle_get_v1_command_not_found(void *data)
+{
+  dir_connection_t *conn = NULL;
+  char *header = NULL;
+  (void) data;
+
+  MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+  conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+  // no frontpage configured
+  tt_ptr_op(get_dirportfrontpage(), OP_EQ, NULL);
+
+  /* V1 path */
+  tt_int_op(directory_handle_command_get(conn, GET("/tor/"), NULL, 0),
+            OP_EQ, 0);
+
+  fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+                      NULL, NULL, 1, 0);
+
+  tt_str_op(NOT_FOUND, OP_EQ, header);
+
+  done:
+    UNMOCK(connection_write_to_buf_impl_);
+    tor_free(conn);
+    tor_free(header);
+}
+
+static const char*
+mock_get_dirportfrontpage(void)
+{
+  return "HELLO FROM FRONTPAGE";
+}
+
+static void
+test_dir_handle_get_v1_command(void *data)
+{
+  dir_connection_t *conn = NULL;
+  char *header = NULL;
+  char *body = NULL;
+  size_t body_used = 0, body_len = 0;
+  const char *exp_body = NULL;
+  (void) data;
+
+  MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+  MOCK(get_dirportfrontpage, mock_get_dirportfrontpage);
+
+  exp_body = get_dirportfrontpage();
+  body_len = strlen(exp_body);
+
+  conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+  tt_int_op(directory_handle_command_get(conn, GET("/tor/"), NULL, 0),
+            OP_EQ, 0);
+
+  fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+                      &body, &body_used, body_len+1, 0);
+
+  tt_assert(header);
+  tt_assert(body);
+
+  tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+  tt_assert(strstr(header, "Content-Type: text/html\r\n"));
+  tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+  tt_assert(strstr(header, "Content-Length: 20\r\n"));
+
+  tt_int_op(body_used, OP_EQ, strlen(body));
+  tt_str_op(body, OP_EQ, exp_body);
+
+  done:
+    UNMOCK(connection_write_to_buf_impl_);
+    UNMOCK(get_dirportfrontpage);
+    tor_free(conn);
+    tor_free(header);
+    tor_free(body);
+}
+
+static void
+test_dir_handle_get_not_found(void *data)
+{
+  dir_connection_t *conn = NULL;
+  char *header = NULL;
+  (void) data;
+
+  MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+  conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+  /* Unrecognized path */
+  tt_int_op(directory_handle_command_get(conn, GET("/anything"), NULL, 0),
+            OP_EQ, 0);
+  fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+                      NULL, NULL, 1, 0);
+
+  tt_str_op(NOT_FOUND, OP_EQ, header);
+
+  done:
+    UNMOCK(connection_write_to_buf_impl_);
+    tor_free(conn);
+    tor_free(header);
+}
+
+static void
+test_dir_handle_get_robots_txt(void *data)
+{
+  dir_connection_t *conn = NULL;
+  char *header = NULL;
+  char *body = NULL;
+  size_t body_used = 0;
+  (void) data;
+
+  MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+  conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+  tt_int_op(directory_handle_command_get(conn, GET("/tor/robots.txt"),
+                                         NULL, 0), OP_EQ, 0);
+  fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+                      &body, &body_used, 29, 0);
+
+  tt_assert(header);
+  tt_assert(body);
+
+  tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+  tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+  tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+  tt_assert(strstr(header, "Content-Length: 28\r\n"));
+
+  tt_int_op(body_used, OP_EQ, strlen(body));
+  tt_str_op(body, OP_EQ, "User-agent: *\r\nDisallow: /\r\n");
+
+  done:
+    UNMOCK(connection_write_to_buf_impl_);
+    tor_free(conn);
+    tor_free(header);
+    tor_free(body);
+}
+
+static void
+test_dir_handle_get_bytes_txt(void *data)
+{
+  dir_connection_t *conn = NULL;
+  char *header = NULL;
+  char *body = NULL;
+  size_t body_used = 0, body_len = 0;
+  char buff[30];
+  char *exp_body = NULL;
+  (void) data;
+
+  exp_body = directory_dump_request_log();
+  body_len = strlen(exp_body);
+
+  MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+  conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+  tt_int_op(directory_handle_command_get(conn, GET("/tor/bytes.txt"), NULL, 0),
+            OP_EQ, 0);
+  fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+                      &body, &body_used, body_len+1, 0);
+
+  tt_assert(header);
+  tt_assert(body);
+
+  tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+  tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+  tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+  tt_assert(strstr(header, "Pragma: no-cache\r\n"));
+
+  sprintf(buff, "Content-Length: %ld\r\n", body_len);
+  tt_assert(strstr(header, buff));
+
+  tt_int_op(body_used, OP_EQ, strlen(body));
+  tt_str_op(body, OP_EQ, exp_body);
+
+  done:
+    UNMOCK(connection_write_to_buf_impl_);
+    tor_free(conn);
+    tor_free(header);
+    tor_free(body);
+}
+
+#define RENDEZVOUS2_GET(descid) GET("/tor/rendezvous2/" descid)
+static void
+test_dir_handle_get_rendezvous2_not_found_if_not_encrypted(void *data)
+{
+  dir_connection_t *conn = NULL;
+  char *header = NULL;
+  (void) data;
+
+  MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+  conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+  // connection is not encrypted
+  tt_assert(!connection_dir_is_encrypted(conn))
+
+  tt_int_op(directory_handle_command_get(conn, RENDEZVOUS2_GET(), NULL, 0),
+            OP_EQ, 0);
+  fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+                      NULL, NULL, 1, 0);
+
+  tt_str_op(NOT_FOUND, OP_EQ, header);
+
+  done:
+    UNMOCK(connection_write_to_buf_impl_);
+    tor_free(conn);
+    tor_free(header);
+}
+
+static void
+test_dir_handle_get_rendezvous2_on_encrypted_conn_with_invalid_desc_id(
+  void *data)
+{
+  dir_connection_t *conn = NULL;
+  char *header = NULL;
+  (void) data;
+
+  MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+  conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+  // connection is encrypted
+  TO_CONN(conn)->linked = 1;
+  tt_assert(connection_dir_is_encrypted(conn));
+
+  tt_int_op(directory_handle_command_get(conn,
+            RENDEZVOUS2_GET("invalid-desc-id"), NULL, 0), OP_EQ, 0);
+  fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+                      NULL, NULL, 1, 0);
+
+  tt_str_op(header, OP_EQ, BAD_REQUEST);
+
+  done:
+    UNMOCK(connection_write_to_buf_impl_);
+    tor_free(conn);
+    tor_free(header);
+}
+
+static void
+test_dir_handle_get_rendezvous2_on_encrypted_conn_not_well_formed(void *data)
+{
+  dir_connection_t *conn = NULL;
+  char *header = NULL;
+  (void) data;
+
+  MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+  conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+  // connection is encrypted
+  TO_CONN(conn)->linked = 1;
+  tt_assert(connection_dir_is_encrypted(conn));
+
+  //TODO: this cant be reached because rend_valid_descriptor_id() prevents this
+  //case to happen. This test is the same as
+  //test_dir_handle_get_rendezvous2_on_encrypted_conn_with_invalid_desc_id
+  //We should refactor to remove the case from the switch.
+
+  const char *req = RENDEZVOUS2_GET("1bababababababababababababababab");
+  tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+  fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+                      NULL, NULL, 1, 0);
+
+  tt_str_op(header, OP_EQ, BAD_REQUEST);
+
+  done:
+    UNMOCK(connection_write_to_buf_impl_);
+    tor_free(conn);
+    tor_free(header);
+}
+
+static void
+test_dir_handle_get_rendezvous2_not_found(void *data)
+{
+  dir_connection_t *conn = NULL;
+  char *header = NULL;
+  (void) data;
+
+  MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+  conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+  rend_cache_init();
+
+  // connection is encrypted
+  TO_CONN(conn)->linked = 1;
+  tt_assert(connection_dir_is_encrypted(conn));
+
+  const char *req = RENDEZVOUS2_GET("3xqunszqnaolrrfmtzgaki7mxelgvkje");
+  tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+  fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+                      NULL, NULL, 1, 0);
+
+  tt_str_op(NOT_FOUND, OP_EQ, header);
+
+  done:
+    UNMOCK(connection_write_to_buf_impl_);
+    tor_free(conn);
+    tor_free(header);
+    rend_cache_free_all();
+}
+
+NS_DECL(const routerinfo_t *, router_get_my_routerinfo, (void));
+NS_DECL(int, hid_serv_responsible_for_desc_id, (const char *id));
+
+static routerinfo_t *mock_routerinfo;
+static int hid_serv_responsible_for_desc_id_response;
+
+static const routerinfo_t *
+NS(router_get_my_routerinfo)(void)
+{
+  if (!mock_routerinfo) {
+    mock_routerinfo = tor_malloc(sizeof(routerinfo_t));
+  }
+
+  return mock_routerinfo;
+}
+
+static int
+NS(hid_serv_responsible_for_desc_id)(const char *id)
+{
+  return hid_serv_responsible_for_desc_id_response;
+}
+
+static void
+test_dir_handle_get_rendezvous2_on_encrypted_conn_success(void *data)
+{
+  dir_connection_t *conn = NULL;
+  char *header = NULL;
+  char *body = NULL;
+  size_t body_used = 0;
+  char buff[30];
+  char req[70];
+  rend_encoded_v2_service_descriptor_t *desc_holder = NULL;
+  char *service_id = NULL;
+  char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
+  size_t body_len = 0;
+  (void) data;
+
+  MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+  NS_MOCK(router_get_my_routerinfo);
+  NS_MOCK(hid_serv_responsible_for_desc_id);
+
+  rend_cache_init();
+  hid_serv_responsible_for_desc_id_response = 1;
+
+  /* create a valid rend service descriptor */
+  #define RECENT_TIME -10
+  generate_desc(RECENT_TIME, &desc_holder, &service_id, 3);
+
+  tt_int_op(rend_cache_store_v2_desc_as_dir(desc_holder->desc_str),
+            OP_EQ, RCS_OKAY);
+
+  base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_holder->desc_id,
+                DIGEST_LEN);
+
+  conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+  // connection is encrypted
+  TO_CONN(conn)->linked = 1;
+  tt_assert(connection_dir_is_encrypted(conn));
+
+  sprintf(req, RENDEZVOUS2_GET("%s"), desc_id_base32);
+
+  tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+  body_len = strlen(desc_holder->desc_str);
+  fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+                      &body, &body_used, body_len+1, 0);
+
+  tt_assert(header);
+  tt_assert(body);
+
+  tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+  tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+  tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+  tt_assert(strstr(header, "Pragma: no-cache\r\n"));
+  sprintf(buff, "Content-Length: %ld\r\n", body_len);
+  tt_assert(strstr(header, buff));
+
+  tt_int_op(body_used, OP_EQ, strlen(body));
+  tt_str_op(body, OP_EQ, desc_holder->desc_str);
+
+  done:
+    UNMOCK(connection_write_to_buf_impl_);
+    NS_UNMOCK(router_get_my_routerinfo);
+    NS_UNMOCK(hid_serv_responsible_for_desc_id);
+
+    tor_free(conn);
+    tor_free(header);
+    tor_free(body);
+    rend_cache_free_all();
+}
+
+#define MICRODESC_GET(digest) GET("/tor/micro/d/" digest)
+static void
+test_dir_handle_get_micro_d_not_found(void *data)
+{
+  dir_connection_t *conn = NULL;
+  char *header = NULL;
+  (void) data;
+
+  MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+  #define B64_256_1 "8/Pz8/u7vz8/Pz+7vz8/Pz+7u/Pz8/P7u/Pz8/P7u78"
+  #define B64_256_2 "zMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMw"
+  conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+  const char *req = MICRODESC_GET(B64_256_1 "-" B64_256_2);
+  tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+  fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+                      NULL, NULL, 1, 0);
+
+  tt_str_op(NOT_FOUND, OP_EQ, header);
+
+  done:
+    UNMOCK(connection_write_to_buf_impl_);
+
+    tor_free(conn);
+    tor_free(header);
+}
+
+static or_options_t *mock_options = NULL;
+static void
+init_mock_options(void)
+{
+  mock_options = malloc(sizeof(or_options_t));
+  memset(mock_options, 0, sizeof(or_options_t));
+  mock_options->TestingTorNetwork = 1;
+}
+
+static const or_options_t *
+mock_get_options(void)
+{
+  tor_assert(mock_options);
+  return mock_options;
+}
+
+static const char microdesc[] =
+  "onion-key\n"
+  "-----BEGIN RSA PUBLIC KEY-----\n"
+  "MIGJAoGBAMjlHH/daN43cSVRaHBwgUfnszzAhg98EvivJ9Qxfv51mvQUxPjQ07es\n"
+  "gV/3n8fyh3Kqr/ehi9jxkdgSRfSnmF7giaHL1SLZ29kA7KtST+pBvmTpDtHa3ykX\n"
+  "Xorc7hJvIyTZoc1HU+5XSynj3gsBE5IGK1ZRzrNS688LnuZMVp1tAgMBAAE=\n"
+  "-----END RSA PUBLIC KEY-----\n";
+
+static void
+test_dir_handle_get_micro_d(void *data)
+{
+  dir_connection_t *conn = NULL;
+  microdesc_cache_t *mc = NULL ;
+  smartlist_t *list = NULL;
+  char digest[DIGEST256_LEN];
+  char digest_base64[128];
+  char path[80];
+  char *header = NULL;
+  char *body = NULL;
+  size_t body_used = 0;
+  (void) data;
+
+  MOCK(get_options, mock_get_options);
+  MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+  /* SETUP */
+  init_mock_options();
+  const char *fn = get_fname("dir_handle_datadir_test1");
+  mock_options->DataDirectory = tor_strdup(fn);
+
+#ifdef _WIN32
+  tt_int_op(0, OP_EQ, mkdir(mock_options->DataDirectory));
+#else
+  tt_int_op(0, OP_EQ, mkdir(mock_options->DataDirectory, 0700));
+#endif
+
+  /* Add microdesc to cache */
+  crypto_digest256(digest, microdesc, strlen(microdesc), DIGEST_SHA256);
+  base64_encode_nopad(digest_base64, sizeof(digest_base64),
+                      (uint8_t *) digest, DIGEST256_LEN);
+
+  mc = get_microdesc_cache();
+  list = microdescs_add_to_cache(mc, microdesc, NULL, SAVED_NOWHERE, 0,
+                                  time(NULL), NULL);
+  tt_int_op(1, OP_EQ, smartlist_len(list));
+
+  /* Make the request */
+  conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+  sprintf(path, MICRODESC_GET("%s"), digest_base64);
+  tt_int_op(directory_handle_command_get(conn, path, NULL, 0), OP_EQ, 0);
+
+  fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+                      &body, &body_used, strlen(microdesc)+1, 0);
+
+  tt_assert(header);
+  tt_assert(body);
+
+  tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+  tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+  tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+
+  tt_int_op(body_used, OP_EQ, strlen(body));
+  tt_str_op(body, OP_EQ, microdesc);
+
+  done:
+    UNMOCK(get_options);
+    UNMOCK(connection_write_to_buf_impl_);
+
+    if (mock_options)
+      tor_free(mock_options->DataDirectory);
+    tor_free(conn);
+    tor_free(header);
+    tor_free(body);
+    smartlist_free(list);
+    microdesc_free_all();
+}
+
+static void
+test_dir_handle_get_micro_d_server_busy(void *data)
+{
+  dir_connection_t *conn = NULL;
+  microdesc_cache_t *mc = NULL ;
+  smartlist_t *list = NULL;
+  char digest[DIGEST256_LEN];
+  char digest_base64[128];
+  char path[80];
+  char *header = NULL;
+  (void) data;
+
+  MOCK(get_options, mock_get_options);
+  MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+  /* SETUP */
+  init_mock_options();
+  const char *fn = get_fname("dir_handle_datadir_test2");
+  mock_options->DataDirectory = tor_strdup(fn);
+
+#ifdef _WIN32
+  tt_int_op(0, OP_EQ, mkdir(mock_options->DataDirectory));
+#else
+  tt_int_op(0, OP_EQ, mkdir(mock_options->DataDirectory, 0700));
+#endif
+
+  /* Add microdesc to cache */
+  crypto_digest256(digest, microdesc, strlen(microdesc), DIGEST_SHA256);
+  base64_encode_nopad(digest_base64, sizeof(digest_base64),
+                      (uint8_t *) digest, DIGEST256_LEN);
+
+  mc = get_microdesc_cache();
+  list = microdescs_add_to_cache(mc, microdesc, NULL, SAVED_NOWHERE, 0,
+                                  time(NULL), NULL);
+  tt_int_op(1, OP_EQ, smartlist_len(list));
+
+  //Make it busy
+  mock_options->CountPrivateBandwidth = 1;
+
+  /* Make the request */
+  conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+  sprintf(path, MICRODESC_GET("%s"), digest_base64);
+  tt_int_op(directory_handle_command_get(conn, path, NULL, 0), OP_EQ, 0);
+
+  fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+                      NULL, NULL, 1, 0);
+
+  tt_str_op(SERVER_BUSY, OP_EQ, header);
+
+  done:
+    UNMOCK(get_options);
+    UNMOCK(connection_write_to_buf_impl_);
+
+    if (mock_options)
+      tor_free(mock_options->DataDirectory);
+
+    tor_free(conn);
+    tor_free(header);
+    smartlist_free(list);
+    microdesc_free_all();
+}
+
+#define BRIDGES_PATH "/tor/networkstatus-bridges"
+static void
+test_dir_handle_get_networkstatus_bridges_not_found_without_auth(void *data)
+{
+  dir_connection_t *conn = NULL;
+  char *header = NULL;
+  (void) data;
+
+  MOCK(get_options, mock_get_options);
+  MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+  /* SETUP */
+  init_mock_options();
+  mock_options->BridgeAuthoritativeDir = 1;
+  mock_options->BridgePassword_AuthDigest_ = "digest";
+
+  conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+  TO_CONN(conn)->linked = 1;
+
+  const char *req = GET(BRIDGES_PATH);
+  tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+  fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+                      NULL, NULL, 1, 0);
+
+  tt_str_op(NOT_FOUND, OP_EQ, header);
+
+  done:
+    UNMOCK(get_options);
+    UNMOCK(connection_write_to_buf_impl_);
+    tor_free(mock_options);
+    tor_free(conn);
+    tor_free(header);
+}
+
+static void
+test_dir_handle_get_networkstatus_bridges(void *data)
+{
+  dir_connection_t *conn = NULL;
+  char *header = NULL;
+  (void) data;
+
+  MOCK(get_options, mock_get_options);
+  MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+  /* SETUP */
+  init_mock_options();
+  mock_options->BridgeAuthoritativeDir = 1;
+  mock_options->BridgePassword_AuthDigest_ = tor_malloc(DIGEST256_LEN);
+  crypto_digest256(mock_options->BridgePassword_AuthDigest_,
+                     "abcdefghijklm12345", 18, DIGEST_SHA256);
+
+  conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+  TO_CONN(conn)->linked = 1;
+
+  const char *req = "GET " BRIDGES_PATH " HTTP/1.0\r\n"
+                    "Authorization: Basic abcdefghijklm12345\r\n\r\n";
+  tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+  fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+                      NULL, NULL, 1, 0);
+
+  tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+  tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+  tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+  tt_assert(strstr(header, "Content-Length: 0\r\n"));
+
+  done:
+    UNMOCK(get_options);
+    UNMOCK(connection_write_to_buf_impl_);
+    tor_free(mock_options);
+    tor_free(conn);
+    tor_free(header);
+}
+
+static void
+test_dir_handle_get_networkstatus_bridges_not_found_wrong_auth(void *data)
+{
+  dir_connection_t *conn = NULL;
+  char *header = NULL;
+  (void) data;
+
+  MOCK(get_options, mock_get_options);
+  MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+  /* SETUP */
+  init_mock_options();
+  mock_options->BridgeAuthoritativeDir = 1;
+  mock_options->BridgePassword_AuthDigest_ = tor_malloc(DIGEST256_LEN);
+  crypto_digest256(mock_options->BridgePassword_AuthDigest_,
+                     "abcdefghijklm12345", 18, DIGEST_SHA256);
+
+  conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+  TO_CONN(conn)->linked = 1;
+
+  const char *req = "GET " BRIDGES_PATH " HTTP/1.0\r\n"
+                           "Authorization: Basic NOTSAMEDIGEST\r\n\r\n";
+  tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+  fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+                      NULL, NULL, 1, 0);
+
+  tt_str_op(NOT_FOUND, OP_EQ, header);
+
+  done:
+    UNMOCK(get_options);
+    UNMOCK(connection_write_to_buf_impl_);
+    tor_free(mock_options);
+    tor_free(conn);
+    tor_free(header);
+}
+
+#define SERVER_DESC_GET(id) GET("/tor/server/" id)
+static void
+test_dir_handle_get_server_descriptors_not_found(void* data)
+{
+  dir_connection_t *conn = NULL;
+  char *header = NULL;
+  (void) data;
+
+  MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+  conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+  const char *req = SERVER_DESC_GET("invalid");
+  tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+  fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+                      NULL, NULL, 1, 0);
+
+  tt_str_op(NOT_FOUND, OP_EQ, header);
+  tt_int_op(conn->dir_spool_src, OP_EQ, DIR_SPOOL_SERVER_BY_FP);
+
+  done:
+    UNMOCK(connection_write_to_buf_impl_);
+    tor_free(mock_options);
+    tor_free(conn);
+    tor_free(header);
+}
+
+static void
+test_dir_handle_get_server_descriptors_all(void* data)
+{
+  dir_connection_t *conn = NULL;
+  char *header = NULL;
+  char *body = NULL;
+  size_t body_used = 0;
+  (void) data;
+
+  /* Setup fake routerlist. */
+  helper_setup_fake_routerlist();
+
+  //TODO: change to router_get_my_extrainfo when testing "extra" path
+  NS_MOCK(router_get_my_routerinfo);
+  MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+  // We are one of the routers
+  routerlist_t *our_routerlist = router_get_routerlist();
+  tt_int_op(smartlist_len(our_routerlist->routers), OP_GE, 1);
+  mock_routerinfo = smartlist_get(our_routerlist->routers, 0);
+  set_server_identity_key(mock_routerinfo->identity_pkey);
+
+  /* Treat "all" requests as if they were unencrypted */
+  mock_routerinfo->cache_info.send_unencrypted = 1;
+
+  conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+  const char *req = SERVER_DESC_GET("all");
+  tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+  //TODO: Is this a BUG?
+  //It requires strlen(signed_descriptor_len)+1 as body_len but returns a body
+  //which is smaller than that by annotation_len bytes
+  fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+                      &body, &body_used,
+                      mock_routerinfo->cache_info.signed_descriptor_len+1, 0);
+
+  tt_assert(header);
+  tt_assert(body);
+
+  tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+  tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+  tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+
+  //TODO: Is this a BUG?
+  //This is what should be expected: tt_int_op(body_used, OP_EQ, strlen(body));
+  tt_int_op(body_used, OP_EQ,
+            mock_routerinfo->cache_info.signed_descriptor_len);
+
+  tt_str_op(body, OP_EQ, mock_routerinfo->cache_info.signed_descriptor_body +
+                         mock_routerinfo->cache_info.annotations_len);
+  tt_int_op(conn->dir_spool_src, OP_EQ, DIR_SPOOL_NONE);
+
+  done:
+    NS_UNMOCK(router_get_my_routerinfo);
+    UNMOCK(connection_write_to_buf_impl_);
+    tor_free(conn);
+    tor_free(header);
+    tor_free(body);
+
+    routerlist_free_all();
+    nodelist_free_all();
+    entry_guards_free_all();
+}
+
+static char
+TEST_DESCRIPTOR[] =
+"@uploaded-at 2014-06-08 19:20:11\n"
+"@source \"127.0.0.1\"\n"
+"router test000a 127.0.0.1 5000 0 7000\n"
+"platform Tor 0.2.5.3-alpha-dev on Linux\n"
+"protocols Link 1 2 Circuit 1\n"
+"published 2014-06-08 19:20:11\n"
+"fingerprint C7E7 CCB8 179F 8CC3 7F5C 8A04 2B3A 180B 934B 14BA\n"
+"uptime 0\n"
+"bandwidth 1073741824 1073741824 0\n"
+"extra-info-digest 67A152A4C7686FB07664F872620635F194D76D95\n"
+"caches-extra-info\n"
+"onion-key\n"
+"-----BEGIN RSA PUBLIC KEY-----\n"
+"MIGJAoGBAOuBUIEBARMkkka/TGyaQNgUEDLP0KG7sy6KNQTNOlZHUresPr/vlVjo\n"
+"HPpLMfu9M2z18c51YX/muWwY9x4MyQooD56wI4+AqXQcJRwQfQlPn3Ay82uZViA9\n"
+"DpBajRieLlKKkl145KjArpD7F5BVsqccvjErgFYXvhhjSrx7BVLnAgMBAAE=\n"
+"-----END RSA PUBLIC KEY-----\n"
+"signing-key\n"
+"-----BEGIN RSA PUBLIC KEY-----\n"
+"MIGJAoGBAN6NLnSxWQnFXxqZi5D3b0BMgV6y9NJLGjYQVP+eWtPZWgqyv4zeYsqv\n"
+"O9y6c5lvxyUxmNHfoAbe/s8f2Vf3/YaC17asAVSln4ktrr3e9iY74a9RMWHv1Gzk\n"
+"3042nMcqj3PEhRN0PoLkcOZNjjmNbaqki6qy9bWWZDNTdo+uI44dAgMBAAE=\n"
+"-----END RSA PUBLIC KEY-----\n"
+"hidden-service-dir\n"
+"contact auth0 at test.test\n"
+"ntor-onion-key pK4bs08ERYN591jj7ca17Rn9Q02TIEfhnjR6hSq+fhU=\n"
+"reject *:*\n"
+"router-signature\n"
+"-----BEGIN SIGNATURE-----\n"
+"rx88DuM3Y7tODlHNDDEVzKpwh3csaG1or+T4l2Xs1oq3iHHyPEtB6QTLYrC60trG\n"
+"aAPsj3DEowGfjga1b248g2dtic8Ab+0exfjMm1RHXfDam5TXXZU3A0wMyoHjqHuf\n"
+"eChGPgFNUvEc+5YtD27qEDcUjcinYztTs7/dzxBT4PE=\n"
+"-----END SIGNATURE-----\n";
+
+static void
+test_dir_handle_get_server_descriptors_authority(void* data)
+{
+  dir_connection_t *conn = NULL;
+  char *header = NULL;
+  char *body = NULL;
+  size_t body_used = 0;
+  crypto_pk_t *identity_pkey = pk_generate(0);
+  (void) data;
+
+  NS_MOCK(router_get_my_routerinfo);
+  MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+  /* init mock */
+  router_get_my_routerinfo();
+  crypto_pk_get_digest(identity_pkey,
+                       mock_routerinfo->cache_info.identity_digest);
+
+  // the digest is mine (the channel is unnecrypted, so we must allow sending)
+  set_server_identity_key(identity_pkey);
+  mock_routerinfo->cache_info.send_unencrypted = 1;
+
+  /* Setup descriptor */
+  int annotation_len = strstr(TEST_DESCRIPTOR, "router ") - TEST_DESCRIPTOR;
+  mock_routerinfo->cache_info.signed_descriptor_body = TEST_DESCRIPTOR;
+  mock_routerinfo->cache_info.signed_descriptor_len = strlen(TEST_DESCRIPTOR);
+  mock_routerinfo->cache_info.annotations_len = annotation_len;
+
+  conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+  const char *req = SERVER_DESC_GET("authority");
+  tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+  //TODO: Is this a BUG?
+  //It requires strlen(TEST_DESCRIPTOR)+1 as body_len but returns a body which
+  //is smaller than that by annotation_len bytes
+  fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+                      &body, &body_used, strlen(TEST_DESCRIPTOR)+1, 0);
+
+  tt_assert(header);
+  tt_assert(body);
+
+  tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+  tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+  tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+
+  //TODO: Is this a BUG?
+  //This is what should be expected:
+  //tt_int_op(body_used, OP_EQ, strlen(body));
+  tt_int_op(body_used, OP_EQ, strlen(TEST_DESCRIPTOR));
+
+  tt_str_op(body, OP_EQ, TEST_DESCRIPTOR + annotation_len);
+  tt_int_op(conn->dir_spool_src, OP_EQ, DIR_SPOOL_NONE);
+
+  done:
+    NS_UNMOCK(router_get_my_routerinfo);
+    UNMOCK(connection_write_to_buf_impl_);
+    tor_free(mock_routerinfo);
+    tor_free(conn);
+    tor_free(header);
+    tor_free(body);
+    crypto_pk_free(identity_pkey);
+}
+
+static void
+test_dir_handle_get_server_descriptors_fp(void* data)
+{
+  dir_connection_t *conn = NULL;
+  char *header = NULL;
+  char *body = NULL;
+  size_t body_used = 0;
+  crypto_pk_t *identity_pkey = pk_generate(0);
+  (void) data;
+
+  NS_MOCK(router_get_my_routerinfo);
+  MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+  /* init mock */
+  router_get_my_routerinfo();
+  crypto_pk_get_digest(identity_pkey,
+                       mock_routerinfo->cache_info.identity_digest);
+
+  // the digest is mine (the channel is unnecrypted, so we must allow sending)
+  set_server_identity_key(identity_pkey);
+  mock_routerinfo->cache_info.send_unencrypted = 1;
+
+  /* Setup descriptor */
+  int annotation_len = strstr(TEST_DESCRIPTOR, "router ") - TEST_DESCRIPTOR;
+  mock_routerinfo->cache_info.signed_descriptor_body = TEST_DESCRIPTOR;
+  mock_routerinfo->cache_info.signed_descriptor_len = strlen(TEST_DESCRIPTOR);
+  mock_routerinfo->cache_info.annotations_len = annotation_len;
+
+  conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+  #define HEX1 "Fe0daff89127389bc67558691231234551193EEE"
+  #define HEX2 "Deadbeef99999991111119999911111111f00ba4"
+  const char *hex_digest = hex_str(mock_routerinfo->cache_info.identity_digest,
+                                   DIGEST_LEN);
+
+  char req[155];
+  sprintf(req, SERVER_DESC_GET("fp/%s+" HEX1 "+" HEX2), hex_digest);
+  tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+  //TODO: Is this a BUG?
+  //It requires strlen(TEST_DESCRIPTOR)+1 as body_len but returns a body which
+  //is smaller than that by annotation_len bytes
+  fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+                      &body, &body_used, strlen(TEST_DESCRIPTOR)+1, 0);
+
+  tt_assert(header);
+  tt_assert(body);
+
+  tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+  tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+  tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+
+  //TODO: Is this a BUG?
+  //This is what should be expected:
+  //tt_int_op(body_used, OP_EQ, strlen(body));
+  tt_int_op(body_used, OP_EQ, strlen(TEST_DESCRIPTOR));
+
+  tt_str_op(body, OP_EQ, TEST_DESCRIPTOR + annotation_len);
+  tt_int_op(conn->dir_spool_src, OP_EQ, DIR_SPOOL_NONE);
+
+  done:
+    NS_UNMOCK(router_get_my_routerinfo);
+    UNMOCK(connection_write_to_buf_impl_);
+    tor_free(mock_routerinfo);
+    tor_free(conn);
+    tor_free(header);
+    tor_free(body);
+    crypto_pk_free(identity_pkey);
+}
+
+#define HEX1 "Fe0daff89127389bc67558691231234551193EEE"
+#define HEX2 "Deadbeef99999991111119999911111111f00ba4"
+
+static void
+test_dir_handle_get_server_descriptors_d(void* data)
+{
+  dir_connection_t *conn = NULL;
+  char *header = NULL;
+  char *body = NULL;
+  size_t body_used = 0;
+  crypto_pk_t *identity_pkey = pk_generate(0);
+  (void) data;
+
+  /* Setup fake routerlist. */
+  helper_setup_fake_routerlist();
+
+  MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+  /* Get one router's signed_descriptor_digest */
+  routerlist_t *our_routerlist = router_get_routerlist();
+  tt_int_op(smartlist_len(our_routerlist->routers), OP_GE, 1);
+  routerinfo_t *router = smartlist_get(our_routerlist->routers, 0);
+  const char *hex_digest = hex_str(router->cache_info.signed_descriptor_digest,
+                                   DIGEST_LEN);
+
+  conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+  char req_header[155];
+  sprintf(req_header, SERVER_DESC_GET("d/%s+" HEX1 "+" HEX2), hex_digest);
+  tt_int_op(directory_handle_command_get(conn, req_header, NULL, 0), OP_EQ, 0);
+
+  //TODO: Is this a BUG?
+  //It requires strlen(signed_descriptor_len)+1 as body_len but returns a body
+  //which is smaller than that by annotation_len bytes
+  fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+                      &body, &body_used,
+                      router->cache_info.signed_descriptor_len+1, 0);
+
+  tt_assert(header);
+  tt_assert(body);
+
+  tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+  tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+  tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+
+  //TODO: Is this a BUG?
+  //This is what should be expected:
+  //tt_int_op(body_used, OP_EQ, strlen(body));
+  tt_int_op(body_used, OP_EQ, router->cache_info.signed_descriptor_len);
+
+  tt_str_op(body, OP_EQ, router->cache_info.signed_descriptor_body +
+                         router->cache_info.annotations_len);
+  tt_int_op(conn->dir_spool_src, OP_EQ, DIR_SPOOL_NONE);
+
+  done:
+    UNMOCK(connection_write_to_buf_impl_);
+    tor_free(mock_routerinfo);
+    tor_free(conn);
+    tor_free(header);
+    tor_free(body);
+    crypto_pk_free(identity_pkey);
+
+    routerlist_free_all();
+    nodelist_free_all();
+    entry_guards_free_all();
+}
+
+static void
+test_dir_handle_get_server_descriptors_busy(void* data)
+{
+  dir_connection_t *conn = NULL;
+  char *header = NULL;
+  crypto_pk_t *identity_pkey = pk_generate(0);
+  (void) data;
+
+  /* Setup fake routerlist. */
+  helper_setup_fake_routerlist();
+
+  MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+  //Make it busy
+  MOCK(get_options, mock_get_options);
+  init_mock_options();
+  mock_options->CountPrivateBandwidth = 1;
+
+  /* Get one router's signed_descriptor_digest */
+  routerlist_t *our_routerlist = router_get_routerlist();
+  tt_int_op(smartlist_len(our_routerlist->routers), OP_GE, 1);
+  routerinfo_t *router = smartlist_get(our_routerlist->routers, 0);
+  const char *hex_digest = hex_str(router->cache_info.signed_descriptor_digest,
+                                   DIGEST_LEN);
+
+  conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+  #define HEX1 "Fe0daff89127389bc67558691231234551193EEE"
+  #define HEX2 "Deadbeef99999991111119999911111111f00ba4"
+  char req_header[155];
+  sprintf(req_header, SERVER_DESC_GET("d/%s+" HEX1 "+" HEX2), hex_digest);
+  tt_int_op(directory_handle_command_get(conn, req_header, NULL, 0), OP_EQ, 0);
+
+  fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+                      NULL, NULL, 1, 0);
+
+  tt_assert(header);
+  tt_str_op(SERVER_BUSY, OP_EQ, header);
+
+  tt_int_op(conn->dir_spool_src, OP_EQ, DIR_SPOOL_NONE);
+
+  done:
+    UNMOCK(get_options);
+    UNMOCK(connection_write_to_buf_impl_);
+    tor_free(mock_routerinfo);
+    tor_free(conn);
+    tor_free(header);
+    crypto_pk_free(identity_pkey);
+
+    routerlist_free_all();
+    nodelist_free_all();
+    entry_guards_free_all();
+}
+
+static void
+test_dir_handle_get_server_keys_bad_req(void* data)
+{
+  dir_connection_t *conn = NULL;
+  char *header = NULL;
+  (void) data;
+
+  MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+  conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+  const char *req = GET("/tor/keys/");
+  tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+  fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+                      NULL, NULL, 1, 0);
+
+  tt_assert(header);
+  tt_str_op(BAD_REQUEST, OP_EQ, header);
+
+  done:
+    UNMOCK(connection_write_to_buf_impl_);
+    tor_free(conn);
+    tor_free(header);
+}
+
+static void
+test_dir_handle_get_server_keys_all_not_found(void* data)
+{
+  dir_connection_t *conn = NULL;
+  char *header = NULL;
+  (void) data;
+
+  MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+  conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+  const char *req = GET("/tor/keys/all");
+  tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+  fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+                      NULL, NULL, 1, 0);
+
+  tt_assert(header);
+  tt_str_op(NOT_FOUND, OP_EQ, header);
+
+  done:
+    UNMOCK(connection_write_to_buf_impl_);
+    tor_free(conn);
+    tor_free(header);
+}
+
+#define TEST_CERTIFICATE AUTHORITY_CERT_3
+#define TEST_SIGNING_KEY AUTHORITY_SIGNKEY_A_DIGEST
+extern const char AUTHORITY_CERT_3[];
+extern const char AUTHORITY_SIGNKEY_A_DIGEST[];
+
+static const char TEST_CERT_IDENT_KEY[] =
+  "D867ACF56A9D229B35C25F0090BC9867E906BE69";
+
+static void
+test_dir_handle_get_server_keys_all(void* data)
+{
+  dir_connection_t *conn = NULL;
+  char *header = NULL;
+  char *body = NULL;
+  size_t body_used = 0;
+
+  dir_server_t *ds = NULL;
+  (void) data;
+
+  MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+  clear_dir_servers();
+  routerlist_free_all();
+
+  /* create a trusted ds */
+  ds = trusted_dir_server_new("ds", "127.0.0.1", 9059, 9060, "", NULL,
+                              V3_DIRINFO, 1.0);
+  tt_assert(ds);
+  dir_server_add(ds);
+
+  /* ds v3_identity_digest is the certificate's identity_key */
+  base16_decode(ds->v3_identity_digest, DIGEST_LEN,
+                TEST_CERT_IDENT_KEY, HEX_DIGEST_LEN);
+  tt_int_op(0, OP_EQ, trusted_dirs_load_certs_from_string(TEST_CERTIFICATE,
+    TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST, 1));
+
+  conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+  const char *req = GET("/tor/keys/all");
+  tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+  fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+                      &body, &body_used, strlen(TEST_CERTIFICATE)+1, 0);
+
+  tt_assert(header);
+  tt_assert(body);
+
+  tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+  tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+  tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+  tt_assert(strstr(header, "Content-Length: 1883\r\n"));
+
+  tt_str_op(TEST_CERTIFICATE, OP_EQ, body);
+
+  done:
+    UNMOCK(connection_write_to_buf_impl_);
+    tor_free(conn);
+    tor_free(header);
+    tor_free(body);
+
+    clear_dir_servers();
+    routerlist_free_all();
+}
+
+static void
+test_dir_handle_get_server_keys_authority_not_found(void* data)
+{
+  dir_connection_t *conn = NULL;
+  char *header = NULL;
+  (void) data;
+
+  MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+  conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+  const char *req = GET("/tor/keys/authority");
+  tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+  fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+                      NULL, NULL, 1, 0);
+
+  tt_assert(header);
+  tt_str_op(NOT_FOUND, OP_EQ, header);
+
+  done:
+    UNMOCK(connection_write_to_buf_impl_);
+    tor_free(conn);
+    tor_free(header);
+}
+
+static authority_cert_t * mock_cert = NULL;
+
+static authority_cert_t *
+get_my_v3_authority_cert_m(void)
+{
+  tor_assert(mock_cert);
+  return mock_cert;
+}
+
+static void
+test_dir_handle_get_server_keys_authority(void* data)
+{
+  dir_connection_t *conn = NULL;
+  char *header = NULL;
+  char *body = NULL;
+  size_t body_used = 0;
+  (void) data;
+
+  mock_cert = authority_cert_parse_from_string(TEST_CERTIFICATE, NULL);
+
+  MOCK(get_my_v3_authority_cert, get_my_v3_authority_cert_m);
+  MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+  conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+  const char *req = GET("/tor/keys/authority");
+  tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+  fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+                      &body, &body_used, strlen(TEST_CERTIFICATE)+1, 0);
+
+  tt_assert(header);
+  tt_assert(body);
+
+  tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+  tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+  tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+  tt_assert(strstr(header, "Content-Length: 1883\r\n"));
+
+  tt_str_op(TEST_CERTIFICATE, OP_EQ, body);
+
+  done:
+    UNMOCK(get_my_v3_authority_cert);
+    UNMOCK(connection_write_to_buf_impl_);
+    tor_free(conn);
+    tor_free(header);
+    tor_free(body);
+    tor_free(mock_cert);
+}
+
+static void
+test_dir_handle_get_server_keys_fp_not_found(void* data)
+{
+  dir_connection_t *conn = NULL;
+  char *header = NULL;
+  (void) data;
+
+  MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+  conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+  const char *req = GET("/tor/keys/fp/somehex");
+  tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+  fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+                      NULL, NULL, 1, 0);
+
+  tt_assert(header);
+  tt_str_op(NOT_FOUND, OP_EQ, header);
+
+  done:
+    UNMOCK(connection_write_to_buf_impl_);
+    tor_free(conn);
+    tor_free(header);
+}
+
+static void
+test_dir_handle_get_server_keys_fp(void* data)
+{
+  dir_connection_t *conn = NULL;
+  char *header = NULL;
+  char *body = NULL;
+  size_t body_used = 0;
+  dir_server_t *ds = NULL;
+  (void) data;
+
+  MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+  clear_dir_servers();
+  routerlist_free_all();
+
+  /* create a trusted ds */
+  ds = trusted_dir_server_new("ds", "127.0.0.1", 9059, 9060, "", NULL,
+                              V3_DIRINFO, 1.0);
+  tt_assert(ds);
+  dir_server_add(ds);
+
+  /* ds v3_identity_digest is the certificate's identity_key */
+  base16_decode(ds->v3_identity_digest, DIGEST_LEN,
+                TEST_CERT_IDENT_KEY, HEX_DIGEST_LEN);
+
+  tt_int_op(0, OP_EQ, trusted_dirs_load_certs_from_string(TEST_CERTIFICATE,
+    TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST, 1));
+
+  conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+  char req[71];
+  sprintf(req, GET("/tor/keys/fp/%s"), TEST_CERT_IDENT_KEY);
+  tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+  fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+                      &body, &body_used, strlen(TEST_CERTIFICATE)+1, 0);
+
+  tt_assert(header);
+  tt_assert(body);
+
+  tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+  tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+  tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+  tt_assert(strstr(header, "Content-Length: 1883\r\n"));
+
+  tt_str_op(TEST_CERTIFICATE, OP_EQ, body);
+
+  done:
+    UNMOCK(connection_write_to_buf_impl_);
+    tor_free(conn);
+    tor_free(header);
+    tor_free(body);
+    clear_dir_servers();
+    routerlist_free_all();
+}
+
+static void
+test_dir_handle_get_server_keys_sk_not_found(void* data)
+{
+  dir_connection_t *conn = NULL;
+  char *header = NULL;
+  (void) data;
+
+  MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+  conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+  const char *req = GET("/tor/keys/sk/somehex");
+  tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+  fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+                      NULL, NULL, 1, 0);
+
+  tt_assert(header);
+  tt_str_op(NOT_FOUND, OP_EQ, header);
+
+  done:
+    UNMOCK(connection_write_to_buf_impl_);
+    tor_free(conn);
+    tor_free(header);
+}
+
+static void
+test_dir_handle_get_server_keys_sk(void* data)
+{
+  dir_connection_t *conn = NULL;
+  char *header = NULL;
+  char *body = NULL;
+  size_t body_used = 0;
+  (void) data;
+
+  mock_cert = authority_cert_parse_from_string(TEST_CERTIFICATE, NULL);
+  MOCK(get_my_v3_authority_cert, get_my_v3_authority_cert_m);
+  MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+  clear_dir_servers();
+  routerlist_free_all();
+
+  tt_int_op(0, OP_EQ, trusted_dirs_load_certs_from_string(TEST_CERTIFICATE,
+    TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST, 1));
+
+  conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+  char req[71];
+  sprintf(req, GET("/tor/keys/sk/%s"), TEST_SIGNING_KEY);
+  tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+  fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+                      &body, &body_used, strlen(TEST_CERTIFICATE)+1, 0);
+
+  tt_assert(header);
+  tt_assert(body);
+
+  tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+  tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+  tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+  tt_assert(strstr(header, "Content-Length: 1883\r\n"));
+
+  tt_str_op(TEST_CERTIFICATE, OP_EQ, body);
+
+  done:
+    UNMOCK(get_my_v3_authority_cert);
+    UNMOCK(connection_write_to_buf_impl_);
+    tor_free(conn);
+    tor_free(header);
+    tor_free(body);
+}
+
+static void
+test_dir_handle_get_server_keys_fpsk_not_found(void* data)
+{
+  dir_connection_t *conn = NULL;
+  char *header = NULL;
+  (void) data;
+
+  MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+  conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+  const char *req = GET("/tor/keys/fp-sk/somehex");
+  tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+  fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+                      NULL, NULL, 1, 0);
+
+  tt_assert(header);
+  tt_str_op(NOT_FOUND, OP_EQ, header);
+
+  done:
+    UNMOCK(connection_write_to_buf_impl_);
+    tor_free(conn);
+    tor_free(header);
+}
+
+static void
+test_dir_handle_get_server_keys_fpsk(void* data)
+{
+  dir_connection_t *conn = NULL;
+  char *header = NULL;
+  char *body = NULL;
+  size_t body_used = 0;
+  dir_server_t *ds = NULL;
+  (void) data;
+
+  MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+  clear_dir_servers();
+  routerlist_free_all();
+
+  /* create a trusted ds */
+  ds = trusted_dir_server_new("ds", "127.0.0.1", 9059, 9060, "", NULL,
+                              V3_DIRINFO, 1.0);
+  tt_assert(ds);
+
+  /* ds v3_identity_digest is the certificate's identity_key */
+  base16_decode(ds->v3_identity_digest, DIGEST_LEN,
+                TEST_CERT_IDENT_KEY, HEX_DIGEST_LEN);
+  dir_server_add(ds);
+
+  tt_int_op(0, OP_EQ, trusted_dirs_load_certs_from_string(TEST_CERTIFICATE,
+    TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST, 1));
+
+  conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+  char req[115];
+  sprintf(req, GET("/tor/keys/fp-sk/%s-%s"),
+          TEST_CERT_IDENT_KEY, TEST_SIGNING_KEY);
+
+  tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+  fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+                      &body, &body_used, strlen(TEST_CERTIFICATE)+1, 0);
+
+  tt_assert(header);
+  tt_assert(body);
+
+  tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+  tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+  tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+  tt_assert(strstr(header, "Content-Length: 1883\r\n"));
+
+  tt_str_op(TEST_CERTIFICATE, OP_EQ, body);
+
+  done:
+    UNMOCK(connection_write_to_buf_impl_);
+    tor_free(conn);
+    tor_free(header);
+    tor_free(body);
+
+    clear_dir_servers();
+    routerlist_free_all();
+}
+
+static void
+test_dir_handle_get_server_keys_busy(void* data)
+{
+  dir_connection_t *conn = NULL;
+  char *header = NULL;
+  dir_server_t *ds = NULL;
+  (void) data;
+
+  clear_dir_servers();
+  routerlist_free_all();
+
+  /* create a trusted ds */
+  ds = trusted_dir_server_new("ds", "127.0.0.1", 9059, 9060, "", NULL,
+                              V3_DIRINFO, 1.0);
+  tt_assert(ds);
+
+  /* ds v3_identity_digest is the certificate's identity_key */
+  base16_decode(ds->v3_identity_digest, DIGEST_LEN,
+                TEST_CERT_IDENT_KEY, HEX_DIGEST_LEN);
+  dir_server_add(ds);
+
+  tt_int_op(0, OP_EQ, trusted_dirs_load_certs_from_string(TEST_CERTIFICATE,
+    TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST, 1));
+
+  MOCK(get_options, mock_get_options);
+  MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+  /* setup busy server */
+  init_mock_options();
+  mock_options->CountPrivateBandwidth = 1;
+
+  conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+  char req[71];
+  sprintf(req, GET("/tor/keys/fp/%s"), TEST_CERT_IDENT_KEY);
+  tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+  fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+                      NULL, NULL, 1, 0);
+
+  tt_assert(header);
+  tt_str_op(SERVER_BUSY, OP_EQ, header);
+
+  done:
+    UNMOCK(get_options);
+    UNMOCK(connection_write_to_buf_impl_);
+    tor_free(conn);
+    tor_free(header);
+    tor_free(mock_options);
+
+    clear_dir_servers();
+    routerlist_free_all();
+}
+
+static networkstatus_t *mock_ns_val = NULL;
+static networkstatus_t *
+mock_ns_get_by_flavor(consensus_flavor_t f)
+{
+  (void)f;
+  return mock_ns_val;
+}
+
+static void
+test_dir_handle_get_status_vote_current_consensus_ns_not_enough_sigs(void* d)
+{
+  dir_connection_t *conn = NULL;
+  char *header = NULL;
+  (void) d;
+
+  /* init mock */
+  mock_ns_val = tor_malloc_zero(sizeof(networkstatus_t));
+  mock_ns_val->flavor = FLAV_NS;
+  mock_ns_val->voters = smartlist_new();
+
+  /* init mock */
+  init_mock_options();
+
+  MOCK(get_options, mock_get_options);
+  MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+  MOCK(networkstatus_get_latest_consensus_by_flavor, mock_ns_get_by_flavor);
+
+  /* start gathering stats */
+  mock_options->DirReqStatistics = 1;
+  geoip_dirreq_stats_init(time(NULL));
+
+  conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+  tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
+    GET("/tor/status-vote/current/consensus-ns/" HEX1 "+" HEX2), NULL, 0));
+
+  fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+                      NULL, NULL, 1, 0);
+
+  tt_assert(header);
+  tt_str_op(NOT_ENOUGH_CONSENSUS_SIGNATURES, OP_EQ, header);
+
+  char *stats = geoip_format_dirreq_stats(time(NULL));
+  tt_assert(stats);
+  tt_assert(strstr(stats, "not-enough-sigs=8"));
+
+  done:
+    UNMOCK(networkstatus_get_latest_consensus_by_flavor);
+    UNMOCK(connection_write_to_buf_impl_);
+    UNMOCK(get_options);
+
+    tor_free(conn);
+    tor_free(header);
+    smartlist_free(mock_ns_val->voters);
+    tor_free(mock_ns_val);
+    tor_free(mock_options);
+}
+
+static void
+test_dir_handle_get_status_vote_current_consensus_ns_not_found(void* data)
+{
+  dir_connection_t *conn = NULL;
+  char *header = NULL;
+  (void) data;
+
+  init_mock_options();
+
+  MOCK(get_options, mock_get_options);
+  MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+  /* start gathering stats */
+  mock_options->DirReqStatistics = 1;
+  geoip_dirreq_stats_init(time(NULL));
+
+  conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+  tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
+    GET("/tor/status-vote/current/consensus-ns"), NULL, 0));
+
+  fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+                      NULL, NULL, 1, 0);
+  tt_assert(header);
+  tt_str_op(NOT_FOUND, OP_EQ, header);
+
+  char *stats = geoip_format_dirreq_stats(time(NULL));
+  tt_assert(stats);
+  tt_assert(strstr(stats, "not-found=8"));
+
+  done:
+    UNMOCK(connection_write_to_buf_impl_);
+    UNMOCK(get_options);
+    tor_free(conn);
+    tor_free(header);
+    tor_free(mock_options);
+}
+
+NS_DECL(int, geoip_get_country_by_addr, (const tor_addr_t *addr));
+
+int
+NS(geoip_get_country_by_addr)(const tor_addr_t *addr)
+{
+  CALLED(geoip_get_country_by_addr)++;
+  return 1;
+}
+
+static void
+status_vote_current_consensus_ns_test(char **header, char **body,
+                                      size_t *body_len)
+{
+  digests_t digests;
+  dir_connection_t *conn = NULL;
+
+  #define NETWORK_STATUS "some network status string"
+  dirserv_set_cached_consensus_networkstatus(NETWORK_STATUS, "ns", &digests,
+                                             time(NULL));
+
+  MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+  tt_assert(mock_options);
+  mock_options->DirReqStatistics = 1;
+  geoip_dirreq_stats_init(time(NULL));
+
+  /* init geoip database */
+  geoip_parse_entry("10,50,AB", AF_INET);
+  tt_str_op("ab", OP_EQ, geoip_get_country_name(1));
+
+  conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+  TO_CONN(conn)->address = "127.0.0.1";
+
+  tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
+    GET("/tor/status-vote/current/consensus-ns"), NULL, 0));
+
+  fetch_from_buf_http(TO_CONN(conn)->outbuf, header, MAX_HEADERS_SIZE,
+                      body, body_len, strlen(NETWORK_STATUS)+7, 0);
+
+  done:
+    UNMOCK(connection_write_to_buf_impl_);
+    tor_free(conn);
+}
+
+static void
+test_dir_handle_get_status_vote_current_consensus_ns(void* data)
+{
+  char *header = NULL;
+  char *body = NULL, *comp_body = NULL;
+  size_t body_used = 0, comp_body_used = 0;
+  char *stats = NULL, *hist = NULL;
+  (void) data;
+
+  dirserv_free_all();
+  clear_geoip_db();
+
+  NS_MOCK(geoip_get_country_by_addr);
+  MOCK(get_options, mock_get_options);
+
+  init_mock_options();
+
+  status_vote_current_consensus_ns_test(&header, &comp_body, &comp_body_used);
+  tt_assert(header);
+
+  tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+  tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+  tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+  tt_assert(strstr(header, "Pragma: no-cache\r\n"));
+
+  compress_method_t compression = detect_compression_method(comp_body,
+                                                            comp_body_used);
+  tt_int_op(ZLIB_METHOD, OP_EQ, compression);
+
+  tor_gzip_uncompress(&body, &body_used, comp_body, comp_body_used,
+                      compression, 0, LOG_PROTOCOL_WARN);
+
+  tt_str_op(NETWORK_STATUS, OP_EQ, body);
+  tt_int_op(strlen(NETWORK_STATUS), OP_EQ, body_used);
+
+  stats = geoip_format_dirreq_stats(time(NULL));
+  tt_assert(stats);
+
+  tt_assert(strstr(stats, "ok=8"));
+  tt_assert(strstr(stats, "dirreq-v3-ips ab=8"));
+  tt_assert(strstr(stats, "dirreq-v3-reqs ab=8"));
+  tt_assert(strstr(stats, "dirreq-v3-direct-dl"
+                          " complete=0,timeout=0,running=4"));
+
+  hist = geoip_get_request_history();
+  tt_assert(hist);
+  tt_str_op("ab=8", OP_EQ, hist);
+
+  done:
+    NS_UNMOCK(geoip_get_country_by_addr);
+    UNMOCK(get_options);
+    tor_free(header);
+    tor_free(comp_body);
+    tor_free(body);
+    tor_free(stats);
+    tor_free(hist);
+    tor_free(mock_options);
+
+    dirserv_free_all();
+    clear_geoip_db();
+}
+
+static void
+test_dir_handle_get_status_vote_current_consensus_ns_busy(void* data)
+{
+  char *header = NULL;
+  char *body = NULL;
+  size_t body_used = 0;
+  (void) data;
+
+  dirserv_free_all();
+  clear_geoip_db();
+
+  MOCK(get_options, mock_get_options);
+
+  // Make it busy
+  init_mock_options();
+  mock_options->CountPrivateBandwidth = 1;
+
+  status_vote_current_consensus_ns_test(&header, &body, &body_used);
+  tt_assert(header);
+
+  tt_str_op(SERVER_BUSY, OP_EQ, header);
+
+  char *stats = geoip_format_dirreq_stats(time(NULL));
+  tt_assert(stats);
+  tt_assert(strstr(stats, "busy=8"));
+
+  done:
+    UNMOCK(get_options);
+    tor_free(header);
+    tor_free(body);
+    tor_free(mock_options);
+
+    dirserv_free_all();
+    clear_geoip_db();
+}
+
+static void
+test_dir_handle_get_status_vote_current_not_found(void* data)
+{
+  dir_connection_t *conn = NULL;
+  char *header = NULL;
+  (void) data;
+
+  MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+  conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+  tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
+    GET("/tor/status-vote/current/" HEX1), NULL, 0));
+
+  fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+                      NULL, NULL, 1, 0);
+  tt_assert(header);
+  tt_str_op(NOT_FOUND, OP_EQ, header);
+
+  done:
+    UNMOCK(connection_write_to_buf_impl_);
+    tor_free(conn);
+    tor_free(header);
+}
+
+#define VOTE_DIGEST "312A4890D4D832597ABBD3089C782DBBFB81E48D"
+
+static void
+status_vote_current_d_test(char **header, char **body, size_t *body_l)
+{
+  dir_connection_t *conn = NULL;
+
+  MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+  conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+  tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
+    GET("/tor/status-vote/current/d/" VOTE_DIGEST), NULL, 0));
+
+  fetch_from_buf_http(TO_CONN(conn)->outbuf, header, MAX_HEADERS_SIZE,
+                      body, body_l, strlen(VOTE_BODY_V3)+1, 0);
+  tt_assert(header);
+
+  done:
+    UNMOCK(connection_write_to_buf_impl_);
+    tor_free(conn);
+}
+
+static void
+status_vote_next_d_test(char **header, char **body, size_t *body_l)
+{
+  dir_connection_t *conn = NULL;
+
+  MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+  conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+  tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
+    GET("/tor/status-vote/next/d/" VOTE_DIGEST), NULL, 0));
+
+  fetch_from_buf_http(TO_CONN(conn)->outbuf, header, MAX_HEADERS_SIZE,
+                      body, body_l, strlen(VOTE_BODY_V3)+1, 0);
+  tt_assert(header);
+
+  done:
+    UNMOCK(connection_write_to_buf_impl_);
+    tor_free(conn);
+}
+
+static void
+test_dir_handle_get_status_vote_current_d_not_found(void* data)
+{
+  char *header = NULL;
+  (void) data;
+
+  status_vote_current_d_test(&header, NULL, NULL);
+
+  tt_assert(header);
+  tt_str_op(NOT_FOUND, OP_EQ, header);
+
+  done:
+    tor_free(header);
+}
+
+static void
+test_dir_handle_get_status_vote_next_d_not_found(void* data)
+{
+  char *header = NULL;
+  (void) data;
+
+  status_vote_next_d_test(&header, NULL, NULL);
+
+  tt_assert(header);
+  tt_str_op(NOT_FOUND, OP_EQ, header);
+
+  done:
+    UNMOCK(connection_write_to_buf_impl_);
+    tor_free(header);
+}
+
+static void
+test_dir_handle_get_status_vote_d(void* data)
+{
+  char *header = NULL, *body = NULL;
+  size_t body_used = 0;
+  dir_server_t *ds = NULL;
+  (void) data;
+
+  clear_dir_servers();
+  dirvote_free_all();
+
+  /* create a trusted ds */
+  ds = trusted_dir_server_new("ds", "127.0.0.1", 9059, 9060, "", NULL,
+                              V3_DIRINFO, 1.0);
+  tt_assert(ds);
+  dir_server_add(ds);
+
+  /* ds v3_identity_digest is the certificate's identity_key */
+  base16_decode(ds->v3_identity_digest, DIGEST_LEN,
+                TEST_CERT_IDENT_KEY, HEX_DIGEST_LEN);
+
+  init_mock_options();
+  mock_options->AuthoritativeDir = 1;
+  mock_options->V3AuthoritativeDir = 1;
+  mock_options->TestingV3AuthVotingStartOffset = 0;
+  mock_options->TestingV3AuthInitialVotingInterval = 1;
+  mock_options->TestingV3AuthInitialVoteDelay = 1;
+  mock_options->TestingV3AuthInitialDistDelay = 1;
+
+  time_t now = 1441223455 -1;
+  dirvote_recalculate_timing(mock_options, now);
+
+  const char *msg_out = NULL;
+  int status_out = 0;
+  struct pending_vote_t *pv = dirvote_add_vote(VOTE_BODY_V3, &msg_out,
+                                               &status_out);
+  tt_assert(pv);
+
+  status_vote_current_d_test(&header, &body, &body_used);
+
+  tt_assert(header);
+  tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+  tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+  tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+  tt_assert(strstr(header, "Content-Length: 4135\r\n"));
+
+  tt_str_op(VOTE_BODY_V3, OP_EQ, body);
+
+  tor_free(header);
+  tor_free(body);
+
+  status_vote_next_d_test(&header, &body, &body_used);
+
+  tt_assert(header);
+  tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+  tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+  tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+  tt_assert(strstr(header, "Content-Length: 4135\r\n"));
+
+  tt_str_op(VOTE_BODY_V3, OP_EQ, body);
+
+  done:
+    tor_free(header);
+    tor_free(body);
+    tor_free(mock_options);
+
+    clear_dir_servers();
+    dirvote_free_all();
+}
+
+static void
+test_dir_handle_get_status_vote_next_not_found(void* data)
+{
+  dir_connection_t *conn = NULL;
+  char *header = NULL;
+  (void) data;
+
+  MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+  conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+  tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
+    GET("/tor/status-vote/next/" HEX1), NULL, 0));
+
+  fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+                      NULL, NULL, 1, 0);
+  tt_assert(header);
+  tt_str_op(NOT_FOUND, OP_EQ, header);
+
+  done:
+    UNMOCK(connection_write_to_buf_impl_);
+    tor_free(conn);
+    tor_free(header);
+}
+
+static void
+status_vote_next_consensus_test(char **header, char **body, size_t *body_used)
+{
+  dir_connection_t *conn = NULL;
+
+  MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+  conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+  tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
+    GET("/tor/status-vote/next/consensus"), NULL, 0));
+
+  fetch_from_buf_http(TO_CONN(conn)->outbuf, header, MAX_HEADERS_SIZE,
+                      body, body_used, 18, 0);
+  done:
+    UNMOCK(connection_write_to_buf_impl_);
+    tor_free(conn);
+}
+
+static void
+test_dir_handle_get_status_vote_next_consensus_not_found(void* data)
+{
+  char *header = NULL, *body = NULL;
+  size_t body_used;
+  (void) data;
+
+  status_vote_next_consensus_test(&header, &body, &body_used);
+
+  tt_assert(header);
+  tt_str_op(NOT_FOUND, OP_EQ, header);
+
+  done:
+    tor_free(header);
+    tor_free(body);
+}
+
+static void
+test_dir_handle_get_status_vote_current_authority_not_found(void* data)
+{
+  dir_connection_t *conn = NULL;
+  char *header = NULL;
+  (void) data;
+
+  MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+  conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+  tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
+    GET("/tor/status-vote/current/authority"), NULL, 0));
+
+  fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+                      NULL, NULL, 1, 0);
+  tt_assert(header);
+  tt_str_op(NOT_FOUND, OP_EQ, header);
+
+  done:
+    UNMOCK(connection_write_to_buf_impl_);
+    tor_free(conn);
+    tor_free(header);
+}
+
+static void
+test_dir_handle_get_status_vote_next_authority_not_found(void* data)
+{
+  dir_connection_t *conn = NULL;
+  char *header = NULL;
+  (void) data;
+
+  MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+  conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+  tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
+    GET("/tor/status-vote/next/authority"), NULL, 0));
+
+  fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+                      NULL, NULL, 1, 0);
+  tt_assert(header);
+  tt_str_op(NOT_FOUND, OP_EQ, header);
+
+  done:
+    UNMOCK(connection_write_to_buf_impl_);
+    tor_free(conn);
+    tor_free(header);
+}
+
+NS_DECL(const char*,
+dirvote_get_pending_consensus, (consensus_flavor_t flav));
+
+const char*
+NS(dirvote_get_pending_consensus)(consensus_flavor_t flav)
+{
+  return "pending consensus";
+}
+
+static void
+test_dir_handle_get_status_vote_next_consensus(void* data)
+{
+  char *header = NULL, *body = NULL;
+  size_t body_used = 0;
+  (void) data;
+
+  NS_MOCK(dirvote_get_pending_consensus);
+
+  status_vote_next_consensus_test(&header, &body, &body_used);
+  tt_assert(header);
+
+  tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+  tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+  tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+  tt_assert(strstr(header, "Content-Length: 17\r\n"));
+
+  tt_str_op("pending consensus", OP_EQ, body);
+
+  done:
+    NS_UNMOCK(dirvote_get_pending_consensus);
+    tor_free(header);
+    tor_free(body);
+}
+
+static void
+test_dir_handle_get_status_vote_next_consensus_busy(void* data)
+{
+  char *header = NULL, *body = NULL;
+  size_t body_used = 0;
+  (void) data;
+
+  MOCK(get_options, mock_get_options);
+  NS_MOCK(dirvote_get_pending_consensus);
+
+  //Make it busy
+  init_mock_options();
+  mock_options->CountPrivateBandwidth = 1;
+
+  status_vote_next_consensus_test(&header, &body, &body_used);
+
+  tt_assert(header);
+  tt_str_op(SERVER_BUSY, OP_EQ, header);
+
+  done:
+    NS_UNMOCK(dirvote_get_pending_consensus);
+    UNMOCK(get_options);
+    tor_free(header);
+    tor_free(body);
+    tor_free(mock_options);
+}
+
+static void
+status_vote_next_consensus_signatures_test(char **header, char **body,
+                                           size_t *body_used)
+{
+  dir_connection_t *conn = NULL;
+
+  MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+  conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+  tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
+    GET("/tor/status-vote/next/consensus-signatures"), NULL, 0));
+
+  fetch_from_buf_http(TO_CONN(conn)->outbuf, header, MAX_HEADERS_SIZE,
+                      body, body_used, 22, 0);
+
+  done:
+    UNMOCK(connection_write_to_buf_impl_);
+}
+
+static void
+test_dir_handle_get_status_vote_next_consensus_signatures_not_found(void* data)
+{
+  dir_connection_t *conn = NULL;
+  char *header = NULL, *body = NULL;
+  size_t body_used;
+  (void) data;
+
+  status_vote_next_consensus_signatures_test(&header, &body, &body_used);
+
+  tt_assert(header);
+  tt_str_op(NOT_FOUND, OP_EQ, header);
+
+  done:
+    tor_free(conn);
+    tor_free(header);
+    tor_free(body);
+}
+
+NS_DECL(const char*,
+dirvote_get_pending_detached_signatures, (void));
+
+const char*
+NS(dirvote_get_pending_detached_signatures)(void)
+{
+  return "pending detached sigs";
+}
+
+static void
+test_dir_handle_get_status_vote_next_consensus_signatures(void* data)
+{
+  char *header = NULL, *body = NULL;
+  size_t body_used = 0;
+  (void) data;
+
+  NS_MOCK(dirvote_get_pending_detached_signatures);
+
+  status_vote_next_consensus_signatures_test(&header, &body, &body_used);
+  tt_assert(header);
+
+  tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+  tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+  tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+  tt_assert(strstr(header, "Content-Length: 21\r\n"));
+
+  tt_str_op("pending detached sigs", OP_EQ, body);
+
+  done:
+    NS_UNMOCK(dirvote_get_pending_detached_signatures);
+    tor_free(header);
+    tor_free(body);
+}
+
+static void
+test_dir_handle_get_status_vote_next_consensus_signatures_busy(void* data)
+{
+  dir_connection_t *conn = NULL;
+  char *header = NULL, *body = NULL;
+  size_t body_used;
+  (void) data;
+
+  NS_MOCK(dirvote_get_pending_detached_signatures);
+  MOCK(get_options, mock_get_options);
+
+  //Make it busy
+  init_mock_options();
+  mock_options->CountPrivateBandwidth = 1;
+
+  status_vote_next_consensus_signatures_test(&header, &body, &body_used);
+
+  tt_assert(header);
+  tt_str_op(SERVER_BUSY, OP_EQ, header);
+
+  done:
+    UNMOCK(get_options);
+    NS_UNMOCK(dirvote_get_pending_detached_signatures);
+    tor_free(conn);
+    tor_free(header);
+    tor_free(body);
+    tor_free(mock_options);
+}
+
+static void
+test_dir_handle_get_status_vote_next_authority(void* data)
+{
+  dir_connection_t *conn = NULL;
+  char *header = NULL, *body = NULL;
+  const char *msg_out = NULL;
+  int status_out = 0;
+  size_t body_used = 0;
+  dir_server_t *ds = NULL;
+  (void) data;
+
+  clear_dir_servers();
+  routerlist_free_all();
+  dirvote_free_all();
+
+  mock_cert = authority_cert_parse_from_string(TEST_CERTIFICATE, NULL);
+
+  /* create a trusted ds */
+  ds = trusted_dir_server_new("ds", "127.0.0.1", 9059, 9060, "", NULL,
+                              V3_DIRINFO, 1.0);
+  tt_assert(ds);
+  dir_server_add(ds);
+
+  /* ds v3_identity_digest is the certificate's identity_key */
+  base16_decode(ds->v3_identity_digest, DIGEST_LEN,
+                TEST_CERT_IDENT_KEY, HEX_DIGEST_LEN);
+  tt_int_op(0, OP_EQ, trusted_dirs_load_certs_from_string(TEST_CERTIFICATE,
+    TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST, 1));
+
+  init_mock_options();
+  mock_options->AuthoritativeDir = 1;
+  mock_options->V3AuthoritativeDir = 1;
+  mock_options->TestingV3AuthVotingStartOffset = 0;
+  mock_options->TestingV3AuthInitialVotingInterval = 1;
+  mock_options->TestingV3AuthInitialVoteDelay = 1;
+  mock_options->TestingV3AuthInitialDistDelay = 1;
+
+  time_t now = 1441223455 -1;
+  dirvote_recalculate_timing(mock_options, now);
+
+  struct pending_vote_t *vote = dirvote_add_vote(VOTE_BODY_V3, &msg_out,
+                                                 &status_out);
+  tt_assert(vote);
+
+  MOCK(get_my_v3_authority_cert, get_my_v3_authority_cert_m);
+  MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+  conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+  tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
+    GET("/tor/status-vote/next/authority"), NULL, 0));
+
+  fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+                      &body, &body_used, strlen(VOTE_BODY_V3)+1, 0);
+
+  tt_assert(header);
+  tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+  tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+  tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+  tt_assert(strstr(header, "Content-Length: 4135\r\n"));
+
+  tt_str_op(VOTE_BODY_V3, OP_EQ, body);
+
+  done:
+    UNMOCK(connection_write_to_buf_impl_);
+    UNMOCK(get_my_v3_authority_cert);
+    tor_free(conn);
+    tor_free(header);
+    tor_free(mock_cert);
+
+    clear_dir_servers();
+    routerlist_free_all();
+    dirvote_free_all();
+}
+
+static void
+test_dir_handle_get_status_vote_current_authority(void* data)
+{
+  dir_connection_t *conn = NULL;
+  char *header = NULL, *body = NULL;
+  const char *msg_out = NULL;
+  int status_out = 0;
+  size_t body_used = 0;
+
+  dir_server_t *ds = NULL;
+  (void) data;
+
+  clear_dir_servers();
+  routerlist_free_all();
+  dirvote_free_all();
+
+  mock_cert = authority_cert_parse_from_string(TEST_CERTIFICATE, NULL);
+
+  /* create a trusted ds */
+  ds = trusted_dir_server_new("ds", "127.0.0.1", 9059, 9060, "", NULL,
+                              V3_DIRINFO, 1.0);
+  tt_assert(ds);
+  dir_server_add(ds);
+
+  /* ds v3_identity_digest is the certificate's identity_key */
+  base16_decode(ds->v3_identity_digest, DIGEST_LEN,
+                TEST_CERT_IDENT_KEY, HEX_DIGEST_LEN);
+
+  tt_int_op(0, OP_EQ, trusted_dirs_load_certs_from_string(TEST_CERTIFICATE,
+    TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST, 1));
+
+  init_mock_options();
+  mock_options->AuthoritativeDir = 1;
+  mock_options->V3AuthoritativeDir = 1;
+  mock_options->TestingV3AuthVotingStartOffset = 0;
+  mock_options->TestingV3AuthInitialVotingInterval = 1;
+  mock_options->TestingV3AuthInitialVoteDelay = 1;
+  mock_options->TestingV3AuthInitialDistDelay = 1;
+
+  time_t now = 1441223455;
+  dirvote_recalculate_timing(mock_options, now-1);
+
+  struct pending_vote_t *vote = dirvote_add_vote(VOTE_BODY_V3, &msg_out,
+                                                 &status_out);
+  tt_assert(vote);
+
+  // move the pending vote to previous vote
+  dirvote_act(mock_options, now+1);
+
+  MOCK(get_my_v3_authority_cert, get_my_v3_authority_cert_m);
+  MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+  conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+  tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
+    GET("/tor/status-vote/current/authority"), NULL, 0));
+
+  fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+                      &body, &body_used, strlen(VOTE_BODY_V3)+1, 0);
+
+  tt_assert(header);
+  tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+  tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+  tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+  tt_assert(strstr(header, "Content-Length: 4135\r\n"));
+
+  tt_str_op(VOTE_BODY_V3, OP_EQ, body);
+
+  done:
+    UNMOCK(connection_write_to_buf_impl_);
+    UNMOCK(get_my_v3_authority_cert);
+    tor_free(conn);
+    tor_free(header);
+    tor_free(mock_cert);
+
+    clear_dir_servers();
+    routerlist_free_all();
+    dirvote_free_all();
+}
+
+#define DIR_HANDLE_CMD(name,flags) \
+  { #name, test_dir_handle_get_##name, (flags), NULL, NULL }
+
+struct testcase_t dir_handle_get_tests[] = {
+  DIR_HANDLE_CMD(not_found, 0),
+  DIR_HANDLE_CMD(bad_request, 0),
+  DIR_HANDLE_CMD(v1_command_not_found, 0),
+  DIR_HANDLE_CMD(v1_command, 0),
+  DIR_HANDLE_CMD(robots_txt, 0),
+  DIR_HANDLE_CMD(bytes_txt, 0),
+  DIR_HANDLE_CMD(rendezvous2_not_found_if_not_encrypted, 0),
+  DIR_HANDLE_CMD(rendezvous2_not_found, 0),
+  DIR_HANDLE_CMD(rendezvous2_on_encrypted_conn_with_invalid_desc_id, 0),
+  DIR_HANDLE_CMD(rendezvous2_on_encrypted_conn_not_well_formed, 0),
+  DIR_HANDLE_CMD(rendezvous2_on_encrypted_conn_success, 0),
+  DIR_HANDLE_CMD(micro_d_not_found, 0),
+  DIR_HANDLE_CMD(micro_d_server_busy, 0),
+  DIR_HANDLE_CMD(micro_d, 0),
+  DIR_HANDLE_CMD(networkstatus_bridges_not_found_without_auth, 0),
+  DIR_HANDLE_CMD(networkstatus_bridges_not_found_wrong_auth, 0),
+  DIR_HANDLE_CMD(networkstatus_bridges, 0),
+  DIR_HANDLE_CMD(server_descriptors_not_found, 0),
+  DIR_HANDLE_CMD(server_descriptors_busy, TT_FORK),
+  DIR_HANDLE_CMD(server_descriptors_all, TT_FORK),
+  DIR_HANDLE_CMD(server_descriptors_authority, TT_FORK),
+  DIR_HANDLE_CMD(server_descriptors_fp, TT_FORK),
+  DIR_HANDLE_CMD(server_descriptors_d, TT_FORK),
+  DIR_HANDLE_CMD(server_keys_bad_req, 0),
+  DIR_HANDLE_CMD(server_keys_busy, 0),
+  DIR_HANDLE_CMD(server_keys_all_not_found, 0),
+  DIR_HANDLE_CMD(server_keys_all, 0),
+  DIR_HANDLE_CMD(server_keys_authority_not_found, 0),
+  DIR_HANDLE_CMD(server_keys_authority, 0),
+  DIR_HANDLE_CMD(server_keys_fp_not_found, 0),
+  DIR_HANDLE_CMD(server_keys_fp, 0),
+  DIR_HANDLE_CMD(server_keys_sk_not_found, 0),
+  DIR_HANDLE_CMD(server_keys_sk, 0),
+  DIR_HANDLE_CMD(server_keys_fpsk_not_found, 0),
+  DIR_HANDLE_CMD(server_keys_fpsk, 0),
+  DIR_HANDLE_CMD(status_vote_current_not_found, 0),
+  DIR_HANDLE_CMD(status_vote_next_not_found, 0),
+  DIR_HANDLE_CMD(status_vote_current_authority_not_found, 0),
+  DIR_HANDLE_CMD(status_vote_current_authority, 0),
+  DIR_HANDLE_CMD(status_vote_next_authority_not_found, 0),
+  DIR_HANDLE_CMD(status_vote_next_authority, 0),
+  DIR_HANDLE_CMD(status_vote_current_consensus_ns_not_enough_sigs, 0),
+  DIR_HANDLE_CMD(status_vote_current_consensus_ns_not_found, 0),
+  DIR_HANDLE_CMD(status_vote_current_consensus_ns_busy, 0),
+  DIR_HANDLE_CMD(status_vote_current_consensus_ns, 0),
+  DIR_HANDLE_CMD(status_vote_current_d_not_found, 0),
+  DIR_HANDLE_CMD(status_vote_next_d_not_found, 0),
+  DIR_HANDLE_CMD(status_vote_d, 0),
+  DIR_HANDLE_CMD(status_vote_next_consensus_not_found, 0),
+  DIR_HANDLE_CMD(status_vote_next_consensus_busy, 0),
+  DIR_HANDLE_CMD(status_vote_next_consensus, 0),
+  DIR_HANDLE_CMD(status_vote_next_consensus_signatures_not_found, 0),
+  DIR_HANDLE_CMD(status_vote_next_consensus_signatures_busy, 0),
+  DIR_HANDLE_CMD(status_vote_next_consensus_signatures, 0),
+  END_OF_TESTCASES
+};
+
diff --git a/src/test/vote_descriptors.inc b/src/test/vote_descriptors.inc
new file mode 100644
index 0000000..c5ce21f
--- /dev/null
+++ b/src/test/vote_descriptors.inc
@@ -0,0 +1,94 @@
+const char* VOTE_BODY_V3 = 
+"network-status-version 3\n"
+"vote-status vote\n"
+"consensus-methods 13 14 15 16 17 18 19 20 21\n"
+"published 2015-09-02 19:34:15\n"
+"valid-after 2015-09-02 19:50:55\n"
+"fresh-until 2015-09-02 20:07:38\n"
+"valid-until 2015-09-02 20:24:15\n"
+"voting-delay 100 250\n"
+"client-versions 0.1.2.14,0.1.2.17\n"
+"server-versions 0.1.2.10,0.1.2.15,0.1.2.16\n"
+"known-flags Authority Exit Fast Guard MadeOfCheese MadeOfTin Running Stable V2Dir Valid\n"
+"flag-thresholds stable-uptime=0 stable-mtbf=0 fast-speed=0 guard-wfu=0.000% guard-tk=0 guard-bw-inc-exits=0 guard-bw-exc-exits=0 enough-mtbf=0 ignoring-advertised-bws=0\n"
+"params circuitwindow=80 foo=660\n"
+"dir-source Voter3 D867ACF56A9D229B35C25F0090BC9867E906BE69 3.4.5.6 3.4.5.6 80 9000\n"
+"contact voter at example.com\n"
+"legacy-dir-key 4141414141414141414141414141414141414141\n"
+"dir-key-certificate-version 3\n"
+"fingerprint D867ACF56A9D229B35C25F0090BC9867E906BE69\n"
+"dir-key-published 2008-12-12 18:07:24\n"
+"dir-key-expires 2009-12-12 18:07:24\n"
+"dir-identity-key\n"
+"-----BEGIN RSA PUBLIC KEY-----\n"
+"MIIBigKCAYEAveMpKlw8oD1YqFqpJchuwSR82BDhutbqgHiez3QO9FmzOctJpV+Y\n"
+"mpTYIJLS/qC+4GBKFF1VK0C4SoBrS3zri0qdXdE+vBGcyrxrjMklpxoqSKRY2011\n"
+"4eqYPghKlo5RzuqteBclGCHyNxWjUJeRKDWgvh+U/gr2uYM6fRm5q0fCzg4aECE7\n"
+"VP6fDGZrMbQI8jHpiMSoC9gkUASNEa6chLInlnP8/H5qUEW4TB9CN/q095pefuwL\n"
+"P+F+1Nz5hnM7fa5XmeMB8iM4RriUmOQlLBZgpQBMpEfWMIPcR9F1Gh3MxERqqUcH\n"
+"tmij+IZdeXg9OkCXykcabaYIhZD3meErn9Tax4oA/THduLfgli9zM0ExwzH1OooN\n"
+"L8rIcJ+2eBo3bQiQUbdYW71sl9w7nSPtircbJUa1mUvWYLPWQxFliPiQSetgJLMj\n"
+"VQqtPmV2hvN2Xk3lLfJO50qMTK7w7Gsaw8UtV4YDM1Hcjp/hQaIB1xfwhXgl+eUU\n"
+"btUa4c+cUTjHAgMBAAE=\n"
+"-----END RSA PUBLIC KEY-----\n"
+"dir-signing-key\n"
+"-----BEGIN RSA PUBLIC KEY-----\n"
+"MIGJAoGBALPSUInyuEu6NV3NjozplaniIEBzQXEjv1x9/+mqnwZABpYVmuy9A8nx\n"
+"eoyY3sZFsnYwNW/IZjAgG23pEmevu3F+L4myMjjaa6ORl3MgRYQ4gmuFqpefrGdm\n"
+"ywRCleh2JerkQ4VxOuq10dn/abITzLyaZzMw30KXWp5pxKXOLtxFAgMBAAE=\n"
+"-----END RSA PUBLIC KEY-----\n"
+"dir-key-crosscert\n"
+"-----BEGIN ID SIGNATURE-----\n"
+"FTBJNR/Hlt4T53yUMp1r/QCSMCpkHJCbYBT0R0pvYqhqFfYN5qHRSICRXaFFImIF\n"
+"0DGWmwRza6DxPKNzkm5/b7I0de9zJW1jNNdQAQK5xppAtQcAafRdu8cBonnmh9KX\n"
+"k1NrAK/X00FYywju3yl/SxCn1GddVNkHYexEudmJMPM=\n"
+"-----END ID SIGNATURE-----\n"
+"dir-key-certification\n"
+"-----BEGIN SIGNATURE-----\n"
+"pjWguLFBfELZDc6DywL6Do21SCl7LcutfpM92MEn4WYeSNcTXNR6lRX7reOEJk4e\n"
+"NwEaMt+Hl7slgeR5wjnW3OmMmRPZK9bquNWbfD+sAOV9bRFZTpXIdleAQFPlwvMF\n"
+"z/Gzwspzn4i2Yh6hySShrctMmW8YL3OM8LsBXzBhp/rG2uHlsxmIsc13DA6HWt61\n"
+"ffY72uNE6KckDGsQ4wPGP9q69y6g+X+TNio1KPbsILbePv6EjbO+rS8FiS4njPlg\n"
+"SPYry1RaUvxzxTkswIzdE1tjJrUiqpbWlTGxrH9N4OszoLm45Pc784KLULrjKIoi\n"
+"Q+vRsGrcMBAa+kDowWU6H1ryKR7KOhzRTcf2uqLE/W3ezaRwmOG+ETmoVFwbhk2X\n"
+"OlbXEM9fWP+INvFkr6Z93VYL2jGkCjV7e3xXmre/Lb92fUcYi6t5dwzfV8gJnIoG\n"
+"eCHd0K8NrQK0ipVk/7zcPDKOPeo9Y5aj/f6X/pDHtb+Dd5sT+l82G/Tqy4DIYUYR\n"
+"-----END SIGNATURE-----\n"
+"r router2 AwMDAwMDAwMDAwMDAwMDAwMDAwM Tk5OTk5OTk5OTk5OTk5OTk5OTk4 2015-09-02 19:09:15 153.0.136.1 443 8000\n"
+"s Running V2Dir\n"
+"v 0.1.2.14\n"
+"w Bandwidth=30 Measured=30\n"
+"p reject 1-65535\n"
+"id ed25519 none\n"
+"m 9,10,11,12,13,14,15,16,17 sha256=xyzajkldsdsajdadlsdjaslsdksdjlsdjsdaskdaaa0\n"
+"r router1 BQUFBQUFBQUFBQUFBQUFBQUFBQU TU1NTU1NTU1NTU1NTU1NTU1NTU0 2015-09-02 19:17:35 153.0.153.1 443 0\n"
+"a [1:2:3::4]:4711\n"
+"s Exit Fast Guard Running Stable Valid\n"
+"v 0.2.0.5\n"
+"w Bandwidth=120 Measured=120\n"
+"p reject 1-65535\n"
+"id ed25519 none\n"
+"m 9,10,11,12,13,14,15,16,17 sha256=xyzajkldsdsajdadlsdjaslsdksdjlsdjsdaskdaaa1\n"
+"r router3 MzMzMzMzMzMzMzMzMzMzMzMzMzM T09PT09PT09PT09PT09PT09PT08 2015-09-02 19:17:35 170.0.153.1 400 9999\n"
+"s Authority Exit Fast Guard Running Stable V2Dir Valid\n"
+"v 0.1.0.3\n"
+"w Bandwidth=120\n"
+"p reject 1-65535\n"
+"id ed25519 none\n"
+"m 9,10,11,12,13,14,15,16,17 "
+"sha256=xyzajkldsdsajdadlsdjaslsdksdjlsdjsdaskdaaa2\n"
+"r router4 NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ Ly8vLy8vLy8vLy8vLy8vLy8vLy8 2015-09-02 19:17:35 192.0.2.3 500 1999\n"
+"s Running V2Dir\n"
+"v 0.1.6.3\n"
+"w Bandwidth=30\n"
+"p reject 1-65535\n"
+"id ed25519 none\n"
+"m 9,10,11,12,13,14,15,16,17 sha256=xyzajkldsdsajdadlsdjaslsdksdjlsdjsdaskdaaa3\n"
+"directory-footer\n"
+"directory-signature D867ACF56A9D229B35C25F0090BC9867E906BE69 CBF56A83368A5150F1A9AAADAFB4D77F8C4170E2\n"
+"-----BEGIN SIGNATURE-----\n"
+"AHiWcHe+T3XbnlQqvqSAk6RY3XmEy1+hM2u9Xk6BNi7BpQkEQM1f0vzRpgn5Dnf2\n"
+"TXQWGUq9Z7jdSVnzWT3xqPA4zjw6eZkj+DKUtwq+oEDZGlf8eHTFmr0NAWfwZbk9\n"
+"NAjbMTUXUP37N2XAZwkoCWwFCrrfMwXrL7OhZbj7ifo=\n"
+"-----END SIGNATURE-----\n";
+





More information about the tor-commits mailing list