Add route printer
diff --git a/src/crow_getroutes_test.cpp b/src/crow_getroutes_test.cpp
new file mode 100644
index 0000000..6a9f538
--- /dev/null
+++ b/src/crow_getroutes_test.cpp
@@ -0,0 +1,38 @@
+#include <crow/app.h>
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+using namespace crow;
+using namespace std;
+
+TEST(GetRoutes, TestEmptyRoutes) {
+  SimpleApp app;
+  decltype(app)::server_t server(&app, "127.0.0.1", 45451);
+
+  EXPECT_THAT(app.get_routes(), testing::IsEmpty());
+}
+
+// Tests that static urls are correctly passed
+TEST(GetRoutes, TestOneRoute) {
+  SimpleApp app;
+  decltype(app)::server_t server(&app, "127.0.0.1", 45451);
+  CROW_ROUTE(app, "/")([]() { return 200; });
+
+  EXPECT_THAT(app.get_routes(), testing::ElementsAre("/"));
+}
+
+// Tests that static urls are correctly passed
+TEST(GetRoutes, TestlotsOfRoutes) {
+  SimpleApp app;
+  decltype(app)::server_t server(&app, "127.0.0.1", 45451);
+  CROW_ROUTE(app, "/")([]() { return 200; });
+  CROW_ROUTE(app, "/foo")([]() { return 200; });
+  CROW_ROUTE(app, "/bar")([]() { return 200; });
+  CROW_ROUTE(app, "/baz")([]() { return 200; });
+  CROW_ROUTE(app, "/boo")([]() { return 200; });
+  CROW_ROUTE(app, "/moo")([]() { return 200; });
+
+  EXPECT_THAT(app.get_routes(),
+              testing::UnorderedElementsAre("/", "/foo", "/bar", "/baz", "/boo",
+                                            "/moo"));
+}
\ No newline at end of file
diff --git a/src/security_headers_middleware_test.cpp b/src/security_headers_middleware_test.cpp
new file mode 100644
index 0000000..2364ab5
--- /dev/null
+++ b/src/security_headers_middleware_test.cpp
@@ -0,0 +1,74 @@
+#include <crow/app.h>
+#include <gmock/gmock.h>
+#include <security_headers_middleware.hpp>
+#include "gtest/gtest.h"
+
+using namespace crow;
+using namespace std;
+
+// Tests that the security headers are added correctly
+TEST(SecurityHeaders, TestHeadersExist) {
+  App<SecurityHeadersMiddleware> app;
+  app.bindaddr("127.0.0.1").port(45451);
+  CROW_ROUTE(app, "/")([]() { return 200; });
+  auto _ = async(launch::async, [&] { app.run(); });
+
+  asio::io_service is;
+  std::array<char, 2048> buf;
+  std::string sendmsg;
+
+  {
+    // Retry a couple of times waiting for the server to come up
+    // TODO(ed)  This is really unfortunate, and should use some form of mock
+    asio::ip::tcp::socket c(is);
+    for (int i = 0; i < 200; i++) {
+      try {
+        c.connect(asio::ip::tcp::endpoint(
+            asio::ip::address::from_string("127.0.0.1"), 45451));
+        c.close();
+        break;
+      } catch (std::exception e) {
+        // do nothing.  We expect this to fail while the server is starting up
+      }
+    }
+  }
+
+  // Test correct login credentials
+  sendmsg = "GET /\r\n\r\n";
+
+  asio::ip::tcp::socket c(is);
+  c.connect(asio::ip::tcp::endpoint(asio::ip::address::from_string("127.0.0.1"),
+                                    45451));
+  c.send(asio::buffer(sendmsg));
+  auto received_count = c.receive(asio::buffer(buf));
+  c.close();
+  auto return_code = std::string(&buf[9], &buf[12]);
+  EXPECT_EQ("200", return_code);
+  std::string response(std::begin(buf), std::end(buf));
+
+  // This is a routine to split strings until a newline is hit
+  // TODO(ed) this should really use the HTTP parser
+  std::vector<std::string> headers;
+  std::string::size_type pos = 0;
+  std::string::size_type prev = 0;
+  while ((pos = response.find("\r\n", prev)) != std::string::npos) {
+    auto this_string = response.substr(prev, pos - prev);
+    if (this_string == "") {
+      break;
+    }
+    headers.push_back(this_string);
+    prev = pos + 2;
+  }
+  headers.push_back(response.substr(prev));
+
+  EXPECT_EQ(headers[0], "HTTP/1.1 200 OK");
+  EXPECT_THAT(headers, ::testing::Contains("Strict-Transport-Security: "
+                                           "max-age=31536000; "
+                                           "includeSubdomains; preload"));
+  EXPECT_THAT(headers, ::testing::Contains("X-UA-Compatible: IE=11"));
+  EXPECT_THAT(headers, ::testing::Contains("X-Frame-Options: DENY"));
+  EXPECT_THAT(headers, ::testing::Contains("X-XSS-Protection: 1; mode=block"));
+  EXPECT_THAT(headers, ::testing::Contains(
+                           "X-Content-Security-Policy: default-src 'self'"));
+  app.stop();
+}
diff --git a/src/token_authorization_middleware.cpp b/src/token_authorization_middleware.cpp
index bf5efe1..a5ecd2d 100644
--- a/src/token_authorization_middleware.cpp
+++ b/src/token_authorization_middleware.cpp
@@ -75,6 +75,7 @@
         x["token"] = auth_token;
 
         res.write(json::dump(x));
+        res.add_header("Content-Type", "application/json");
         res.end();
       } else {
         return_unauthorized();
diff --git a/src/token_authorization_middleware_test.cpp b/src/token_authorization_middleware_test.cpp
index 9f8e626..68efe8f 100644
--- a/src/token_authorization_middleware_test.cpp
+++ b/src/token_authorization_middleware_test.cpp
@@ -1,6 +1,7 @@
 #include "token_authorization_middleware.hpp"
 #include <crow/app.h>
 #include "gtest/gtest.h"
+#include "gmock/gmock.h"
 
 using namespace crow;
 using namespace std;
@@ -217,11 +218,31 @@
       "\"password\": \"dude\"}\r\n";
   {
     send_to_localhost(sendmsg);
-    auto return_code = std::string(&buf[9], &buf[12]);
-    EXPECT_EQ("200", return_code);
+    std::string response(std::begin(buf), std::end(buf));
+    // This is a routine to split strings until a newline is hit
+    // TODO(ed) this should really use the HTTP parser
+    std::vector<std::string> headers;
+    std::string::size_type pos = 0;
+    std::string::size_type prev = 0;
+    int content_length = 0;
+    std::string content_encoding("");
+    while ((pos = response.find("\r\n", prev)) != std::string::npos) {
+      auto this_string = response.substr(prev, pos - prev);
+      if (this_string == "") {
+        prev = pos + 2;
+        break;
+      }
+
+      headers.push_back(this_string);
+      prev = pos + 2;
+    }
+    EXPECT_EQ(headers[0], "HTTP/1.1 200 OK");
+    EXPECT_THAT(headers, testing::Contains("Content-Type: application/json"));
+    auto http_content = response.substr(prev);
   }
 
 
+
   // Try to use those login credentials to access a resource
   sendmsg =
       "GET /\r\nAuthorization: token\r\n\r\n{\"username\": \"dude\", "
diff --git a/src/udpclient.cpp b/src/udpclient.cpp
index 5f04a9a..772739b 100644
--- a/src/udpclient.cpp
+++ b/src/udpclient.cpp
@@ -59,7 +59,7 @@
     uint8_t session_id = 0;
 
     uint8_t seq_number2 = 0;
-    uint8_t seq_number_lun = seq_number << 2 + lun;
+    uint8_t seq_number_lun = (seq_number << 2) + lun;
 
     uint8_t seq2 = 0xff;  //????
     uint8_t rmcp_class = 0x07;
diff --git a/src/webassets_test.cpp b/src/webassets_test.cpp
new file mode 100644
index 0000000..2157f03
--- /dev/null
+++ b/src/webassets_test.cpp
@@ -0,0 +1,207 @@
+#include <crow/app.h>
+#include <gmock/gmock.h>
+#include <zlib.h>
+#include <boost/algorithm/string/predicate.hpp>
+#include <boost/iostreams/copy.hpp>
+#include <boost/iostreams/filter/gzip.hpp>
+#include <boost/iostreams/filtering_streambuf.hpp>
+#include <boost/lexical_cast.hpp>
+#include <sstream>
+#include <webassets.hpp>
+#include "gtest/gtest.h"
+using namespace crow;
+using namespace std;
+using namespace testing;
+
+bool gzipInflate(const std::string& compressedBytes,
+                 std::string& uncompressedBytes) {
+  if (compressedBytes.size() == 0) {
+    uncompressedBytes = compressedBytes;
+    return true;
+  }
+
+  uncompressedBytes.clear();
+
+  unsigned full_length = compressedBytes.size();
+  unsigned half_length = compressedBytes.size() / 2;
+
+  unsigned uncompLength = full_length;
+  char* uncomp = (char*)calloc(sizeof(char), uncompLength);
+
+  z_stream strm;
+  strm.next_in = (Bytef*)compressedBytes.c_str();
+  strm.avail_in = compressedBytes.size();
+  strm.total_out = 0;
+  strm.zalloc = Z_NULL;
+  strm.zfree = Z_NULL;
+
+  bool done = false;
+
+  if (inflateInit2(&strm, (16 + MAX_WBITS)) != Z_OK) {
+    free(uncomp);
+    return false;
+  }
+
+  while (!done) {
+    // If our output buffer is too small
+    if (strm.total_out >= uncompLength) {
+      // Increase size of output buffer
+      char* uncomp2 = (char*)calloc(sizeof(char), uncompLength + half_length);
+      memcpy(uncomp2, uncomp, uncompLength);
+      uncompLength += half_length;
+      free(uncomp);
+      uncomp = uncomp2;
+    }
+
+    strm.next_out = (Bytef*)(uncomp + strm.total_out);
+    strm.avail_out = uncompLength - strm.total_out;
+
+    // Inflate another chunk.
+    int err = inflate(&strm, Z_SYNC_FLUSH);
+    if (err == Z_STREAM_END)
+      done = true;
+    else if (err != Z_OK) {
+      break;
+    }
+  }
+
+  if (inflateEnd(&strm) != Z_OK) {
+    free(uncomp);
+    return false;
+  }
+
+  for (size_t i = 0; i < strm.total_out; ++i) {
+    uncompressedBytes += uncomp[i];
+  }
+  free(uncomp);
+  return true;
+}
+
+
+
+
+// Tests static files are loaded correctly
+TEST(Webassets, StaticFilesFixedRoutes) {
+  std::array<char, 2048> buf;
+  SimpleApp app;
+  webassets::request_routes(app);
+  Server<SimpleApp> server(&app, "127.0.0.1", 45451);
+  auto _ = async(launch::async, [&] { server.run(); });
+
+  // Get the homepage
+  std::string sendmsg = "GET /\r\n\r\n";
+
+  asio::io_service is;
+
+  asio::ip::tcp::socket c(is);
+  c.connect(asio::ip::tcp::endpoint(asio::ip::address::from_string("127.0.0.1"),
+                                    45451));
+
+  c.send(asio::buffer(sendmsg));
+
+  c.receive(asio::buffer(buf, 2048));
+  c.close();
+
+  std::string response(std::begin(buf), std::end(buf));
+  // This is a routine to split strings until a newline is hit
+  // TODO(ed) this should really use the HTTP parser
+  std::vector<std::string> headers;
+  std::string::size_type pos = 0;
+  std::string::size_type prev = 0;
+  int content_length = 0;
+  std::string content_encoding("");
+  while ((pos = response.find("\r\n", prev)) != std::string::npos) {
+    auto this_string = response.substr(prev, pos - prev);
+    if (this_string == "") {
+      prev = pos + 2;
+      break;
+    }
+
+    if (boost::starts_with(this_string, "Content-Length: ")) {
+      content_length = boost::lexical_cast<int>(this_string.substr(16));
+      // TODO(ed) This is an unfortunate test, but it's all we have at this
+      // point
+      // Realistically, the index.html will be more than 500 bytes.  This
+      // test will need to be improved at some point
+      EXPECT_GT(content_length, 500);
+    }
+    if (boost::starts_with(this_string, "Content-Encoding: ")) {
+      content_encoding = this_string.substr(18);
+    }
+
+    headers.push_back(this_string);
+    prev = pos + 2;
+  }
+
+  auto http_content = response.substr(prev);
+  // TODO(ed) ideally the server should support non-compressed gzip assets.
+  // Once this
+  // occurs, this line will be obsolete
+  std::string ungziped_content = http_content;
+  if (content_encoding == "gzip") {
+    EXPECT_TRUE(gzipInflate(http_content, ungziped_content));
+  }
+
+  EXPECT_EQ(headers[0], "HTTP/1.1 200 OK");
+  EXPECT_THAT(headers,
+              ::testing::Contains("Content-Type: text/html;charset=UTF-8"));
+
+  EXPECT_EQ(ungziped_content.substr(0, 21), "<!DOCTYPE html>\n<html");
+
+  server.stop();
+}
+
+
+
+// Tests static files are loaded correctly
+TEST(Webassets, EtagIsSane) {
+  std::array<char, 2048> buf;
+  SimpleApp app;
+  webassets::request_routes(app);
+  Server<SimpleApp> server(&app, "127.0.0.1", 45451);
+  auto _ = async(launch::async, [&] { server.run(); });
+
+  // Get the homepage
+  std::string sendmsg = "GET /\r\n\r\n";
+
+  asio::io_service is;
+
+  asio::ip::tcp::socket c(is);
+  c.connect(asio::ip::tcp::endpoint(asio::ip::address::from_string("127.0.0.1"),
+                                    45451));
+
+  c.send(asio::buffer(sendmsg));
+
+  c.receive(asio::buffer(buf, 2048));
+  c.close();
+
+  std::string response(std::begin(buf), std::end(buf));
+  // This is a routine to split strings until a newline is hit
+  // TODO(ed) this should really use the HTTP parser
+  std::vector<std::string> headers;
+  std::string::size_type pos = 0;
+  std::string::size_type prev = 0;
+  int content_length = 0;
+  std::string content_encoding("");
+  while ((pos = response.find("\r\n", prev)) != std::string::npos) {
+    auto this_string = response.substr(prev, pos - prev);
+    if (this_string == "") {
+      prev = pos + 2;
+      break;
+    }
+
+    if (boost::starts_with(this_string, "ETag: ")) {
+      auto etag = this_string.substr(6);
+      // ETAG should not be blank
+      EXPECT_NE(etag, "");
+      // SHa1 is 20 characters long
+      EXPECT_EQ(etag.size(), 40);
+      EXPECT_THAT(etag, MatchesRegex("^[a-f0-9]+$"));
+    }
+
+    headers.push_back(this_string);
+    prev = pos + 2;
+  }
+
+  server.stop();
+}
\ No newline at end of file
diff --git a/src/webserver_main.cpp b/src/webserver_main.cpp
index 32a1192..cf8500f 100644
--- a/src/webserver_main.cpp
+++ b/src/webserver_main.cpp
@@ -58,7 +58,7 @@
   crow::kvm::request_routes(app);
 
   crow::logger::setLogLevel(crow::LogLevel::INFO);
-
+  app.debug_print();
   CROW_ROUTE(app, "/systeminfo")
   ([]() {