Squashed 'third_party/seasocks/' content from commit 016dc60

Change-Id: I195fa5bfd0c0e3cc66fbbefcc7b5170bafcf7a36
git-subtree-dir: third_party/seasocks
git-subtree-split: 016dc60b247e0d1d563aea6d22a9075e6884ab9f
diff --git a/src/test/c/ConnectionTests.cpp b/src/test/c/ConnectionTests.cpp
new file mode 100644
index 0000000..ee73681
--- /dev/null
+++ b/src/test/c/ConnectionTests.cpp
@@ -0,0 +1,89 @@
+// Copyright (c) 2013, Matt Godbolt
+// All rights reserved.
+// 
+// Redistribution and use in source and binary forms, with or without 
+// modification, are permitted provided that the following conditions are met:
+// 
+// Redistributions of source code must retain the above copyright notice, this 
+// list of conditions and the following disclaimer.
+// 
+// Redistributions in binary form must reproduce the above copyright notice, 
+// this list of conditions and the following disclaimer in the documentation 
+// and/or other materials provided with the distribution.
+// 
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+// POSSIBILITY OF SUCH DAMAGE.
+
+#include "MockServerImpl.h"
+#include "seasocks/Connection.h"
+#include "seasocks/IgnoringLogger.h"
+
+#include <gmock/gmock.h>
+
+#include <iostream>
+#include <sstream>
+#include <string.h>
+#include <string>
+
+using namespace seasocks;
+
+class TestHandler: public WebSocket::Handler {
+public:
+    int _stage;
+    TestHandler() :
+        _stage(0) {
+    }
+    ~TestHandler() {
+        if (_stage != 2) {
+            ADD_FAILURE() << "Invalid state";
+        }
+    }
+    virtual void onConnect(WebSocket*) {
+    }
+    virtual void onData(WebSocket*, const char* data) {
+        if (_stage == 0) {
+            ASSERT_STREQ(data, "a");
+        } else if (_stage == 1) {
+            ASSERT_STREQ(data, "b");
+        } else {
+            FAIL() << "unexpected state";
+        }
+        ++_stage;
+    }
+    virtual void onDisconnect(WebSocket*) {
+    }
+};
+
+TEST(ConnectionTests, shouldBreakHixieMessagesApartInSameBuffer) {
+    sockaddr_in addr = { AF_INET, 0x1234, { 0x01020304 } };
+    std::shared_ptr<Logger> logger(new IgnoringLogger);
+    testing::NiceMock<MockServerImpl> mockServer;
+    Connection connection(logger, mockServer, -1, addr);
+    connection.setHandler(
+            std::shared_ptr<WebSocket::Handler>(new TestHandler));
+    uint8_t foo[] = { 0x00, 'a', 0xff, 0x00, 'b', 0xff };
+    connection.getInputBuffer().assign(&foo[0], &foo[sizeof(foo)]);
+    connection.handleHixieWebSocket();
+    SUCCEED();
+}
+
+TEST(ConnectionTests, shouldAcceptMultipleConnectionTypes) {
+    sockaddr_in addr = { AF_INET, 0x1234, { 0x01020304 } };
+    std::shared_ptr<Logger> logger(new IgnoringLogger);
+    testing::NiceMock<MockServerImpl> mockServer;
+    Connection connection(logger, mockServer, -1, addr);
+    const uint8_t message[] = "GET /ws-test HTTP/1.1\r\nConnection: keep-alive, Upgrade\r\nUpgrade: websocket\r\n\r\n";
+    connection.getInputBuffer().assign(&message[0], &message[sizeof(message)]);
+    EXPECT_CALL(mockServer, getWebSocketHandler(testing::StrEq("/ws-test")))
+        .WillOnce(testing::Return(std::shared_ptr<WebSocket::Handler>()));
+    connection.handleNewData();
+}
diff --git a/src/test/c/CrackedUriTests.cpp b/src/test/c/CrackedUriTests.cpp
new file mode 100644
index 0000000..a9243f6
--- /dev/null
+++ b/src/test/c/CrackedUriTests.cpp
@@ -0,0 +1,147 @@
+// Copyright (c) 2013, Matt Godbolt
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+#include "seasocks/util/CrackedUri.h"
+
+#include <gmock/gmock.h>
+
+using namespace testing;
+using namespace seasocks;
+using namespace std;
+
+namespace {
+
+TEST(CrackedUriTests, shouldHandleRoot) {
+    CrackedUri uri("/");
+    EXPECT_EQ(vector<string>(), uri.path());
+    EXPECT_TRUE(uri.queryParams().empty());
+}
+
+TEST(CrackedUriTests, shouldHandleTopLevel) {
+    CrackedUri uri("/monkey");
+    vector<string> expected = { "monkey" };
+    EXPECT_EQ(expected, uri.path());
+    EXPECT_TRUE(uri.queryParams().empty());
+}
+
+TEST(CrackedUriTests, shouldHandleSimplePaths) {
+    CrackedUri uri("/foo/bar/baz/bungo");
+    vector<string> expected = { "foo", "bar", "baz", "bungo" };
+    EXPECT_EQ(expected, uri.path());
+    EXPECT_TRUE(uri.queryParams().empty());
+}
+
+TEST(CrackedUriTests, shouldTreatTrailingSlashAsNewPage) {
+    CrackedUri uri("/ooh/a/directory/");
+    vector<string> expected = { "ooh", "a", "directory", "" };
+    EXPECT_EQ(expected, uri.path());
+    EXPECT_TRUE(uri.queryParams().empty());
+}
+
+TEST(CrackedUriTests, shouldHandleRootWithQuery) {
+    CrackedUri uri("/?a=spotch");
+    EXPECT_EQ(vector<string>(), uri.path());
+    ASSERT_FALSE(uri.queryParams().empty());
+    EXPECT_TRUE(uri.hasParam("a"));
+    EXPECT_EQ("spotch", uri.queryParam("a"));
+}
+
+TEST(CrackedUriTests, shouldHonorDefaults) {
+    CrackedUri uri("/?a=spotch");
+    EXPECT_EQ("monkey", uri.queryParam("notThere", "monkey"));
+}
+
+TEST(CrackedUriTests, shouldHandleEmptyParams) {
+    CrackedUri uri("/?a&b&c");
+    EXPECT_EQ("", uri.queryParam("a", "notEmptyDefault"));
+    EXPECT_EQ("", uri.queryParam("b", "notEmptyDefault"));
+    EXPECT_EQ("", uri.queryParam("c", "notEmptyDefault"));
+}
+
+TEST(CrackedUriTests, shouldHandleDuplicateParams) {
+    CrackedUri uri("/?a=a&q=10&q=5&z=yibble&q=100&q=blam");
+    vector<string> expected = { "10", "5", "100", "blam" };
+    sort(expected.begin(), expected.end());
+    auto params = uri.allQueryParams("q");
+    sort(params.begin(), params.end());
+    EXPECT_EQ(expected, params);
+}
+
+
+TEST(CrackedUriTests, shouldHandlePathWithQuery) {
+    CrackedUri uri("/badger/badger/badger/mushroom?q=snake");
+    vector<string> expected = { "badger", "badger", "badger", "mushroom" };
+    EXPECT_EQ(expected, uri.path());
+    ASSERT_FALSE(uri.queryParams().empty());
+    EXPECT_TRUE(uri.hasParam("q"));
+    EXPECT_EQ("snake", uri.queryParam("q"));
+}
+
+TEST(CrackedUriTests, shouldUnescapePaths) {
+    CrackedUri uri("/foo+bar/baz%2f/%40%4F");
+    vector<string> expected = { "foo bar", "baz/", "@O" };
+    EXPECT_EQ(expected, uri.path());
+}
+
+TEST(CrackedUriTests, shouldUnescapeQueries) {
+    CrackedUri uri("/?q=a+b&t=%20%20&p=%41");
+    EXPECT_EQ("a b", uri.queryParam("q"));
+    EXPECT_EQ("  ", uri.queryParam("t"));
+    EXPECT_EQ("A", uri.queryParam("p"));
+}
+
+
+TEST(CrackedUriTests, shouldThrowOnRubbish) {
+    EXPECT_THROW(CrackedUri(""), exception);
+    EXPECT_THROW(CrackedUri("rubbish"), exception);
+    EXPECT_THROW(CrackedUri("../../.."), exception);
+    EXPECT_THROW(CrackedUri("/?a===b"), exception);
+    EXPECT_THROW(CrackedUri("/%"), exception);
+    EXPECT_THROW(CrackedUri("/%a"), exception);
+    EXPECT_THROW(CrackedUri("/%qq"), exception);
+}
+
+TEST(CrackedUriTests, shouldShift) {
+    CrackedUri uri("/a/b/c.html");
+    vector<string> expected1 = {"a", "b", "c.html"};
+    EXPECT_EQ(expected1, uri.path());
+
+    uri = uri.shift();
+    vector<string> expected2 = {"b", "c.html"};
+    EXPECT_EQ(expected2, uri.path());
+
+    uri = uri.shift();
+    vector<string> expected3 = {"c.html"};
+    EXPECT_EQ(expected3, uri.path());
+
+    uri = uri.shift();
+    vector<string> expected4 = {""};
+    EXPECT_EQ(expected4, uri.path());
+
+    uri = uri.shift();
+    EXPECT_EQ(expected4, uri.path());
+}
+
+}
diff --git a/src/test/c/HeaderMapTests.cpp b/src/test/c/HeaderMapTests.cpp
new file mode 100644
index 0000000..a889739
--- /dev/null
+++ b/src/test/c/HeaderMapTests.cpp
@@ -0,0 +1,49 @@
+#include "internal/HeaderMap.h"
+
+#include "internal/Config.h"
+
+#include <gmock/gmock.h>
+
+using namespace seasocks;
+
+namespace {
+
+void emplace(HeaderMap &map, const char *header, const char *value) {
+#if HAVE_UNORDERED_MAP_EMPLACE
+    map.emplace(header, value);
+#else
+    map.insert(std::make_pair(header, value));
+#endif
+}
+
+TEST(HeaderMapTests, shouldConstruct) {
+    HeaderMap map;
+    EXPECT_TRUE(map.empty());
+}
+
+TEST(HeaderMapTests, shouldStoreAndRetrieve) {
+    HeaderMap map;
+    emplace(map, "Foo", "Bar");
+    EXPECT_EQ(1, map.size());
+    EXPECT_EQ("Bar", map.at("Foo"));
+    emplace(map, "Baz", "Moo");
+    EXPECT_EQ(2, map.size());
+    EXPECT_EQ("Bar", map.at("Foo"));
+    EXPECT_EQ("Moo", map.at("Baz"));
+}
+
+TEST(HeaderMapTests, shouldBeCaseInsensitive) {
+    HeaderMap map;
+    emplace(map, "Foo", "Bar");
+    EXPECT_EQ("Bar", map.at("FOO"));
+    EXPECT_EQ("Bar", map.at("foO"));
+}
+
+TEST(HeaderMapTests, shouldPreserveOriginalCase) {
+    HeaderMap map;
+    emplace(map, "Foo", "Bar");
+    auto it = map.find("Foo");
+    EXPECT_EQ("Foo", it->first);
+}
+
+}
diff --git a/src/test/c/HtmlTests.cpp b/src/test/c/HtmlTests.cpp
new file mode 100644
index 0000000..1df7156
--- /dev/null
+++ b/src/test/c/HtmlTests.cpp
@@ -0,0 +1,74 @@
+// Copyright (c) 2013, Matt Godbolt
+// All rights reserved.
+// 
+// Redistribution and use in source and binary forms, with or without 
+// modification, are permitted provided that the following conditions are met:
+// 
+// Redistributions of source code must retain the above copyright notice, this 
+// list of conditions and the following disclaimer.
+// 
+// Redistributions in binary form must reproduce the above copyright notice, 
+// this list of conditions and the following disclaimer in the documentation 
+// and/or other materials provided with the distribution.
+// 
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+// POSSIBILITY OF SUCH DAMAGE.
+
+#include "seasocks/util/Html.h"
+
+#include <gmock/gmock.h>
+
+using namespace seasocks;
+using namespace seasocks::html;
+
+TEST(HtmlTests, shouldRenderASimpleDocument) {
+    auto document = html::html(head(title("Hello")), body(h1("A header"), "This is a document"));
+    EXPECT_EQ("<html><head><title>Hello</title></head><body><h1>A header</h1>This is a document</body></html>", document.str());
+}
+
+TEST(HtmlTests, shouldAllowAppending) {
+    auto list = ul();
+    list << li("Element 1") << li("Element 2");
+    EXPECT_EQ("<ul><li>Element 1</li><li>Element 2</li></ul>", list.str());
+}
+
+TEST(HtmlTests, shouldAllowCommaSeparation) {
+    auto list = ul(li("Element 1"), li("Element 2"));
+    EXPECT_EQ("<ul><li>Element 1</li><li>Element 2</li></ul>", list.str());
+}
+
+TEST(HtmlTests, shouldHandleAttributes) {
+    auto elem = span("Some text").clazz("class");
+    EXPECT_EQ("<span class=\"class\">Some text</span>", elem.str());
+}
+
+TEST(HtmlTests, shouldAppendLikeAStreamFromEmpty) {
+    auto elem = empty() << "Text " << 1 << ' ' << 10.23;
+    EXPECT_EQ("Text 1 10.23", elem.str());
+}
+
+TEST(HtmlTests, shouldNotNeedExtraMarkupForTextNodes) {
+    auto elem = text("This ") << text("is") << text(" a test") << ".";
+    EXPECT_EQ("This is a test.", elem.str());
+}
+
+TEST(HtmlTests, shouldWorkWithAnchors) {
+    auto elem = a("http://google.com/?q=badgers", div(span("foo")));
+    EXPECT_EQ("<a href=\"http://google.com/?q=badgers\"><div><span>foo</span></div></a>", elem.str());
+}
+
+TEST(HtmlTests, shouldAddAll) {
+    std::vector<std::string> strings = { "Hi", "Moo", "Foo" };
+    auto list = ul().addAll(strings, [](const std::string& s) { return li(s); });
+    EXPECT_EQ("<ul><li>Hi</li><li>Moo</li><li>Foo</li></ul>", list.str());
+
+}
diff --git a/src/test/c/HybiTests.cpp b/src/test/c/HybiTests.cpp
new file mode 100644
index 0000000..ab270eb
--- /dev/null
+++ b/src/test/c/HybiTests.cpp
@@ -0,0 +1,145 @@
+// Copyright (c) 2013, Matt Godbolt
+// All rights reserved.
+// 
+// Redistribution and use in source and binary forms, with or without 
+// modification, are permitted provided that the following conditions are met:
+// 
+// Redistributions of source code must retain the above copyright notice, this 
+// list of conditions and the following disclaimer.
+// 
+// Redistributions in binary form must reproduce the above copyright notice, 
+// this list of conditions and the following disclaimer in the documentation 
+// and/or other materials provided with the distribution.
+// 
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+// POSSIBILITY OF SUCH DAMAGE.
+
+#include "internal/HybiAccept.h"
+#include "internal/HybiPacketDecoder.h"
+
+#include "seasocks/IgnoringLogger.h"
+
+#include <gmock/gmock.h>
+
+#include <iostream>
+#include <sstream>
+#include <string.h>
+#include <string>
+
+using namespace seasocks;
+
+static IgnoringLogger ignore;
+
+void testSingleString(
+        HybiPacketDecoder::MessageState expectedState,
+        const char* expectedPayload,
+        const std::vector<uint8_t>& v,
+        uint32_t size = 0) {
+    HybiPacketDecoder decoder(ignore, v);
+    std::vector<uint8_t> decoded;
+    ASSERT_EQ(expectedState, decoder.decodeNextMessage(decoded));
+    ASSERT_EQ(expectedPayload, std::string(reinterpret_cast<const char*>(&decoded[0]), decoded.size()));
+    ASSERT_EQ(HybiPacketDecoder::NoMessage, decoder.decodeNextMessage(decoded));
+    ASSERT_EQ(size ? size : v.size(), decoder.numBytesDecoded());
+}
+
+void testLongString(size_t size, std::vector<uint8_t> v) {
+    for (size_t i = 0; i < size; ++i) {
+        v.push_back('A');
+    }
+    HybiPacketDecoder decoder(ignore, v);
+    std::vector<uint8_t> decoded;
+    ASSERT_EQ(HybiPacketDecoder::TextMessage, decoder.decodeNextMessage(decoded));
+    ASSERT_EQ(size, decoded.size());
+    for (size_t i = 0; i < size; ++i) {
+        ASSERT_EQ('A', decoded[i]);
+    }
+    ASSERT_EQ(HybiPacketDecoder::NoMessage, decoder.decodeNextMessage(decoded));
+    ASSERT_EQ(v.size(), decoder.numBytesDecoded());
+}
+
+TEST(HybiTests, textExamples) {
+    // CF. http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-10 #4.7
+    testSingleString(HybiPacketDecoder::TextMessage, "Hello", {0x81, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f});
+    testSingleString(HybiPacketDecoder::TextMessage, "Hello", {0x81, 0x85, 0x37, 0xfa, 0x21, 0x3d, 0x7f, 0x9f, 0x4d, 0x51, 0x58});
+    testSingleString(HybiPacketDecoder::Ping, "Hello", {0x89, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f});
+}
+
+TEST(HybiTests, withPartialMessageFollowing) {
+    testSingleString(HybiPacketDecoder::TextMessage, "Hello", {0x81, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x81}, 7);
+    testSingleString(HybiPacketDecoder::TextMessage, "Hello", {0x81, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x81, 0x05}, 7);
+    testSingleString(HybiPacketDecoder::TextMessage, "Hello", {0x81, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x81, 0x05, 0x48}, 7);
+    testSingleString(HybiPacketDecoder::TextMessage, "Hello", {0x81, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x81, 0x05, 0x48, 0x65, 0x6c, 0x6c}, 7);
+}
+
+TEST(HybiTests, binaryMessage) {
+    std::vector<uint8_t> packet { 0x82, 0x03, 0x00, 0x01, 0x02 };
+    std::vector<uint8_t> expected_body { 0x00, 0x01, 0x02 };
+    HybiPacketDecoder decoder(ignore, packet);
+    std::vector<uint8_t> decoded;
+    ASSERT_EQ(HybiPacketDecoder::BinaryMessage, decoder.decodeNextMessage(decoded));
+    ASSERT_EQ(expected_body, decoded);
+    ASSERT_EQ(HybiPacketDecoder::NoMessage, decoder.decodeNextMessage(decoded));
+    ASSERT_EQ(packet.size(), decoder.numBytesDecoded());
+}
+
+TEST(HybiTests, withTwoMessages) {
+    std::vector<uint8_t> data {
+        0x81, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f,
+        0x81, 0x07, 0x47, 0x6f, 0x6f, 0x64, 0x62, 0x79, 0x65};
+    HybiPacketDecoder decoder(ignore, data);
+    std::vector<uint8_t> decoded;
+    ASSERT_EQ(HybiPacketDecoder::TextMessage, decoder.decodeNextMessage(decoded));
+    ASSERT_EQ("Hello", std::string(reinterpret_cast<const char*>(&decoded[0]), decoded.size()));
+    ASSERT_EQ(HybiPacketDecoder::TextMessage, decoder.decodeNextMessage(decoded));
+    ASSERT_EQ("Goodbye", std::string(reinterpret_cast<const char*>(&decoded[0]), decoded.size()));
+    ASSERT_EQ(HybiPacketDecoder::NoMessage, decoder.decodeNextMessage(decoded));
+    ASSERT_EQ(data.size(), decoder.numBytesDecoded());
+}
+
+TEST(HybiTests, withTwoMessagesOneBeingMaskedd) {
+    std::vector<uint8_t> data {
+        0x81, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f,  // hello
+        0x81, 0x85, 0x37, 0xfa, 0x21, 0x3d, 0x7f, 0x9f, 0x4d, 0x51, 0x58  // also hello
+    };
+    HybiPacketDecoder decoder(ignore, data);
+    std::vector<uint8_t> decoded;
+    ASSERT_EQ(HybiPacketDecoder::TextMessage, decoder.decodeNextMessage(decoded));
+    ASSERT_EQ("Hello", std::string(reinterpret_cast<const char*>(&decoded[0]), decoded.size()));
+    ASSERT_EQ(HybiPacketDecoder::TextMessage, decoder.decodeNextMessage(decoded));
+    ASSERT_EQ("Hello", std::string(reinterpret_cast<const char*>(&decoded[0]), decoded.size()));
+    ASSERT_EQ(HybiPacketDecoder::NoMessage, decoder.decodeNextMessage(decoded));
+    ASSERT_EQ(data.size(), decoder.numBytesDecoded());
+}
+
+TEST(HybiTests, regressionBug) {
+    // top bit set of second byte of message used to trigger a MASK decode of the remainder
+    std::vector<uint8_t> data {
+        0x82, 0x05, 0x80, 0x81, 0x82, 0x83, 0x84  
+    };
+    std::vector<uint8_t> expected_body { 0x80, 0x81, 0x82, 0x83, 0x84 };
+    HybiPacketDecoder decoder(ignore, data);
+    std::vector<uint8_t> decoded;
+    ASSERT_EQ(HybiPacketDecoder::BinaryMessage, decoder.decodeNextMessage(decoded));
+    ASSERT_EQ(expected_body, decoded);
+    ASSERT_EQ(data.size(), decoder.numBytesDecoded());
+}
+
+TEST(HybiTests, longStringExamples) {
+    // These are the binary examples, but cast as strings.
+    testLongString(256, {0x81, 0x7E, 0x01, 0x00});
+    testLongString(65536, {0x81, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00});
+}
+
+TEST(HybiTests, accept) {
+    ASSERT_EQ("s3pPLMBiTxaQ9kYGzzhZRbK+xOo=", getAcceptKey("dGhlIHNhbXBsZSBub25jZQ=="));
+}
diff --git a/src/test/c/JsonTests.cpp b/src/test/c/JsonTests.cpp
new file mode 100644
index 0000000..b284b50
--- /dev/null
+++ b/src/test/c/JsonTests.cpp
@@ -0,0 +1,131 @@
+// Copyright (c) 2013, Matt Godbolt
+// All rights reserved.
+// 
+// Redistribution and use in source and binary forms, with or without 
+// modification, are permitted provided that the following conditions are met:
+// 
+// Redistributions of source code must retain the above copyright notice, this 
+// list of conditions and the following disclaimer.
+// 
+// Redistributions in binary form must reproduce the above copyright notice, 
+// this list of conditions and the following disclaimer in the documentation 
+// and/or other materials provided with the distribution.
+// 
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+// POSSIBILITY OF SUCH DAMAGE.
+
+#include "seasocks/util/Json.h"
+
+#include <gmock/gmock.h>
+#include <ostream>
+#include <map>
+#include <unordered_map>
+
+using namespace testing;
+using namespace seasocks;
+
+namespace {
+
+TEST(JsonTests, shouldHandleMaps) {
+    EXPECT_EQ("{\"monkey\":12}", makeMap("monkey", 12));
+}
+
+TEST(JsonTests, shouldHandleQuotedStrings) {
+    EXPECT_EQ("{\"key\":\"I have \\\"quotes\\\"\"}",
+            makeMap("key", "I have \"quotes\""));
+}
+
+TEST(JsonTests, shouldHandleNewLinesInStrings) {
+    std::stringstream str;
+    jsonToStream(str, "I have\nnew\rlines");
+    EXPECT_EQ("\"I have\\nnew\\rlines\"", str.str());
+}
+
+TEST(JsonTests, shouldHandleCrazyChars) {
+    std::stringstream str;
+    jsonToStream(str, "\x01\x02\x1f");
+    EXPECT_EQ("\"\\u0001\\u0002\\u001f\"", str.str());
+}
+
+TEST(JsonTests, shouldHandleDate) {
+    std::stringstream str;
+    jsonToStream(str, EpochTimeAsLocal(209001600));
+    EXPECT_EQ("new Date(209001600000).toLocaleString()", str.str());
+}
+
+struct Object {
+    void jsonToStream(std::ostream &ostr) const {
+        ostr << makeMap("object", true);
+    }
+    // Clang is pernickity about this. We don't want use this function
+    // but it's here to catch errors where we accidentally use it instead of the
+    // jsonToStream.
+    friend std::ostream &operator << (std::ostream &o, const Object &ob) {
+        return o << "Not this one";
+    }
+};
+
+struct Object2 {
+    friend std::ostream &operator << (std::ostream &o, const Object2 &ob) {
+        return o << "This is object 2";
+    }
+};
+
+static_assert(is_streamable<Object2>::value, "Should be streamable");
+
+TEST(JsonTests, shouldHandleCustomObjects) {
+    EXPECT_EQ(R"({"obj":{"object":true}})", makeMap("obj", Object()));
+    Object o;
+    EXPECT_EQ(R"({"obj":{"object":true}})", makeMap("obj", o));
+    EXPECT_EQ(R"({"obj":"This is object 2"})", makeMap("obj", Object2()));
+    Object2 o2;
+    EXPECT_EQ(R"({"obj":"This is object 2"})", makeMap("obj", o2));
+    // Putting a clang-specific pragma to thwart the unused warning in Object
+    // upsets GCC...so we just put a test for this to ensure it's used.
+    std::ostringstream ost;
+    ost << Object();
+    EXPECT_EQ("Not this one", ost.str()); // See comment
+}
+
+TEST(JsonTests, to_json) {
+    EXPECT_EQ("1", to_json(1));
+    EXPECT_EQ("3.14", to_json(3.14));
+    EXPECT_EQ("\"hello\"", to_json("hello"));
+    EXPECT_EQ(R"({"object":true})", to_json(Object()));
+    EXPECT_EQ(R"("This is object 2")", to_json(Object2()));
+}
+TEST(JsonTests, handlesArrays) {
+    EXPECT_EQ(R"([])", makeArray());
+    EXPECT_EQ(R"([1])", makeArray(1));
+    EXPECT_EQ(R"([1,2,3])", makeArray(1, 2, 3));
+    EXPECT_EQ(R"([1,2,3])", makeArray({1, 2, 3}));
+    EXPECT_EQ(R"(["abc"])", makeArray("abc"));
+    EXPECT_EQ(R"(["a","b","c"])", makeArray("a", "b", "c"));
+    EXPECT_EQ(R"(["a","b","c"])", makeArray({"a", "b", "c"}));
+    std::vector<JsonnedString> strs = { to_json(false), to_json(true) };
+    EXPECT_EQ(R"([false,true])", makeArrayFromContainer(strs));
+}
+
+TEST(JsonTests, handlesMaps) {
+    std::map<std::string, JsonnedString> ordMap;
+    ordMap["hello"] = to_json(true);
+    ordMap["goodbye"] = to_json(false);
+    EXPECT_EQ(R"({"goodbye":false,"hello":true})", makeMapFromContainer(ordMap));
+    std::map<std::string, JsonnedString> unordMap;
+    unordMap["hello"] = to_json(true);
+    unordMap["goodbye"] = to_json(false);
+    EXPECT_THAT(makeMapFromContainer(unordMap), AnyOf(
+            Eq(R"({"goodbye":false,"hello":true})"),
+            Eq(R"({"hello":true,"goodbye":false})")));
+}
+
+}
diff --git a/src/test/c/MockServerImpl.h b/src/test/c/MockServerImpl.h
new file mode 100644
index 0000000..42546b5
--- /dev/null
+++ b/src/test/c/MockServerImpl.h
@@ -0,0 +1,49 @@
+// Copyright (c) 2013, Matt Godbolt
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+#include "seasocks/ServerImpl.h"
+
+#include <gmock/gmock.h>
+
+namespace seasocks {
+
+class MockServerImpl : public ServerImpl {
+public:
+    virtual ~MockServerImpl() {}
+
+    MOCK_METHOD1(remove, void(Connection* connection));
+    MOCK_METHOD1(subscribeToWriteEvents, bool(Connection* connection));
+    MOCK_METHOD1(unsubscribeFromWriteEvents, bool(Connection* connection));
+    MOCK_CONST_METHOD0(getStaticPath, const std::string&());
+    MOCK_CONST_METHOD1(getWebSocketHandler, std::shared_ptr<WebSocket::Handler>(const char *));
+    MOCK_CONST_METHOD1(isCrossOriginAllowed, bool(const std::string&));
+    MOCK_METHOD1(handle, std::shared_ptr<Response>(const Request &));
+    MOCK_CONST_METHOD0(getStatsDocument, std::string());
+    MOCK_CONST_METHOD0(checkThread, void());
+};
+
+}
diff --git a/src/test/c/test_main.cpp b/src/test/c/test_main.cpp
new file mode 100644
index 0000000..11624ac
--- /dev/null
+++ b/src/test/c/test_main.cpp
@@ -0,0 +1,31 @@
+// Copyright (c) 2013, Matt Godbolt
+// All rights reserved.
+// 
+// Redistribution and use in source and binary forms, with or without 
+// modification, are permitted provided that the following conditions are met:
+// 
+// Redistributions of source code must retain the above copyright notice, this 
+// list of conditions and the following disclaimer.
+// 
+// Redistributions in binary form must reproduce the above copyright notice, 
+// this list of conditions and the following disclaimer in the documentation 
+// and/or other materials provided with the distribution.
+// 
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+// POSSIBILITY OF SUCH DAMAGE.
+
+#include <gmock/gmock.h>
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    return RUN_ALL_TESTS();
+}