Squashed 'third_party/seasocks/' changes from 016dc60..bac7d40

bac7d40 Merge pull request #118 from iwanders/allow-disabling-example-apps
5656c43 Allow disabling the example applications.
44c1aff Clang 8 CI build added.
64f5912 Usages of 'using namespace std' removed.
9d273db Usages of std::endl replaced with '\n'.
82d3e24 Code improvements.
8fb923f Naming of some C API variables improved.
9e1e9e3 Braces added.
0e936b7 Parameter names unified.
e519edc Iterator made const and braces added.
21f9e17 Merge branch 'offa/unit_tests'
0633d1f Merge pull request #116 from iwanders/provide_cmake_config
1c758a9 Tests for the trimWhitespace function added.
125dd89 Catch v2.6.0 update.
27ab414 Provide SeasocksConfig.cmake files when installed.
6e76ab2 Travis updated to Xenial.
7c2e3f2 Merge pull request #115 from twanas/master
7782925 Updated return type as per @offa suggestion.
f58cba2 Resolved ignoring return value error
c3eb2da CI builds changed to Release build type (#114).
1c7a038 Tests for the split function added.
bf00af7 Tests for the replace function added.
673cc4b String utils comparison test added.
2984f41 Calls to c_str() cleaned up.
da0d091 Calls to atoi() replaced with the safer stoi().
cfc3a46 Some code improvements; static access, size() replaced by empty() and loop variable made const-Ref.
766ccef Merge pull request #107 from mattgodbolt/102-clangformat
bbc3bd1 Code formatted by clang-format (#102).
79ee9a0 Ctor initializer indention improved (#107, #102).
4ee3d51 Clang-Format CMake support added (#102).
7daedf9 Clang-Format config added (#102).
7fa6e89 Rename log levels to not be ALL CAPS to avoid clashing with commonly-defined macros. Fixes #111
0008558 Merge pull request #110 from iwanders/allow-embedding-as-subproject
2fa6b27 Use current binary directory and provide aliases.
0c42700 Embedded data made const and indention fixed.
c185a6e Missing overrides added.
a10ddcb Merge pull request #108 from iwanders/add-export-fix-zlib-link-interface
12edb16 Export zlib to link interface for static library.
6fdd5f7 Export targets to allow using the build tree.
33f6537 Typo in the test case fixed.
8bce5c0 Merge pull request #106 from offa/105-non_ascii_escape
143d167 Test for the json handling of ascii strings added.
f568cd2 Escaping for non-ascii characters (#105).
6ec0bf4 Unit test execution target added and integrated into the ci build (#104).
eb54d16 Catch updated to v2.5.0.
bb1bbeb Merge pull request #100 from membar/patch-2
79a963d Deprecated travis setting removed.
23995dd Merge pull request #99 from membar/patch-1
a611a87 Return errbuf
4d9dc6f Add byteswap.h
8742bd7 Version fixed.
f6373e2 Fix typo in CMakeLists.txt
3d8653d Fix path to Config.h.in
5b31bf0 Catch updated to v2.4.1.
544541c Clang 7 CI build added.
70f072d Some travis yml cleanup.
12f0f0f Merge pull request #97 from nkowdley/master
a51be5d Update CMakeLists.txt to work on CMake 3.12
afeb46c RaiiFd extracted (#95).
4daa1e3 Merge pull request #92 from offa/python3_support
6dd4574 Old style cast replaced with static_cast<>().
9ee6e9b Parameter named as in the header.
9d0dffc Overrides added.
fe933b2 Accessing npos through std::string and braces added.
27f9781 Consts added.
5cb9a27 Constant moved to anonymous namespace.
62711b0 Function paramters named consistent.
5d34845 Single argument Ctor made explicit.
9e6f08e Braces added and formatting.
ff62ce6 Parameter made const-ref.
11f658c Single argument ctor made explicit.
c777c22 Braces added.
8108180 Naming fixed and override added.
5aaf62a Ctor made explicit.
925ac0b Formatting improved and braces added.
ab1c956 Missing override added.
9206ea3 Private dependencies for tests.
0a06a52 Unused variable and directory removed from the travis ci config.
e6d8684 License file of Catch added.
b11fd3f Catch updated to latest v2.3.0.
c5ef750 Scope of the output file reduced.
99e2046 No longer needed parameter removed.
4b78344 Embedded generator script improved. The structure of the generated source code is now more maintainable.
1694ad5 Script gen_embedded.py refactored to reflect the structure of the generated file better.
c7022d5 Using the default python installation again. This can either be python2 or python3 - whatever the caller has set default.
050a801 Call of the gen_embedded.py script updated to the new arguments.
fafe609 Refactoring of the gen_embedded.py script. The scripts takes arguments for the input files and the destination files now. The result is written directly to the output file instead of stdout.
f1b0404 Workaround for the python 2/3 compatibility.
127c329 Support for python3 only as python2 is incompatible now.
46f8a91 Const added in the generated code.
8193732 Tabs replaced with spaces in the generated code.
82648cf Embedded content generator updated to support python3.
9ef2bcd GCC 8 CI build added.
a1bc19e CMake file with the compiler settings renamed.
0f4c576 Catch updated to v2.2.2.
ba8d7cb Clang 6.0 CI build added.
3f40182 Catch updated to v2.2.1.
648b5fa Support (in a very primitive way), the protocols. See bug #81
12bedeb Catch updated to v2.1.2.
7d9b0bb Catch updated to v2.1.1.
b9f5a21 Catch updated to latest v2.1.0 release.
c56265c GCC 4.8 CI build removed. This version does not provide enough C++14 support.
735eb1f SHA1 doesn't need a (virtual) dtor. Accidentally cleaned up some Whitespaces.
fd8d431 Deleted member functions made public.
8b77076 Modernization of some loops; range-based loops instead of manual iterator loops.
1c30fe1 Old typedef syntax replaced.
aebd693 Resetting unique ptr improved.
caa0e1f Deprecated C headers replaced with their C++ equivalent.
5fd3e30 Empty braces replaced by '= default' for default implementation.
00b5a66 Add coverage bage
1f3f1cc Installation of CMake no longer necessary as the recent build image update ships CMake 3.9 now.
8e8c2b8 Actually disable coverage for clang 3.9
7274f79 Build debug, coverage off for clang 3.9 where it doesn't seem to work
2e0b1d9 Coverage take two
b86ce89 Enable coverage
a1a71f4 Catch has removed the matchers in v2, instead operators are used.
4c195e3 Missing include added.
d4cd9ad Catch updated to v2.0.1 release.
41a75a8 Whoops :)
823f197 Add new CLion stuff
2833512 Include all files in CMakeLists.txt
de8c718 Naked new's replaced and formatting.
f26fc41 Naked new's replaced.
edf08a3 Tests for all concrete response factory functions added.
8c28510 Small fix.
a34114a Some more naked news used with shared_ptr replaced with make_unique(). Some cleanup.
ef1c909 Some naked new's in context of shared_ptr replaced with make_shared().
ca9d0ea GCC 4.7 CI build removed since it has no C++14 support.
cc9523f Moved to C++14.
612dce6 Release v1.3.2.
0cfa345 Inclusion of Catch improved.
896662b Cleanup of the main CMakeLists.txt.
72bf82b Setting the C++ Standard using CMAKE_CXX_STANDARD and let CMake figure out which flag is necessary for the used compiler.
3203132 Using add_compile_options().
9cd2c8d Clang 5 CI build added.
c382014 Catch updated to v1.10.0. Thanks to some performance related improvements of catch, unit tests should run notably faster now.
96b163d Merge pull request #78 from dhouck/soname
9a275ee Add SONAME versioning to shared library
0ba3d9c Merge pull request #75 from offa/catch_1.9.6
5ceed7e Catch updated to latest release (v1.9.6) and some updates accordingly.
bc32e79 Merge pull request #73 from offa/ci_gcc7
4a71b69 Gcc7 CI Build added.
1368d33 Release v1.3.1.
ea54b6c Version corrected.
be6e5a0 Typo fixed.
524a984 Update copyright
29f9d5c Try another approach
da0e29d Make static to hopefully prevent some older G++ warnings
5a88f4f Ahem sources for travis
2cf5eef WAG at testing in travis
68dca58 Support configurable dependency on deflate. On by default.
a098b9b Merge pull request #70 from caffinatedmonkey/streaming-response
7d4844e Added StreamingResponse
3d94f43 Merge pull request #71 from offa/zlib_dependency_fix
865c116 Forward declaration instead of include to break dependency to an internal header.
c1e3d5d Merge pull request #69 from offa/install_path_fix
6f26211 Install path fixed for runtime targets (= executables).
6488d1a Merge pull request #67 from offa/issue66/type_deduction_fix
cdb3c9b Type deduction fixed (#66).
ea485c5 Merge pull request #65 from offa/ci/clang40
9a28644 Missing override added.
a3b1373 Fix wrong compiler used.
8eec2d2 Clang 4.0 clang build added.
4480081 Merge pull request #64 from offa/ci_clang39
9124c42 Clang 3.9 has finally been whitelisted by travis ci.
1061092 Merge pull request #63 from offa/issue62-static_lib
dff594a Fix for the static library target not including the embedded content object correctly (#62).
749726a Merge pull request #60 from hoytech/permessage-deflate
027afb7 Per-message deflate as described in RFC 7692
cc1c68a Merge pull request #61 from offa/gitignore_cleanup
5694418 Obsolete gitignore file removed.
1c2ca3e Merge pull request #59 from offa/issue57
db8c1c0 Debug flag removed (see #57).
042a476 Merge pull request #58 from offa/compiler_checks_cleanup
24c390f Compiler checks removed as the tested features should be supported by any C++11 compiler.
54de807 Merge pull request #56 from offa/cmake_python_search
468d95c Search and check python executable instead of hardcoding it.
df9173a Merge pull request #52 from offa/version_fix
ae6866e Merge pull request #54 from offa/missing_override
c3e531a Missing include for bswap64 added.
48d295a Missing include added.
0f95aec Missing override added (#53).
ab6d881 Using pragma once.
851c4a7 Compile definition of the version removed.
68bc86f Obsolute template file removed.
aad7edf Version.h removed as it is no longer needed.
728f0ef Braces unified.
d655c57 Version made a constant.
d1074e6 Version fixed for all targets.
5cdd0f4 Merge pull request #51 from offa/shadow_fixes
a99c79b Fixes for shadowed variables.
51f3826 Rev to 1.2.6
636b057 Rev to 1.2.5
3d78946 Make client buffer size configurable
0f496f9 Merge pull request #50 from offa/embedded_content_test
d7ff2fd Some more tests added.
5f2d2ed Merge pull request #49 from offa/cmake_include_cleanup
25cacaa Unit Test for checking the embedded content generation added.
b76bd9f Cleanup (see #48).
cd5652c Merge pull request #48 from offa/some_fixes
34d13ca Broken generation of embedded content fixed. CMake warning according add_library() fixed.
6fcdc6a Replaced source file globbing as this is not recommended by cmake.
0c17838 Merge pull request #46 from dascandy/master
faadb44 Aargh travis
03152e7 More travis
92ec5f8 Travis...
892e653 Travis tweaks
2db009e Big travis refactor
a8bae23 More travis attempts
f584a79 Dump cmake version in travis
809257a Remove some do-nothing commands
693dcc0 Merge branch 'master' of https://github.com/mattgodbolt/seasocks
2d7b1e1 Moved the embedded logic into its own subfolder with cleaned up CMakeLists; made use more of target-specific properties rather than global ones; made library build only once instead of twice. One cmake warning left sadly.
ee19673 Arg
eed081b Use suppressions. Fixes #44
be0ecf1 Fix compile error #45
c5823d6 Merge pull request #43 from offa/scoped_enums
158c09e Logger Level converted to scoped enums.
572e4f9 Request Verb converted to scoped enum.
0e7661e Connection State converted to scoped enum.
3b356c7 Merge pull request #42 from offa/scoped_enum
bb9ee7c Enum changed to scoped enum.
306b460 Merge pull request #41 from hoytech/master
1cd4ceb Support listening on AF_UNIX sockets
0beca0c Handle PONG frames. Should fix #40
39b458f Updates for CLion
934116b Merge pull request #38 from offa/ci
6400c5a Clang 3.9 added to Ci builds.
3993599 Ci builds for more compilers added. There are builds for Gcc 4.7 - 6 and Clang 3.6 - 3.8.
c5a975b Merge pull request #37 from offa/version_fix
bd8c981 Version fixed.
c5bea26 Merge pull request #36 from seeekr/patch-1
fed61b7 fix typo in async_test.cpp
b95be51 Merge pull request #35 from offa/constants_constexpr
f3bdd0f Constants made constexpr.
5d17e7b Merge pull request #32 from offa/to_string
f8c7d3b Workaround for CI failure.
a791e03 toString() for integral types implemented using std::to_string().
e5488c6 Fix the tests for toString so they really show bug 28 was fixed. Closes #28
b11439e Really fix #28
1e924a1 Set a default C locale before formatting things in toString. Fixes #28
f9a46d4 Remove emplace from chatroom example
353f870 Add a chatroom example
229e15b Merge pull request #27 from offa/some_fixes
35967b6 Ctors made explicit.
e421542 Missing init of member variabled added.
c7dc05e Fix for 1.2.3
f6b4dc9 Rev to 1.2.3
48713f3 Use c++1y if available
f72d695 Upgrade DRW compiler version
30071e9 New CLion; add a shared library build
8dfd835 Couple more server tests
4a4a9a4 Use std::ostringstream instead of printf/%x. Portable solution. Fixes #25 again
1fe8f1e Fix size_t printf using C99/GNU %z
495a5b5 Bump to 1.2.2
8294c91 Fix error documents
40a89b7 Add Transfer Encoding
d7bf183 Bump to 1.2.0
3e6784c Bump to 1.1.7
4340303 Merge branch 'master' into async
2b22fbc Merge pull request #24 from offa/minor_fix
4d91214 Tidying up of CMakeLists
97be762 Ensure async test files are copied
a0ab831 Remove unused declaration
fc3cf05 Merge branch 'master' into async
66c57c8 Improve documentation
9b45d96 Greatly simplify the async test using the new API
6d8dda6 Accept lambdas/function<void()> as parameters to 'execute'
2d5bfd5 API change
a87272f Unify capitalization of Seasocks
2f78ab3 Accept lambdas/function<void()> as parameters to 'execute'
4997145 Tidying up
48df0d2 Fix tests
a25ed1c Slightly better async example
eb210b2 More CLion churn (c.f. https://youtrack.jetbrains.com/issue/CPP-6031)
fcd6551 Further work in progress on asynchronous handlers. Working, if ugly and inelgant interface
c09dd01 Work in progress on asynchronous handlers
2ca7237 Add override
e141a4d New CLionisms
ebe5f28 Copy the ws_test_web stuff into the build directory so it can be run directly from there
df925f2 Add all files to CMakeLists to keep CLion happy
c9ee356 Update copyright
636d1fd Enable debug info. Add a simple server executor test
34dc3fb Parameter name used in the macro.
986ff8c Merge pull request #22 from AustinSchuh/arm_alignment_error
5cf784b Fixed alignment error in HybiPacketDecoder on ARM.
d4df8ba Merge pull request #20 from offa/tests_optional
f385375 Merge pull request #19 from offa/pedantic
531f3c0 Option for tests added.
45fb99b Pedantic flag added.
e2bb596 Another attempt at fixing embedded pendantic compilation. Should address #16
b055a03 Merge pull request #17 from offa/nullptr
e32eb53 Merge pull request #18 from offa/ci_buildtypes
6c0d593 CI: Build types added to matrix.
fc36072 NULL replaced by nullptr.
55dd65f DRW config fixups
0dd6e24 Cut up large constant strings to stay clean under -pendantic. Fixes #16
e2155e7 Merge pull request #15 from offa/ci_make
87a02cb CI: Make added as extra step.
4288c74 Remove autoconf from travis
be061b9 Remove the autoconf/configure steps
c223bca Fix up TODOs; update copyright
d690c4c Remove more TODOs for CMake; c++11 and better compiler feature support
d9ae885 CMake tests and valgrind
90dea18 Misc random clion changes
2ecd0a8 Misc tidyups, no functional change
9f7dafa Add CLion files
84e430a Update DRW config
3c73fee Fix clang3.7 warning
882b6ea Fix up gcc4.7 compound initializer issues
d71d67e More warnings fixed, warnings are errors
47fa3db Patch catch.hpp
cb48383 Move to using catch instead of gmock
ee8ece3 ignore idea files for now
9ee6d50 Merge pull request #14 from offa/cmake
c6af9b6 Tests added.
f5b90a2 Apps added.
017fa41 Travis builds using cmake added and cmake set to 2.8.x, since there's no v3.x availalbe on travis yet.
4d26544 Install target added.
7a8d72b Version set.
99adbbe Merge branch 'master' into cmake
1c6fc8a Merge pull request #13 from offa/travi_apt_valgrind
b3516f9 Travis yml: Sudo explicitly disabled.
3078506 Installing valgrind using apt addon -- should enable travis ci container.
636e0f6 Merge branch 'master' into cmake
2b68564 Wall added.
f57aadc Merge pull request #12 from offa/travis_ci_addon
4737e03 Install compilers through Travis Apt-addon.
c090ad4 Embedded content linkage fixed.
3cf5a34 Linking generated sources (WIP).
161966f Static lib removed.
787d965 Embedded library.
5bba071 Map test integrated.
4fd9a1b Configure file.
70b13ec Call fix.
2a17c1c Dependencies fixed.
5ae3f0c Some Improvements.
06a5f50 CMakeFiles added.
1742e9d Look for python2 and run that in preference to python for gen_embedded.py
57c572f Merge pull request #10 from tet112001/master
1eed0fd Head method support
62e1ff1 Merge pull request #9 from Shin-nn/master
a44ec74 fixing style
cdd3f94 forgotten debug line
66f1585 Reponse can modify Cache-Control and Expires headers
8b214c4 Merge pull request #7 from Shin-nn/master
f1185a7 Fix for GCC warnings

Change-Id: I4bd1ec73efb282482c7a1e568625761e6bed0880
git-subtree-dir: third_party/seasocks
git-subtree-split: bac7d40a9bc081997ac5e9165b408e4f54410a44
diff --git a/src/main/c/CMakeLists.txt b/src/main/c/CMakeLists.txt
new file mode 100644
index 0000000..5c4d7f8
--- /dev/null
+++ b/src/main/c/CMakeLists.txt
@@ -0,0 +1,120 @@
+set(SEASOCKS_SOURCE_FILES
+        Connection.cpp
+        HybiAccept.cpp
+        HybiPacketDecoder.cpp
+        internal/Base64.cpp
+        internal/Base64.h
+        internal/ConcreteResponse.h
+        internal/Debug.h
+        internal/Embedded.h
+        internal/HeaderMap.h
+        internal/HybiAccept.h
+        internal/HybiPacketDecoder.h
+        internal/LogStream.h
+        internal/PageRequest.h
+        Logger.cpp
+        md5/md5.cpp
+        md5/md5.h
+        PageRequest.cpp
+        Response.cpp
+        seasocks/Connection.h
+        seasocks/Credentials.h
+        seasocks/IgnoringLogger.h
+        seasocks/Logger.h
+        seasocks/PageHandler.h
+        seasocks/PrintfLogger.h
+        seasocks/Request.cpp
+        seasocks/Request.h
+        seasocks/ResponseBuilder.cpp
+        seasocks/ResponseBuilder.h
+        seasocks/ResponseCode.cpp
+        seasocks/ResponseCodeDefs.h
+        seasocks/ResponseCode.h
+        seasocks/Response.h
+        seasocks/ResponseWriter.h
+        seasocks/Server.h
+        seasocks/ServerImpl.h
+        seasocks/SimpleResponse.h
+        seasocks/StreamingResponse.cpp
+        seasocks/StreamingResponse.h
+        seasocks/StringUtil.h
+        seasocks/SynchronousResponse.cpp
+        seasocks/SynchronousResponse.h
+        seasocks/ToString.h
+        seasocks/TransferEncoding.h
+        seasocks/util/CrackedUri.h
+        seasocks/util/CrackedUriPageHandler.h
+        seasocks/util/Html.h
+        seasocks/util/Json.h
+        seasocks/util/PathHandler.h
+        seasocks/util/RootPageHandler.h
+        seasocks/util/StaticResponseHandler.h
+        seasocks/WebSocket.h
+        seasocks/ZlibContext.h
+        Server.cpp
+        sha1/sha1.cpp
+        sha1/sha1.h
+        StringUtil.cpp
+        util/CrackedUri.cpp
+        util/Json.cpp
+        util/PathHandler.cpp
+        util/RootPageHandler.cpp
+        )
+
+if (DEFLATE_SUPPORT)
+    set(SEASOCKS_SOURCE_FILES ${SEASOCKS_SOURCE_FILES} seasocks/ZlibContext.cpp)
+else()
+    set(SEASOCKS_SOURCE_FILES ${SEASOCKS_SOURCE_FILES} seasocks/ZlibContextDisabled.cpp)
+endif()
+
+add_library(seasocks_obj OBJECT ${SEASOCKS_SOURCE_FILES})
+target_include_directories(seasocks_obj PUBLIC
+    $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/>
+    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/.>
+)
+set_property(TARGET seasocks_obj PROPERTY POSITION_INDEPENDENT_CODE TRUE)
+
+add_library(seasocks STATIC $<TARGET_OBJECTS:seasocks_obj> $<TARGET_OBJECTS:embedded>)
+add_library(Seasocks::seasocks ALIAS seasocks)
+target_include_directories(seasocks PUBLIC
+    $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/>
+    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/.>
+)
+target_link_libraries(seasocks PRIVATE ${CMAKE_THREAD_LIBS_INIT})
+if (DEFLATE_SUPPORT)
+    target_link_libraries(seasocks PRIVATE "${ZLIB_LIBRARIES}")
+endif()
+
+add_library(seasocks_so SHARED $<TARGET_OBJECTS:seasocks_obj> $<TARGET_OBJECTS:embedded>)
+add_library(Seasocks::seasocks_so ALIAS seasocks_so)
+target_include_directories(seasocks_so PUBLIC ${ZLIB_INCLUDE_DIRS} 
+    $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/>
+    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/.>
+)
+if (DEFLATE_SUPPORT)
+    target_link_libraries(seasocks_so PRIVATE ${CMAKE_THREAD_LIBS_INIT} "${ZLIB_LIBRARIES}")
+endif()
+set_target_properties(seasocks_so PROPERTIES OUTPUT_NAME seasocks VERSION ${PROJECT_VERSION})
+
+install(TARGETS seasocks seasocks_so EXPORT ${PROJECT_NAME}Config
+        LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+        ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+        RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+        )
+install(DIRECTORY seasocks
+        DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
+        FILES_MATCHING PATTERN "*.h"
+        )
+# export for build tree.
+export(
+    TARGETS seasocks seasocks_so
+    NAMESPACE ${PROJECT_NAME}::
+    FILE ${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake
+)
+# export for install.
+install(
+  EXPORT ${PROJECT_NAME}Config
+  FILE ${PROJECT_NAME}Config.cmake
+  NAMESPACE ${PROJECT_NAME}::
+  DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}/
+)
diff --git a/src/main/c/Connection.cpp b/src/main/c/Connection.cpp
index 9c250a0..bace14d 100644
--- a/src/main/c/Connection.cpp
+++ b/src/main/c/Connection.cpp
@@ -1,26 +1,26 @@
-// Copyright (c) 2013, Matt Godbolt
+// Copyright (c) 2013-2017, Matt Godbolt
 // All rights reserved.
-// 
-// Redistribution and use in source and binary forms, with or without 
+//
+// 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 
+//
+// 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 
+//
+// 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 
+//
+// 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/Config.h"
@@ -30,7 +30,7 @@
 #include "internal/HybiPacketDecoder.h"
 #include "internal/LogStream.h"
 #include "internal/PageRequest.h"
-#include "internal/Version.h"
+#include "internal/RaiiFd.h"
 
 #include "md5/md5.h"
 
@@ -41,22 +41,29 @@
 #include "seasocks/Server.h"
 #include "seasocks/StringUtil.h"
 #include "seasocks/ToString.h"
+#include "seasocks/ResponseWriter.h"
+#include "seasocks/ZlibContext.h"
 
+#include <sys/socket.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 
-#include <assert.h>
-#include <ctype.h>
-#include <errno.h>
+#include <algorithm>
+#include <cassert>
+#include <cctype>
+#include <cerrno>
 #include <fcntl.h>
 #include <fstream>
 #include <iostream>
 #include <limits>
+#include <memory>
 #include <sstream>
-#include <stdio.h>
-#include <string.h>
+#include <cstdio>
+#include <cstring>
 #include <unistd.h>
+#include <byteswap.h>
 #include <unordered_map>
+#include <memory>
 
 namespace {
 
@@ -73,85 +80,51 @@
     return numSpaces > 0 ? keyNumber / numSpaces : 0;
 }
 
-char* extractLine(uint8_t*& first, uint8_t* last, char** colon = NULL) {
+char* extractLine(uint8_t*& first, uint8_t* last, char** colon = nullptr) {
     for (uint8_t* ptr = first; ptr < last - 1; ++ptr) {
         if (ptr[0] == '\r' && ptr[1] == '\n') {
             ptr[0] = 0;
             uint8_t* result = first;
             first = ptr + 2;
-            return reinterpret_cast<char*> (result);
+            return reinterpret_cast<char*>(result);
         }
-        if (colon && ptr[0] == ':' && *colon == NULL) {
-            *colon = reinterpret_cast<char*> (ptr);
+        if (colon && ptr[0] == ':' && *colon == nullptr) {
+            *colon = reinterpret_cast<char*>(ptr);
         }
     }
-    return NULL;
+    return nullptr;
 }
 
-std::string webtime(time_t time) {
-    struct tm tm;
-    gmtime_r(&time, &tm);
-    char buf[1024];
-    // Wed, 20 Apr 2011 17:31:28 GMT
-    strftime(buf, sizeof(buf)-1, "%a, %d %b %Y %H:%M:%S %Z", &tm);
-    return buf;
-}
-
-std::string now() {
-    return webtime(time(NULL));
-}
-
-class RaiiFd {
-    int _fd;
-public:
-    RaiiFd(const char* filename) {
-        _fd = ::open(filename, O_RDONLY);
-    }
-    RaiiFd(const RaiiFd&) = delete;
-    RaiiFd& operator=(const RaiiFd&) = delete;
-    ~RaiiFd() {
-        if (_fd != -1) {
-            ::close(_fd);
-        }
-    }
-    bool ok() const {
-        return _fd != -1;
-    }
-    operator int() const {
-        return _fd;
-    }
-};
-
 const std::unordered_map<std::string, std::string> contentTypes = {
-    { "txt", "text/plain" },
-    { "css", "text/css" },
-    { "csv", "text/csv" },
-    { "htm", "text/html" },
-    { "html", "text/html" },
-    { "xml", "text/xml" },
-    { "js", "text/javascript" }, // Technically it should be application/javascript (RFC 4329), but IE8 struggles with that
-    { "xhtml", "application/xhtml+xml" },
-    { "json", "application/json" },
-    { "pdf", "application/pdf" },
-    { "zip", "application/zip" },
-    { "tar", "application/x-tar" },
-    { "gif", "image/gif" },
-    { "jpeg", "image/jpeg" },
-    { "jpg", "image/jpeg" },
-    { "tiff", "image/tiff" },
-    { "tif", "image/tiff" },
-    { "png", "image/png" },
-    { "svg", "image/svg+xml" },
-    { "ico", "image/x-icon" },
-    { "swf", "application/x-shockwave-flash" },
-    { "mp3", "audio/mpeg" },
-    { "wav", "audio/x-wav" },
-    { "ttf", "font/ttf" },
+    {"txt", "text/plain"},
+    {"css", "text/css"},
+    {"csv", "text/csv"},
+    {"htm", "text/html"},
+    {"html", "text/html"},
+    {"xml", "text/xml"},
+    {"js", "text/javascript"}, // Technically it should be application/javascript (RFC 4329), but IE8 struggles with that
+    {"xhtml", "application/xhtml+xml"},
+    {"json", "application/json"},
+    {"pdf", "application/pdf"},
+    {"zip", "application/zip"},
+    {"tar", "application/x-tar"},
+    {"gif", "image/gif"},
+    {"jpeg", "image/jpeg"},
+    {"jpg", "image/jpeg"},
+    {"tiff", "image/tiff"},
+    {"tif", "image/tiff"},
+    {"png", "image/png"},
+    {"svg", "image/svg+xml"},
+    {"ico", "image/x-icon"},
+    {"swf", "application/x-shockwave-flash"},
+    {"mp3", "audio/mpeg"},
+    {"wav", "audio/x-wav"},
+    {"ttf", "font/ttf"},
 };
 
 std::string getExt(const std::string& path) {
     auto lastDot = path.find_last_of('.');
-    if (lastDot != path.npos) {
+    if (lastDot != std::string::npos) {
         return path.substr(lastDot + 1);
     }
     return "";
@@ -177,24 +150,25 @@
     return false;
 }
 
-const size_t MaxBufferSize = 16 * 1024 * 1024;
-const size_t ReadWriteBufferSize = 16 * 1024;
-const size_t MaxWebsocketMessageSize = 16384;
-const size_t MaxHeadersSize = 64 * 1024;
+constexpr size_t ReadWriteBufferSize = 16 * 1024;
+constexpr size_t MaxWebsocketMessageSize = 16384;
+constexpr size_t MaxHeadersSize = 64 * 1024;
 
 class PrefixWrapper : public seasocks::Logger {
     std::string _prefix;
     std::shared_ptr<Logger> _logger;
+
 public:
     PrefixWrapper(const std::string& prefix, std::shared_ptr<Logger> logger)
-    : _prefix(prefix), _logger(logger) {}
+            : _prefix(prefix), _logger(logger) {
+    }
 
-    virtual void log(Level level, const char* message) {
+    virtual void log(Level level, const char* message) override {
         _logger->log(level, (_prefix + message).c_str());
     }
 };
 
-bool hasConnectionType(const std::string &connection, const std::string &type) {
+bool hasConnectionType(const std::string& connection, const std::string& type) {
     for (auto conType : seasocks::split(connection, ',')) {
         while (!conType.empty() && isspace(conType[0]))
             conType = conType.substr(1);
@@ -204,27 +178,66 @@
     return false;
 }
 
-}  // namespace
+} // namespace
 
 namespace seasocks {
 
+struct Connection::Writer : ResponseWriter {
+    Connection* _connection;
+    explicit Writer(Connection& connection)
+            : _connection(&connection) {
+    }
+
+    void detach() {
+        _connection = nullptr;
+    }
+
+    void begin(ResponseCode responseCode, TransferEncoding encoding) override {
+        if (_connection)
+            _connection->begin(responseCode, encoding);
+    }
+    void header(const std::string& header, const std::string& value) override {
+        if (_connection)
+            _connection->header(header, value);
+    }
+    void payload(const void* data, size_t size, bool flush) override {
+        if (_connection)
+            _connection->payload(data, size, flush);
+    }
+    void finish(bool keepConnectionOpen) override {
+        if (_connection)
+            _connection->finish(keepConnectionOpen);
+    }
+    void error(ResponseCode responseCode, const std::string& payload) override {
+        if (_connection)
+            _connection->error(responseCode, payload);
+    }
+
+    bool isActive() const override {
+        return _connection;
+    }
+};
+
 Connection::Connection(
-        std::shared_ptr<Logger> logger,
-        ServerImpl& server,
-        int fd,
-        const sockaddr_in& address)
-    : _logger(new PrefixWrapper(formatAddress(address) + " : ", logger)),
-      _server(server),
-      _fd(fd),
-      _shutdown(false),
-      _hadSendError(false),
-      _closeOnEmpty(false),
-      _registeredForWriteEvents(false),
-      _address(address),
-      _bytesSent(0),
-      _bytesReceived(0),
-      _shutdownByUser(false),
-      _state(READING_HEADERS) {
+    std::shared_ptr<Logger> logger,
+    ServerImpl& server,
+    int fd,
+    const sockaddr_in& address)
+        : _logger(std::make_shared<PrefixWrapper>(formatAddress(address) + " : ", logger)),
+          _server(server),
+          _fd(fd),
+          _shutdown(false),
+          _hadSendError(false),
+          _closeOnEmpty(false),
+          _registeredForWriteEvents(false),
+          _address(address),
+          _bytesSent(0),
+          _bytesReceived(0),
+          _shutdownByUser(false),
+          _transferEncoding(TransferEncoding::Raw),
+          _chunk(0u),
+          _writer(std::make_shared<Writer>(*this)),
+          _state(State::READING_HEADERS) {
 }
 
 Connection::~Connection() {
@@ -258,6 +271,12 @@
 
 
 void Connection::finalise() {
+    if (_response) {
+        _response->cancel();
+        _response.reset();
+        _writer->detach();
+        _writer.reset();
+    }
     if (_webSocketHandler) {
         _webSocketHandler->onDisconnect(this);
         _webSocketHandler.reset();
@@ -270,12 +289,12 @@
     _fd = -1;
 }
 
-int Connection::safeSend(const void* data, size_t size) {
+ssize_t Connection::safeSend(const void* data, size_t size) {
     if (_fd == -1 || _hadSendError || _shutdown) {
         // Ignore further writes to the socket, it's already closed or has been shutdown
         return -1;
     }
-    int sendResult = ::send(_fd, data, size, MSG_NOSIGNAL);
+    auto sendResult = ::send(_fd, data, size, MSG_NOSIGNAL);
     if (sendResult == -1) {
         if (errno == EAGAIN || errno == EWOULDBLOCK) {
             // Treat this as if zero bytes were written.
@@ -294,7 +313,7 @@
         return false;
     }
     if (size) {
-        int bytesSent = 0;
+        ssize_t bytesSent = 0;
         if (_outBuf.empty() && flushIt) {
             // Attempt fast path, send directly.
             bytesSent = safeSend(data, size);
@@ -309,9 +328,9 @@
         size_t bytesToBuffer = size - bytesSent;
         size_t endOfBuffer = _outBuf.size();
         size_t newBufferSize = endOfBuffer + bytesToBuffer;
-        if (newBufferSize >= MaxBufferSize) {
-            LS_WARNING(_logger, "Closing connection: buffer size too large (" 
-                    << newBufferSize << " >= " << MaxBufferSize << ")");
+        if (newBufferSize >= _server.clientBufferSize()) {
+            LS_WARNING(_logger, "Closing connection: buffer size too large ("
+                                    << newBufferSize << " >= " << _server.clientBufferSize() << ")");
             closeInternal();
             return false;
         }
@@ -325,8 +344,9 @@
 }
 
 bool Connection::bufferLine(const char* line) {
-    static const char crlf[] = { '\r', '\n' };
-    if (!write(line, strlen(line), false)) return false;
+    static const char crlf[] = {'\r', '\n'};
+    if (!write(line, strlen(line), false))
+        return false;
     return write(crlf, 2, false);
 }
 
@@ -341,7 +361,7 @@
     }
     size_t curSize = _inBuf.size();
     _inBuf.resize(curSize + ReadWriteBufferSize);
-    int result = ::read(_fd, &_inBuf[curSize], ReadWriteBufferSize);
+    auto result = ::read(_fd, &_inBuf[curSize], ReadWriteBufferSize);
     if (result == -1) {
         LS_WARNING(_logger, "Unable to read from socket : " << getLastError());
         return;
@@ -367,12 +387,12 @@
     if (_outBuf.empty()) {
         return true;
     }
-    int numSent = safeSend(&_outBuf[0], _outBuf.size());
+    auto numSent = safeSend(&_outBuf[0], _outBuf.size());
     if (numSent == -1) {
         return false;
     }
     _outBuf.erase(_outBuf.begin(), _outBuf.begin() + numSent);
-    if (_outBuf.size() > 0 && !_registeredForWriteEvents) {
+    if (!_outBuf.empty() && !_registeredForWriteEvents) {
         if (!_server.subscribeToWriteEvents(this)) {
             return false;
         }
@@ -396,24 +416,28 @@
 
 void Connection::handleNewData() {
     switch (_state) {
-    case READING_HEADERS:
-        handleHeaders();
-        break;
-    case READING_WEBSOCKET_KEY3:
-        handleWebSocketKey3();
-        break;
-    case HANDLING_HIXIE_WEBSOCKET:
-        handleHixieWebSocket();
-        break;
-    case HANDLING_HYBI_WEBSOCKET:
-        handleHybiWebSocket();
-        break;
-    case BUFFERING_POST_DATA:
-        handleBufferingPostData();
-        break;
-    default:
-        assert(false);
-        break;
+        case State::READING_HEADERS:
+            handleHeaders();
+            break;
+        case State::READING_WEBSOCKET_KEY3:
+            handleWebSocketKey3();
+            break;
+        case State::HANDLING_HIXIE_WEBSOCKET:
+            handleHixieWebSocket();
+            break;
+        case State::HANDLING_HYBI_WEBSOCKET:
+            handleHybiWebSocket();
+            break;
+        case State::BUFFERING_POST_DATA:
+            handleBufferingPostData();
+            break;
+        case State::AWAITING_RESPONSE_BEGIN:
+        case State::SENDING_RESPONSE_BODY:
+        case State::SENDING_RESPONSE_HEADERS:
+            break;
+        default:
+            assert(false);
+            break;
     }
 }
 
@@ -423,9 +447,9 @@
     }
     for (size_t i = 0; i <= _inBuf.size() - 4; ++i) {
         if (_inBuf[i] == '\r' &&
-            _inBuf[i+1] == '\n' &&
-            _inBuf[i+2] == '\r' &&
-            _inBuf[i+3] == '\n') {
+            _inBuf[i + 1] == '\n' &&
+            _inBuf[i + 2] == '\r' &&
+            _inBuf[i + 3] == '\n') {
             if (!processHeaders(&_inBuf[0], &_inBuf[i + 2])) {
                 closeInternal();
                 return;
@@ -474,7 +498,7 @@
     bufferLine("Connection: Upgrade");
     bool allowCrossOrigin = _server.isCrossOriginAllowed(_request->getRequestUri());
     if (_request->hasHeader("Origin") && allowCrossOrigin) {
-        bufferLine("Sec-WebSocket-Origin: " +  _request->getHeader("Origin"));
+        bufferLine("Sec-WebSocket-Origin: " + _request->getHeader("Origin"));
     }
     if (_request->hasHeader("Host")) {
         auto host = _request->getHeader("Host");
@@ -483,20 +507,39 @@
         }
         bufferLine("Sec-WebSocket-Location: ws://" + host + _request->getRequestUri());
     }
+    pickProtocol();
     bufferLine("");
 
     write(&digest, 16, true);
 
-    _state = HANDLING_HIXIE_WEBSOCKET;
+    _state = State::HANDLING_HIXIE_WEBSOCKET;
     _inBuf.erase(_inBuf.begin(), _inBuf.begin() + 8);
     if (_webSocketHandler) {
         _webSocketHandler->onConnect(this);
     }
 }
 
+void Connection::pickProtocol() {
+    static std::string protocolHeader = "Sec-WebSocket-Protocol";
+    if (!_request->hasHeader(protocolHeader) || !_webSocketHandler)
+        return;
+    // Ideally we need o support this header being set multiple times...but the headers don't support that.
+    auto protocols = split(_request->getHeader(protocolHeader), ',');
+    LS_DEBUG(_logger, "Requested protocols:");
+    std::transform(protocols.begin(), protocols.end(), protocols.begin(), trimWhitespace);
+    for (auto&& p : protocols) {
+        LS_DEBUG(_logger, "  " + p);
+    }
+    auto choice = _webSocketHandler->chooseProtocol(protocols);
+    if (choice >= 0 && choice < static_cast<ssize_t>(protocols.size())) {
+        LS_DEBUG(_logger, "Chose protocol " + protocols[choice]);
+        bufferLine(protocolHeader + ": " + protocols[choice]);
+    }
+}
+
 void Connection::handleBufferingPostData() {
     if (_request->consumeContent(_inBuf)) {
-        _state = READING_HEADERS;
+        _state = State::READING_HEADERS;
         if (!handlePageRequest()) {
             closeInternal();
         }
@@ -512,18 +555,21 @@
         return;
     }
     auto messageLength = strlen(webSocketResponse);
-    if (_state == HANDLING_HIXIE_WEBSOCKET) {
+    if (_state == State::HANDLING_HIXIE_WEBSOCKET) {
         uint8_t zero = 0;
-        if (!write(&zero, 1, false)) return;
-        if (!write(webSocketResponse, messageLength, false)) return;
+        if (!write(&zero, 1, false))
+            return;
+        if (!write(webSocketResponse, messageLength, false))
+            return;
         uint8_t effeff = 0xff;
         write(&effeff, 1, true);
         return;
     }
-    sendHybi(HybiPacketDecoder::OPCODE_TEXT, reinterpret_cast<const uint8_t*>(webSocketResponse), messageLength);
+    sendHybi(static_cast<uint8_t>(HybiPacketDecoder::Opcode::Text),
+             reinterpret_cast<const uint8_t*>(webSocketResponse), messageLength);
 }
 
-void Connection::send(const uint8_t* data, size_t length) {
+void Connection::send(const uint8_t* webSocketResponse, size_t length) {
     _server.checkThread();
     if (_shutdown) {
         if (_shutdownByUser) {
@@ -531,29 +577,51 @@
         }
         return;
     }
-    if (_state == HANDLING_HIXIE_WEBSOCKET) {
+    if (_state == State::HANDLING_HIXIE_WEBSOCKET) {
         LS_ERROR(_logger, "Hixie does not support binary");
         return;
     }
-    sendHybi(HybiPacketDecoder::OPCODE_BINARY, data, length);
+    sendHybi(static_cast<uint8_t>(HybiPacketDecoder::Opcode::Binary), webSocketResponse, length);
 }
 
-void Connection::sendHybi(int opcode, const uint8_t* webSocketResponse, size_t messageLength) {
+void Connection::sendHybi(uint8_t opcode, const uint8_t* webSocketResponse, size_t messageLength) {
     uint8_t firstByte = 0x80 | opcode;
-    if (!write(&firstByte, 1, false)) return;
+    if (_perMessageDeflate)
+        firstByte |= 0x40;
+    if (!write(&firstByte, 1, false))
+        return;
+
+    if (_perMessageDeflate) {
+        std::vector<uint8_t> compressed;
+
+        zlibContext.deflate(webSocketResponse, messageLength, compressed);
+
+        LS_DEBUG(_logger, "Compression result: " << messageLength << " bytes -> " << compressed.size() << " bytes");
+        sendHybiData(compressed.data(), compressed.size());
+    } else {
+        sendHybiData(webSocketResponse, messageLength);
+    }
+}
+
+void Connection::sendHybiData(const uint8_t* webSocketResponse, size_t messageLength) {
     if (messageLength < 126) {
         uint8_t nextByte = messageLength; // No MASK bit set.
-        if (!write(&nextByte, 1, false)) return;
+        if (!write(&nextByte, 1, false))
+            return;
     } else if (messageLength < 65536) {
         uint8_t nextByte = 126; // No MASK bit set.
-        if (!write(&nextByte, 1, false)) return;
+        if (!write(&nextByte, 1, false))
+            return;
         auto lengthBytes = htons(messageLength);
-        if (!write(&lengthBytes, 2, false)) return;
+        if (!write(&lengthBytes, 2, false))
+            return;
     } else {
         uint8_t nextByte = 127; // No MASK bit set.
-        if (!write(&nextByte, 1, false)) return;
+        if (!write(&nextByte, 1, false))
+            return;
         uint64_t lengthBytes = __bswap_64(messageLength);
-        if (!write(&lengthBytes, 8, false)) return;
+        if (!write(&lengthBytes, 8, false))
+            return;
     }
     write(webSocketResponse, messageLength, true);
 }
@@ -570,7 +638,7 @@
     size_t messageStart = 0;
     while (messageStart < _inBuf.size()) {
         if (_inBuf[messageStart] != 0) {
-            LS_WARNING(_logger, "Error in WebSocket input stream (got " << (int)_inBuf[messageStart] << ")");
+            LS_WARNING(_logger, "Error in WebSocket input stream (got " << (int) _inBuf[messageStart] << ")");
             closeInternal();
             return;
         }
@@ -607,31 +675,67 @@
     bool done = false;
     while (!done) {
         std::vector<uint8_t> decodedMessage;
-        switch (decoder.decodeNextMessage(decodedMessage)) {
-        default:
-            closeInternal();
-            LS_WARNING(_logger, "Unknown HybiPacketDecoder state");
-            return;
-        case HybiPacketDecoder::Error:
-            closeInternal();
-            return;
-        case HybiPacketDecoder::TextMessage:
-            decodedMessage.push_back(0);  // avoids a copy
-            handleWebSocketTextMessage(reinterpret_cast<const char*>(&decodedMessage[0]));
-            break;
-        case HybiPacketDecoder::BinaryMessage:
-            handleWebSocketBinaryMessage(decodedMessage);
-            break;
-        case HybiPacketDecoder::Ping:
-            sendHybi(HybiPacketDecoder::OPCODE_PONG, &decodedMessage[0], decodedMessage.size());
-            break;
-        case HybiPacketDecoder::NoMessage:
-            done = true;
-            break;
-        case HybiPacketDecoder::Close:
-            LS_DEBUG(_logger, "Received WebSocket close");
-            closeInternal();
-            return;
+        bool deflateNeeded = false;
+
+        auto messageState = decoder.decodeNextMessage(decodedMessage, deflateNeeded);
+
+        if (deflateNeeded) {
+            if (!_perMessageDeflate) {
+                LS_WARNING(_logger, "Received deflated hybi frame but deflate wasn't negotiated");
+                closeInternal();
+                return;
+            }
+
+            size_t compressed_size = decodedMessage.size();
+
+            std::vector<uint8_t> decompressed;
+            int zlibError;
+
+            // Note: inflate() alters decodedMessage
+            bool success = zlibContext.inflate(decodedMessage, decompressed, zlibError);
+
+            if (!success) {
+                LS_WARNING(_logger, "Decompression error from zlib: " << zlibError);
+                closeInternal();
+                return;
+            }
+
+            LS_DEBUG(_logger, "Decompression result: " << compressed_size << " bytes -> " << decodedMessage.size() << " bytes");
+
+            decodedMessage.swap(decompressed);
+        }
+
+
+        switch (messageState) {
+            default:
+                closeInternal();
+                LS_WARNING(_logger, "Unknown HybiPacketDecoder state");
+                return;
+            case HybiPacketDecoder::MessageState::Error:
+                closeInternal();
+                return;
+            case HybiPacketDecoder::MessageState::TextMessage:
+                decodedMessage.push_back(0); // avoids a copy
+                handleWebSocketTextMessage(reinterpret_cast<const char*>(&decodedMessage[0]));
+                break;
+            case HybiPacketDecoder::MessageState::BinaryMessage:
+                handleWebSocketBinaryMessage(decodedMessage);
+                break;
+            case HybiPacketDecoder::MessageState::Ping:
+                sendHybi(static_cast<uint8_t>(HybiPacketDecoder::Opcode::Pong),
+                         &decodedMessage[0], decodedMessage.size());
+                break;
+            case HybiPacketDecoder::MessageState::Pong:
+                // Pongs can be sent unsolicited (MSIE and Edge do this)
+                // The spec says to ignore them.
+                break;
+            case HybiPacketDecoder::MessageState::NoMessage:
+                done = true;
+                break;
+            case HybiPacketDecoder::MessageState::Close:
+                LS_DEBUG(_logger, "Received WebSocket close");
+                closeInternal();
+                return;
         }
     }
     if (decoder.numBytesDecoded() != 0) {
@@ -658,7 +762,7 @@
 }
 
 bool Connection::sendError(ResponseCode errorCode, const std::string& body) {
-    assert(_state != HANDLING_HIXIE_WEBSOCKET);
+    assert(_state != State::HANDLING_HIXIE_WEBSOCKET);
     auto errorNumber = static_cast<int>(errorCode);
     auto message = ::name(errorCode);
     bufferResponseAndCommonHeaders(errorCode);
@@ -672,8 +776,9 @@
     } else {
         std::stringstream documentStr;
         documentStr << "<html><head><title>" << errorNumber << " - " << message << "</title></head>"
-                << "<body><h1>" << errorNumber << " - " << message << "</h1>"
-                << "<div>" << body << "</div><hr/><div><i>Powered by seasocks</i></div></body></html>";
+                    << "<body><h1>" << errorNumber << " - " << message << "</h1>"
+                    << "<div>" << body << "</div><hr/><div><i>Powered by "
+                                          "<a href=\"https://github.com/mattgodbolt/seasocks\">Seasocks</a></i></div></body></html>";
         document = documentStr.str();
     }
     bufferLine("Content-Length: " + toString(document.length()));
@@ -717,7 +822,7 @@
     // Be careful about lifetimes though and multiple requests coming in, should
     // we ever support HTTP pipelining and/or long-lived requests.
     char* requestLine = extractLine(first, last);
-    assert(requestLine != NULL);
+    assert(requestLine != nullptr);
 
     LS_ACCESS(_logger, "Request: " << requestLine);
 
@@ -730,12 +835,12 @@
         return sendBadRequest("Malformed request line");
     }
     const char* requestUri = shift(requestLine);
-    if (requestUri == NULL) {
+    if (requestUri == nullptr) {
         return sendBadRequest("Malformed request line");
     }
 
     const char* httpVersion = shift(requestLine);
-    if (httpVersion == NULL) {
+    if (httpVersion == nullptr) {
         return sendBadRequest("Malformed request line");
     }
     if (strcmp(httpVersion, "HTTP/1.1") != 0) {
@@ -747,26 +852,20 @@
 
     HeaderMap headers(31);
     while (first < last) {
-        char* colonPos = NULL;
+        char* colonPos = nullptr;
         char* headerLine = extractLine(first, last, &colonPos);
-        assert(headerLine != NULL);
-        if (colonPos == NULL) {
+        assert(headerLine != nullptr);
+        if (colonPos == nullptr) {
             return sendBadRequest("Malformed header");
         }
         *colonPos = 0;
         const char* key = headerLine;
         const char* value = skipWhitespace(colonPos + 1);
         LS_DEBUG(_logger, "Key: " << key << " || " << value);
-#if HAVE_UNORDERED_MAP_EMPLACE
         headers.emplace(key, value);
-#else
-        headers.insert(std::make_pair(key, value));
-#endif
     }
 
-    if (headers.count("Connection") && headers.count("Upgrade")
-            && hasConnectionType(headers["Connection"], "Upgrade")
-            && caseInsensitiveSame(headers["Upgrade"], "websocket")) {
+    if (headers.count("Connection") && headers.count("Upgrade") && hasConnectionType(headers["Connection"], "Upgrade") && caseInsensitiveSame(headers["Upgrade"], "websocket")) {
         LS_INFO(_logger, "Websocket request for " << requestUri << "'");
         if (verb != Request::Verb::Get) {
             return sendBadRequest("Non-GET WebSocket request");
@@ -777,23 +876,30 @@
             return send404();
         }
         verb = Request::Verb::WebSocket;
+
+        if (_server.server().getPerMessageDeflateEnabled() && headers.count("Sec-WebSocket-Extensions")) {
+            parsePerMessageDeflateHeader(headers["Sec-WebSocket-Extensions"]);
+        }
     }
 
-    _request.reset(new PageRequest(_address, requestUri, verb, std::move(headers)));
+    _request = std::make_unique<PageRequest>(_address, requestUri, _server.server(),
+                                             verb, std::move(headers));
 
-    const EmbeddedContent *embedded = findEmbeddedContent(requestUri);
+    const EmbeddedContent* embedded = findEmbeddedContent(requestUri);
     if (verb == Request::Verb::Get && embedded) {
         // MRG: one day, this could be a request handler.
         return sendData(getContentType(requestUri), embedded->data, embedded->length);
+    } else if (verb == Request::Verb::Head && embedded) {
+        return sendHeader(getContentType(requestUri), embedded->length);
     }
 
-    if (_request->contentLength() > MaxBufferSize) {
+    if (_request->contentLength() > _server.clientBufferSize()) {
         return sendBadRequest("Content length too long");
     }
     if (_request->contentLength() == 0) {
         return handlePageRequest();
     }
-    _state = BUFFERING_POST_DATA;
+    _state = State::BUFFERING_POST_DATA;
     return true;
 }
 
@@ -811,14 +917,14 @@
     auto uri = _request->getRequestUri();
     if (!response && _request->verb() == Request::Verb::WebSocket) {
         _webSocketHandler = _server.getWebSocketHandler(uri.c_str());
-        auto webSocketVersion = atoi(_request->getHeader("Sec-WebSocket-Version").c_str());
+        const auto webSocketVersion = std::stoi(_request->getHeader("Sec-WebSocket-Version"));
         if (!_webSocketHandler) {
             LS_WARNING(_logger, "Couldn't find WebSocket end point for '" << uri << "'");
             return send404();
         }
         if (webSocketVersion == 0) {
             // Hixie
-            _state = READING_WEBSOCKET_KEY3;
+            _state = State::READING_WEBSOCKET_KEY3;
             return true;
         }
         auto hybiKey = _request->getHeader("Sec-WebSocket-Key");
@@ -828,47 +934,108 @@
 }
 
 bool Connection::sendResponse(std::shared_ptr<Response> response) {
-    const auto requestUri = _request->getRequestUri();
     if (response == Response::unhandled()) {
         return sendStaticData();
     }
-    if (response->responseCode() == ResponseCode::NotFound) {
-        // TODO: better here; we use this purely to serve our own embedded content.
-        return send404();
-    } else if (!isOk(response->responseCode())) {
-        return sendError(response->responseCode(), response->payload());
-    }
-
-    bufferResponseAndCommonHeaders(response->responseCode());
-    bufferLine("Content-Length: " + toString(response->payloadSize()));
-    bufferLine("Content-Type: " + response->contentType());
-    if (response->keepConnectionAlive()) {
-        bufferLine("Connection: keep-alive");
-    } else {
-        bufferLine("Connection: close");
-    }
-    bufferLine("Last-Modified: " + now());
-    bufferLine("Cache-Control: no-store");
-    bufferLine("Pragma: no-cache");
-    bufferLine("Expires: " + now());
-    auto headers = response->getAdditionalHeaders();
-    for (auto it = headers.begin(); it != headers.end(); ++it) {
-        bufferLine(it->first + ": " + it->second);
-    }
-    bufferLine("");
-
-    if (!write(response->payload(), response->payloadSize(), true)) {
-        return false;
-    }
-    if (!response->keepConnectionAlive()) {
-        closeWhenEmpty();
-    }
+    assert(_response.get() == nullptr);
+    _state = State::AWAITING_RESPONSE_BEGIN;
+    _transferEncoding = TransferEncoding::Raw;
+    _chunk = 0;
+    _response = response;
+    _response->handle(_writer);
     return true;
 }
 
+void Connection::error(ResponseCode responseCode, const std::string& payload) {
+    _server.checkThread();
+    if (_state != State::AWAITING_RESPONSE_BEGIN) {
+        LS_ERROR(_logger, "error() called when in wrong state");
+        return;
+    }
+    if (isOk(responseCode)) {
+        LS_ERROR(_logger, "error() called with a non-error code");
+    }
+    if (responseCode == ResponseCode::NotFound) {
+        // TODO: better here; we use this purely to serve our own embedded content.
+        send404();
+    } else {
+        sendError(responseCode, payload);
+    }
+}
+
+void Connection::begin(ResponseCode responseCode, TransferEncoding encoding) {
+    _server.checkThread();
+    if (_state != State::AWAITING_RESPONSE_BEGIN) {
+        LS_ERROR(_logger, "begin() called when in wrong state");
+        return;
+    }
+    _state = State::SENDING_RESPONSE_HEADERS;
+    bufferResponseAndCommonHeaders(responseCode);
+    _transferEncoding = encoding;
+    if (_transferEncoding == TransferEncoding::Chunked) {
+        bufferLine("Transfer-encoding: chunked");
+    }
+}
+
+void Connection::header(const std::string& header, const std::string& value) {
+    _server.checkThread();
+    if (_state != State::SENDING_RESPONSE_HEADERS) {
+        LS_ERROR(_logger, "header() called when in wrong state");
+        return;
+    }
+    bufferLine(header + ": " + value);
+}
+void Connection::payload(const void* data, size_t size, bool flush) {
+    _server.checkThread();
+    if (_state == State::SENDING_RESPONSE_HEADERS) {
+        bufferLine("");
+        _state = State::SENDING_RESPONSE_BODY;
+    } else if (_state != State::SENDING_RESPONSE_BODY) {
+        LS_ERROR(_logger, "payload() called when in wrong state");
+        return;
+    }
+    if (size && _transferEncoding == TransferEncoding::Chunked) {
+        writeChunkHeader(size);
+    }
+    write(data, size, flush);
+}
+
+void Connection::writeChunkHeader(size_t size) {
+    std::ostringstream lengthStr;
+    if (_chunk)
+        lengthStr << "\r\n";
+    lengthStr << std::hex << size << "\r\n";
+    auto length = lengthStr.str();
+    _chunk++;
+    write(length.c_str(), length.size(), false);
+}
+
+void Connection::finish(bool keepConnectionOpen) {
+    _server.checkThread();
+    if (_state == State::SENDING_RESPONSE_HEADERS) {
+        bufferLine("");
+    } else if (_state != State::SENDING_RESPONSE_BODY) {
+        LS_ERROR(_logger, "finish() called when in wrong state");
+        return;
+    }
+    if (_transferEncoding == TransferEncoding::Chunked) {
+        writeChunkHeader(0);
+        write("\r\n", 2, false);
+    }
+
+    flush();
+
+    if (!keepConnectionOpen) {
+        closeWhenEmpty();
+    }
+
+    _state = State::READING_HEADERS;
+    _response.reset();
+}
+
 bool Connection::handleHybiHandshake(
-        int webSocketVersion,
-        const std::string& webSocketKey) {
+    int webSocketVersion,
+    const std::string& webSocketKey) {
     if (webSocketVersion != 8 && webSocketVersion != 13) {
         return sendBadRequest("Invalid websocket version");
     }
@@ -880,16 +1047,33 @@
     bufferLine("Upgrade: websocket");
     bufferLine("Connection: Upgrade");
     bufferLine("Sec-WebSocket-Accept: " + getAcceptKey(webSocketKey));
+    if (_perMessageDeflate)
+        bufferLine("Sec-WebSocket-Extensions: permessage-deflate");
+    pickProtocol();
     bufferLine("");
     flush();
 
     if (_webSocketHandler) {
         _webSocketHandler->onConnect(this);
     }
-    _state = HANDLING_HYBI_WEBSOCKET;
+    _state = State::HANDLING_HYBI_WEBSOCKET;
     return true;
 }
 
+void Connection::parsePerMessageDeflateHeader(const std::string& header) {
+    for (auto& extField : seasocks::split(header, ';')) {
+        while (!extField.empty() && isspace(extField[0])) {
+            extField = extField.substr(1);
+        }
+
+        if (seasocks::caseInsensitiveSame(extField, "permessage-deflate")) {
+            LS_INFO(_logger, "Enabling per-message deflate");
+            _perMessageDeflate = true;
+            zlibContext.initialise();
+        }
+    }
+}
+
 bool Connection::parseRange(const std::string& rangeStr, Range& range) const {
     size_t minusPos = rangeStr.find('-');
     if (minusPos == std::string::npos) {
@@ -898,15 +1082,15 @@
     }
     if (minusPos == 0) {
         // A range like "-500" means 500 bytes from end of file to end.
-        range.start = atoi(rangeStr.c_str());
+        range.start = std::stoi(rangeStr);
         range.end = std::numeric_limits<long>::max();
         return true;
     } else {
-        range.start = atoi(rangeStr.substr(0, minusPos).c_str());
-        if (minusPos == rangeStr.size()-1) {
+        range.start = std::stoi(rangeStr.substr(0, minusPos));
+        if (minusPos == rangeStr.size() - 1) {
             range.end = std::numeric_limits<long>::max();
         } else {
-            range.end = atoi(rangeStr.substr(minusPos + 1).c_str());
+            range.end = std::stoi(rangeStr.substr(minusPos + 1));
         }
         return true;
     }
@@ -920,9 +1104,9 @@
         return false;
     }
     auto rangesText = split(range.substr(expectedPrefix.length()), ',');
-    for (auto it = rangesText.begin(); it != rangesText.end(); ++it) {
+    for (auto& it : rangesText) {
         Range r;
-        if (!parseRange(*it, r)) {
+        if (!parseRange(it, r)) {
             return false;
         }
         ranges.push_back(r);
@@ -937,7 +1121,7 @@
         // Easy case: a non-range request.
         bufferResponseAndCommonHeaders(ResponseCode::Ok);
         bufferLine("Content-Length: " + toString(fileSize));
-        return { Range { 0, fileSize - 1 } };
+        return {Range{0, fileSize - 1}};
     }
 
     // Partial content request.
@@ -946,8 +1130,7 @@
     std::ostringstream rangeLine;
     rangeLine << "Content-Range: bytes ";
     std::list<Range> sendRanges;
-    for (auto rangeIter = origRanges.cbegin(); rangeIter != origRanges.cend(); ++rangeIter) {
-        Range actualRange = *rangeIter;
+    for (auto actualRange : origRanges) {
         if (actualRange.start < 0) {
             actualRange.start += fileSize;
         }
@@ -973,26 +1156,27 @@
     auto rangeHeader = getHeader("Range");
     // Trim any trailing queries.
     size_t queryPos = path.find('?');
-    if (queryPos != path.npos) {
+    if (queryPos != std::string::npos) {
         path.resize(queryPos);
     }
     if (*path.rbegin() == '/') {
         path += "index.html";
     }
-    RaiiFd input(path.c_str());
-    struct stat stat;
-    if (!input.ok() || ::fstat(input, &stat) == -1) {
+
+    RaiiFd input{::open(path.c_str(), O_RDONLY)};
+    struct stat fileStat;
+    if (!input.ok() || ::fstat(input, &fileStat) == -1) {
         return send404();
     }
     std::list<Range> ranges;
     if (!rangeHeader.empty() && !parseRanges(rangeHeader, ranges)) {
         return sendBadRequest("Bad range header");
     }
-    ranges = processRangesForStaticData(ranges, stat.st_size);
+    ranges = processRangesForStaticData(ranges, fileStat.st_size);
     bufferLine("Content-Type: " + getContentType(path));
     bufferLine("Connection: keep-alive");
     bufferLine("Accept-Ranges: bytes");
-    bufferLine("Last-Modified: " + webtime(stat.st_mtime));
+    bufferLine("Last-Modified: " + webtime(fileStat.st_mtime));
     if (!isCacheable(path)) {
         bufferLine("Cache-Control: no-store");
         bufferLine("Pragma: no-cache");
@@ -1003,12 +1187,12 @@
         return false;
     }
 
-    for (auto rangeIter = ranges.cbegin(); rangeIter != ranges.cend(); ++rangeIter) {
-        if (::lseek(input, rangeIter->start, SEEK_SET) == -1) {
+    for (auto range : ranges) {
+        if (::lseek(input, range.start, SEEK_SET) == -1) {
             // We've (probably) already sent data.
             return false;
         }
-        auto bytesLeft = rangeIter->length();
+        auto bytesLeft = range.length();
         while (bytesLeft) {
             char buf[ReadWriteBufferSize];
             auto bytesRead = ::read(input, buf, std::min(sizeof(buf), bytesLeft));
@@ -1027,6 +1211,14 @@
     return true;
 }
 
+bool Connection::sendHeader(const std::string& type, size_t size) {
+    bufferResponseAndCommonHeaders(ResponseCode::Ok);
+    bufferLine("Content-Type: " + type);
+    bufferLine("Content-Length: " + toString(size));
+    bufferLine("Connection: keep-alive");
+    return bufferLine("");
+}
+
 bool Connection::sendData(const std::string& type, const char* start, size_t size) {
     bufferResponseAndCommonHeaders(ResponseCode::Ok);
     bufferLine("Content-Type: " + type);
@@ -1043,7 +1235,7 @@
     auto response = std::string("HTTP/1.1 " + toString(responseCodeInt) + " " + responseCodeName);
     LS_ACCESS(_logger, "Response: " << response);
     bufferLine(response);
-    bufferLine("Server: " SEASOCKS_VERSION_STRING);
+    bufferLine("Server: " + std::string(Config::version));
     bufferLine("Date: " + now());
     bufferLine("Access-Control-Allow-Origin: *");
 }
@@ -1053,7 +1245,7 @@
         return;
     }
     const int secondsToLinger = 1;
-    struct linger linger = { true, secondsToLinger };
+    struct linger linger = {true, secondsToLinger};
     if (::setsockopt(_fd, SOL_SOCKET, SO_LINGER, &linger, sizeof(linger)) == -1) {
         LS_INFO(_logger, "Unable to set linger on socket");
     }
@@ -1072,4 +1264,8 @@
     return _request ? _request->getRequestUri() : empty;
 }
 
-}  // seasocks
+Server& Connection::server() const {
+    return _server.server();
+}
+
+} // seasocks
diff --git a/src/main/c/HybiAccept.cpp b/src/main/c/HybiAccept.cpp
index 8c2dbde..fcf05fc 100644
--- a/src/main/c/HybiAccept.cpp
+++ b/src/main/c/HybiAccept.cpp
@@ -1,26 +1,26 @@
-// Copyright (c) 2013, Matt Godbolt
+// Copyright (c) 2013-2017, Matt Godbolt
 // All rights reserved.
-// 
-// Redistribution and use in source and binary forms, with or without 
+//
+// 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 
+//
+// 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 
+//
+// 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 
+//
+// 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/Base64.h"
@@ -32,7 +32,9 @@
 
 namespace seasocks {
 
-static const std::string magicString("258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
+namespace {
+const char* magicString = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
+}
 
 std::string getAcceptKey(const std::string& challenge) {
     auto fullString = challenge + magicString;
@@ -40,8 +42,8 @@
     hasher.Input(fullString.c_str(), fullString.size());
     unsigned hash[5];
     hasher.Result(hash);
-    for (int i = 0; i < 5; ++i) {
-        hash[i] = htonl(hash[i]);
+    for (unsigned int& i : hash) {
+        i = htonl(i);
     }
     return base64Encode(hash, sizeof(hash));
 }
diff --git a/src/main/c/HybiPacketDecoder.cpp b/src/main/c/HybiPacketDecoder.cpp
index f0fdefe..80977cd 100644
--- a/src/main/c/HybiPacketDecoder.cpp
+++ b/src/main/c/HybiPacketDecoder.cpp
@@ -1,97 +1,123 @@
-// Copyright (c) 2013, Matt Godbolt
+// Copyright (c) 2013-2017, Matt Godbolt
 // All rights reserved.
-// 
-// Redistribution and use in source and binary forms, with or without 
+//
+// 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 
+//
+// 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 
+//
+// 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 
+//
+// 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/HybiPacketDecoder.h"
 #include "internal/LogStream.h"
 
 #include <arpa/inet.h>
+#include <byteswap.h>
+#include <cstring>
 
 namespace seasocks {
 
-HybiPacketDecoder::HybiPacketDecoder(Logger& logger, const std::vector<uint8_t>& buffer) :
-    _logger(logger),
-    _buffer(buffer),
-    _messageStart(0) {
+HybiPacketDecoder::HybiPacketDecoder(Logger& logger,
+                                     const std::vector<uint8_t>& buffer)
+        : _logger(logger),
+          _buffer(buffer),
+          _messageStart(0) {
 }
 
-HybiPacketDecoder::MessageState HybiPacketDecoder::decodeNextMessage(std::vector<uint8_t>& messageOut) {
+HybiPacketDecoder::MessageState HybiPacketDecoder::decodeNextMessage(
+    std::vector<uint8_t>& messageOut, bool& deflateNeeded) {
     if (_messageStart + 1 >= _buffer.size()) {
-        return NoMessage;
+        return MessageState::NoMessage;
     }
     if ((_buffer[_messageStart] & 0x80) == 0) {
         // FIN bit is not clear...
         // TODO: support
         LS_WARNING(&_logger, "Received hybi frame without FIN bit set - unsupported");
-        return Error;
+        return MessageState::Error;
     }
-    if ((_buffer[_messageStart] & (7<<4)) != 0) {
+
+    auto reservedBits = _buffer[_messageStart] & (7 << 4);
+    if ((reservedBits & 0x30) != 0) {
         LS_WARNING(&_logger, "Received hybi frame with reserved bits set - error");
-        return Error;
+        return MessageState::Error;
     }
-    auto opcode = _buffer[_messageStart] & 0xf;
-    size_t payloadLength = _buffer[_messageStart + 1] & 0x7f;
+
+    deflateNeeded = !!(reservedBits & 0x40);
+
+    auto opcode = static_cast<Opcode>(_buffer[_messageStart] & 0xf);
+    size_t payloadLength = _buffer[_messageStart + 1] & 0x7fu;
     auto maskBit = _buffer[_messageStart + 1] & 0x80;
     auto ptr = _messageStart + 2;
     if (payloadLength == 126) {
-        if (_buffer.size() < 4) { return NoMessage; }
-        payloadLength = htons(*reinterpret_cast<const uint16_t*>(&_buffer[ptr]));
+        if (_buffer.size() < 4) {
+            return MessageState::NoMessage;
+        }
+        uint16_t raw_length;
+        memcpy(&raw_length, &_buffer[ptr], sizeof(raw_length));
+        payloadLength = htons(raw_length);
         ptr += 2;
     } else if (payloadLength == 127) {
-        if (_buffer.size() < 10) { return NoMessage; }
-        payloadLength = __bswap_64(*reinterpret_cast<const uint64_t*>(&_buffer[ptr]));
+        if (_buffer.size() < 10) {
+            return MessageState::NoMessage;
+        }
+        uint64_t raw_length;
+        memcpy(&raw_length, &_buffer[ptr], sizeof(raw_length));
+        payloadLength = __bswap_64(raw_length);
         ptr += 8;
     }
     uint32_t mask = 0;
     if (maskBit) {
         // MASK is set.
-        if (_buffer.size() < ptr + 4) { return NoMessage; }
-        mask = htonl(*reinterpret_cast<const uint32_t*>(&_buffer[ptr]));
+        if (_buffer.size() < ptr + 4) {
+            return MessageState::NoMessage;
+        }
+        uint32_t raw_length;
+        memcpy(&raw_length, &_buffer[ptr], sizeof(raw_length));
+        mask = htonl(raw_length);
         ptr += 4;
     }
     auto bytesLeftInBuffer = _buffer.size() - ptr;
-    if (payloadLength > bytesLeftInBuffer) { return NoMessage; }
+    if (payloadLength > bytesLeftInBuffer) {
+        return MessageState::NoMessage;
+    }
 
     messageOut.clear();
     messageOut.reserve(payloadLength);
     for (auto i = 0u; i < payloadLength; ++i) {
         auto byteShift = (3 - (i & 3)) * 8;
-        messageOut.push_back(static_cast<char>((_buffer[ptr++] ^ (mask >> byteShift)) & 0xff));
+        messageOut.push_back(static_cast<uint8_t>((_buffer[ptr++] ^ (mask >> byteShift)) & 0xff));
     }
     _messageStart = ptr;
     switch (opcode) {
-    default:
-        LS_WARNING(&_logger, "Received hybi frame with unknown opcode " << opcode);
-        return Error;
-    case OPCODE_TEXT:
-        return TextMessage;
-    case OPCODE_BINARY:
-        return BinaryMessage;
-    case OPCODE_PING:
-        return Ping;
-    case OPCODE_CLOSE:
-        return Close;
+        default:
+            LS_WARNING(&_logger, "Received hybi frame with unknown opcode "
+                                     << static_cast<int>(opcode));
+            return MessageState::Error;
+        case Opcode::Text:
+            return MessageState::TextMessage;
+        case Opcode::Binary:
+            return MessageState::BinaryMessage;
+        case Opcode::Ping:
+            return MessageState::Ping;
+        case Opcode::Pong:
+            return MessageState::Pong;
+        case Opcode::Close:
+            return MessageState::Close;
     }
 }
 
diff --git a/src/main/c/Logger.cpp b/src/main/c/Logger.cpp
index 347ae30..bb69672 100644
--- a/src/main/c/Logger.cpp
+++ b/src/main/c/Logger.cpp
@@ -1,76 +1,78 @@
-// Copyright (c) 2013, Matt Godbolt
+// Copyright (c) 2013-2017, Matt Godbolt
 // All rights reserved.
-// 
-// Redistribution and use in source and binary forms, with or without 
+//
+// 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 
+//
+// 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 
+//
+// 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 
+//
+// 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/Debug.h"
 
 #include "seasocks/Logger.h"
 
-#include <stdarg.h>
-#include <stdio.h>
+#include <cstdarg>
+#include <cstdio>
 
 namespace seasocks {
 
-const int MAX_MESSAGE_LENGTH = 1024;
+constexpr int MAX_MESSAGE_LENGTH = 1024;
 
-#define PRINT_TO_MESSAGEBUF() \
-    char messageBuf[MAX_MESSAGE_LENGTH]; \
-    va_list args; \
-    va_start(args, message); \
+#define PRINT_TO_MESSAGEBUF()                                 \
+    char messageBuf[MAX_MESSAGE_LENGTH];                      \
+    va_list args;                                             \
+    va_start(args, message);                                  \
     vsnprintf(messageBuf, MAX_MESSAGE_LENGTH, message, args); \
     va_end(args)
 
 void Logger::debug(const char* message, ...) {
 #ifdef LOG_DEBUG_INFO
     PRINT_TO_MESSAGEBUF();
-    log(DEBUG, messageBuf);
+    log(Level::Debug, messageBuf);
+#else
+    (void) message;
 #endif
 }
 
 void Logger::access(const char* message, ...) {
     PRINT_TO_MESSAGEBUF();
-    log(ACCESS, messageBuf);
+    log(Level::Access, messageBuf);
 }
 
 void Logger::info(const char* message, ...) {
     PRINT_TO_MESSAGEBUF();
-    log(INFO, messageBuf);
+    log(Level::Info, messageBuf);
 }
 
 void Logger::warning(const char* message, ...) {
     PRINT_TO_MESSAGEBUF();
-    log(WARNING, messageBuf);
+    log(Level::Warning, messageBuf);
 }
 
 void Logger::error(const char* message, ...) {
     PRINT_TO_MESSAGEBUF();
-    log(ERROR, messageBuf);
+    log(Level::Error, messageBuf);
 }
 
 void Logger::severe(const char* message, ...) {
     PRINT_TO_MESSAGEBUF();
-    log(SEVERE, messageBuf);
+    log(Level::Severe, messageBuf);
 }
 
-}  // namespace seasocks
+} // namespace seasocks
diff --git a/src/main/c/PageRequest.cpp b/src/main/c/PageRequest.cpp
index a2a5313..ac59b45 100644
--- a/src/main/c/PageRequest.cpp
+++ b/src/main/c/PageRequest.cpp
@@ -1,26 +1,26 @@
-// Copyright (c) 2013, Matt Godbolt
+// Copyright (c) 2013-2017, Matt Godbolt
 // All rights reserved.
-// 
-// Redistribution and use in source and binary forms, with or without 
+//
+// 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 
+//
+// 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 
+//
+// 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 
+//
+// 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/PageRequest.h"
@@ -31,20 +31,24 @@
 namespace seasocks {
 
 PageRequest::PageRequest(
-        const sockaddr_in& remoteAddress,
-        const std::string& requestUri,
-        Verb verb,
-        HeaderMap&& headers) :
-            _credentials(std::shared_ptr<Credentials>(new Credentials())),
-            _remoteAddress(remoteAddress),
-            _requestUri(requestUri),
-            _verb(verb),
-            _headers(std::move(headers)),
-            _contentLength(getIntHeader("Content-Length")) {
+    const sockaddr_in& remoteAddress,
+    const std::string& requestUri,
+    Server& server,
+    Verb verb,
+    HeaderMap&& headers)
+        : _credentials(std::make_shared<Credentials>()),
+          _remoteAddress(remoteAddress),
+          _requestUri(requestUri),
+          _server(server),
+          _verb(verb),
+          _headers(std::move(headers)),
+          _contentLength(getUintHeader("Content-Length")) {
 }
 
 bool PageRequest::consumeContent(std::vector<uint8_t>& buffer) {
-    if (buffer.size() < _contentLength) return false;
+    if (buffer.size() < _contentLength) {
+        return false;
+    }
     if (buffer.size() == _contentLength) {
         _content.swap(buffer);
     } else {
@@ -54,9 +58,16 @@
     return true;
 }
 
-int PageRequest::getIntHeader(const std::string& name) const {
-    auto iter = _headers.find(name);
-    return iter == _headers.end() ? 0 : atoi(iter->second.c_str());
+size_t PageRequest::getUintHeader(const std::string& name) const {
+    const auto iter = _headers.find(name);
+    if (iter == _headers.end()) {
+        return 0u;
+    }
+    const auto val = std::stoi(iter->second);
+    if (val < 0) {
+        return 0u;
+    }
+    return static_cast<size_t>(val);
 }
 
-}  // namespace seasocks
+} // namespace seasocks
diff --git a/src/main/c/Response.cpp b/src/main/c/Response.cpp
index 2eddfe2..1e364e5 100644
--- a/src/main/c/Response.cpp
+++ b/src/main/c/Response.cpp
@@ -1,34 +1,32 @@
-// Copyright (c) 2013, Matt Godbolt
+// Copyright (c) 2013-2017, Matt Godbolt
 // All rights reserved.
-// 
-// Redistribution and use in source and binary forms, with or without 
+//
+// 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 
+//
+// 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 
+//
+// 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 
+//
+// 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/ConcreteResponse.h"
 
 #include "seasocks/Response.h"
 
-using namespace seasocks;
-
 namespace seasocks {
 
 std::shared_ptr<Response> Response::unhandled() {
@@ -37,27 +35,38 @@
 }
 
 std::shared_ptr<Response> Response::notFound() {
-    static std::shared_ptr<Response> notFound(new ConcreteResponse(ResponseCode::NotFound, "Not found", "text/plain", Response::Headers(), false));
+    static std::shared_ptr<Response> notFound = std::make_shared<ConcreteResponse>(
+        ResponseCode::NotFound,
+        "Not found", "text/plain",
+        SynchronousResponse::Headers(), false);
     return notFound;
 }
 
-std::shared_ptr<Response> Response::error(ResponseCode code, const std::string& reason) {
-    return std::shared_ptr<Response>(new ConcreteResponse(code, reason, "text/plain", Response::Headers(), false));
+std::shared_ptr<Response> Response::error(ResponseCode code, const std::string& error) {
+    return std::make_shared<ConcreteResponse>(
+        code, error, "text/plain",
+        SynchronousResponse::Headers(), false);
 }
 
 std::shared_ptr<Response> Response::textResponse(const std::string& response) {
-    return std::shared_ptr<Response>(
-            new ConcreteResponse(ResponseCode::Ok, response, "text/plain", Response::Headers(), true));
+    return std::make_shared<ConcreteResponse>(
+        ResponseCode::Ok,
+        response, "text/plain",
+        SynchronousResponse::Headers(), true);
 }
 
 std::shared_ptr<Response> Response::jsonResponse(const std::string& response) {
-    return std::shared_ptr<Response>(
-            new ConcreteResponse(ResponseCode::Ok, response, "application/json", Response::Headers(), true));
+    return std::make_shared<ConcreteResponse>(
+        ResponseCode::Ok, response,
+        "application/json",
+        SynchronousResponse::Headers(), true);
 }
 
 std::shared_ptr<Response> Response::htmlResponse(const std::string& response) {
-    return std::shared_ptr<Response>(
-            new ConcreteResponse(ResponseCode::Ok, response, "text/html", Response::Headers(), true));
+    return std::make_shared<ConcreteResponse>(
+        ResponseCode::Ok, response,
+        "text/html",
+        SynchronousResponse::Headers(), true);
 }
 
 }
diff --git a/src/main/c/Server.cpp b/src/main/c/Server.cpp
index 5ad1c30..4908b90 100644
--- a/src/main/c/Server.cpp
+++ b/src/main/c/Server.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2013, Matt Godbolt
+// Copyright (c) 2013-2017, Matt Godbolt
 // All rights reserved.
 //
 // Redistribution and use in source and binary forms, with or without
@@ -23,6 +23,7 @@
 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 // POSSIBILITY OF SUCH DAMAGE.
 
+#include "internal/Config.h"
 #include "internal/LogStream.h"
 
 #include "seasocks/Connection.h"
@@ -40,23 +41,34 @@
 #include <sys/ioctl.h>
 #include <sys/socket.h>
 #include <sys/syscall.h>
+#include <sys/un.h>
 
 #include <memory>
 #include <stdexcept>
-#include <string.h>
+#include <cstring>
 #include <unistd.h>
 
 namespace {
 
 struct EventBits {
     uint32_t bits;
-    explicit EventBits(uint32_t bits) : bits(bits) {}
+    explicit EventBits(uint32_t b)
+            : bits(b) {
+    }
 };
 
-std::ostream& operator <<(std::ostream& o, const EventBits& b) {
+std::ostream& operator<<(std::ostream& o, const EventBits& b) {
     uint32_t bits = b.bits;
-#define DO_BIT(NAME) \
-        do { if (bits & (NAME)) { if (bits != b.bits) {o << ", "; } o << #NAME; bits &= ~(NAME); } } while (0)
+#define DO_BIT(NAME)              \
+    do {                          \
+        if (bits & (NAME)) {      \
+            if (bits != b.bits) { \
+                o << ", ";        \
+            }                     \
+            o << #NAME;           \
+            bits &= ~(NAME);      \
+        }                         \
+    } while (0)
     DO_BIT(EPOLLIN);
     DO_BIT(EPOLLPRI);
     DO_BIT(EPOLLOUT);
@@ -76,22 +88,25 @@
     return o;
 }
 
-const int EpollTimeoutMillis = 500;  // Twice a second is ample.
-const int DefaultLameConnectionTimeoutSeconds = 10;
-int gettid() {
-    return syscall(SYS_gettid);
+constexpr int EpollTimeoutMillis = 500; // Twice a second is ample.
+constexpr int DefaultLameConnectionTimeoutSeconds = 10;
+pid_t gettid() {
+    return static_cast<pid_t>(syscall(SYS_gettid));
 }
 
 }
 
 namespace seasocks {
 
+constexpr size_t Server::DefaultClientBufferSize;
+
 Server::Server(std::shared_ptr<Logger> logger)
-: _logger(logger), _listenSock(-1), _epollFd(-1), _eventFd(-1),
-  _maxKeepAliveDrops(0),
-  _lameConnectionTimeoutSeconds(DefaultLameConnectionTimeoutSeconds),
-  _nextDeadConnectionCheck(0), _threadId(0), _terminate(false),
-  _expectedTerminate(false) {
+        : _logger(logger), _listenSock(-1), _epollFd(-1), _eventFd(-1),
+          _maxKeepAliveDrops(0),
+          _lameConnectionTimeoutSeconds(DefaultLameConnectionTimeoutSeconds),
+          _clientBufferSize(DefaultClientBufferSize),
+          _nextDeadConnectionCheck(0), _threadId(0), _terminate(false),
+          _expectedTerminate(false) {
 
     _epollFd = epoll_create(10);
     if (_epollFd == -1) {
@@ -105,7 +120,7 @@
         return;
     }
 
-    epoll_event eventWake = { EPOLLIN, { &_eventFd } };
+    epoll_event eventWake = {EPOLLIN, {&_eventFd}};
     if (epoll_ctl(_epollFd, EPOLL_CTL_ADD, _eventFd, &eventWake) == -1) {
         LS_ERROR(_logger, "Unable to add wake socket to epoll: " << getLastError());
         return;
@@ -192,12 +207,17 @@
     return startListening(INADDR_ANY, port);
 }
 
-bool Server::startListening(uint32_t hostAddr, int port) {
+bool Server::startListening(uint32_t ipInHostOrder, int port) {
     if (_epollFd == -1 || _eventFd == -1) {
         LS_ERROR(_logger, "Unable to serve, did not initialize properly.");
         return false;
     }
 
+    auto port16 = static_cast<uint16_t>(port);
+    if (port != port16) {
+        LS_ERROR(_logger, "Invalid port: " << port);
+        return false;
+    }
     _listenSock = socket(AF_INET, SOCK_STREAM, 0);
     if (_listenSock == -1) {
         LS_ERROR(_logger, "Unable to create listen socket: " << getLastError());
@@ -208,8 +228,8 @@
     }
     sockaddr_in sock;
     memset(&sock, 0, sizeof(sock));
-    sock.sin_port = htons(port);
-    sock.sin_addr.s_addr = htonl(hostAddr);
+    sock.sin_port = htons(port16);
+    sock.sin_addr.s_addr = htonl(ipInHostOrder);
     sock.sin_family = AF_INET;
     if (bind(_listenSock, reinterpret_cast<const sockaddr*>(&sock), sizeof(sock)) == -1) {
         LS_ERROR(_logger, "Unable to bind socket: " << getLastError());
@@ -219,7 +239,7 @@
         LS_ERROR(_logger, "Unable to listen on socket: " << getLastError());
         return false;
     }
-    epoll_event event = { EPOLLIN, { this } };
+    epoll_event event = {EPOLLIN, {this}};
     if (epoll_ctl(_epollFd, EPOLL_CTL_ADD, _listenSock, &event) == -1) {
         LS_ERROR(_logger, "Unable to add listen socket to epoll: " << getLastError());
         return false;
@@ -232,6 +252,43 @@
     return true;
 }
 
+bool Server::startListeningUnix(const char* socketPath) {
+    struct sockaddr_un sock;
+
+    _listenSock = socket(AF_UNIX, SOCK_STREAM, 0);
+    if (_listenSock == -1) {
+        LS_ERROR(_logger, "Unable to create unix listen socket: " << getLastError());
+        return false;
+    }
+    if (!configureSocket(_listenSock)) {
+        return false;
+    }
+
+    memset(&sock, 0, sizeof(struct sockaddr_un));
+    sock.sun_family = AF_UNIX;
+    strncpy(sock.sun_path, socketPath, sizeof(sock.sun_path) - 1);
+
+    if (bind(_listenSock, reinterpret_cast<const sockaddr*>(&sock), sizeof(sock)) == -1) {
+        LS_ERROR(_logger, "Unable to bind unix socket (" << socketPath << "): " << getLastError());
+        return false;
+    }
+
+    if (listen(_listenSock, 5) == -1) {
+        LS_ERROR(_logger, "Unable to listen on unix socket: " << getLastError());
+        return false;
+    }
+
+    epoll_event event = {EPOLLIN, {this}};
+    if (epoll_ctl(_epollFd, EPOLL_CTL_ADD, _listenSock, &event) == -1) {
+        LS_ERROR(_logger, "Unable to add unix listen socket to epoll: " << getLastError());
+        return false;
+    }
+
+    LS_INFO(_logger, "Listening on unix socket: http://unix:" << socketPath);
+
+    return true;
+}
+
 void Server::handlePipe() {
     uint64_t dummy;
     while (::read(_eventFd, &dummy, sizeof(dummy)) != -1) {
@@ -245,18 +302,18 @@
 }
 
 Server::NewState Server::handleConnectionEvents(Connection* connection, uint32_t events) {
-    if (events & ~(EPOLLIN|EPOLLOUT|EPOLLHUP|EPOLLERR)) {
+    if (events & ~(EPOLLIN | EPOLLOUT | EPOLLHUP | EPOLLERR)) {
         LS_WARNING(_logger, "Got unhandled epoll event (" << EventBits(events) << ") on connection: "
-                << formatAddress(connection->getRemoteAddress()));
-        return Close;
+                                                          << formatAddress(connection->getRemoteAddress()));
+        return NewState::Close;
     } else if (events & EPOLLERR) {
         LS_INFO(_logger, "Error on socket (" << EventBits(events) << "): "
-                << formatAddress(connection->getRemoteAddress()));
-        return Close;
+                                             << formatAddress(connection->getRemoteAddress()));
+        return NewState::Close;
     } else if (events & EPOLLHUP) {
         LS_DEBUG(_logger, "Graceful hang-up (" << EventBits(events) << ") of socket: "
-                << formatAddress(connection->getRemoteAddress()));
-        return Close;
+                                               << formatAddress(connection->getRemoteAddress()));
+        return NewState::Close;
     } else {
         if (events & EPOLLOUT) {
             connection->handleDataReadyForWrite();
@@ -265,11 +322,11 @@
             connection->handleDataReadyForRead();
         }
     }
-    return KeepOpen;
+    return NewState::KeepOpen;
 }
 
 void Server::checkAndDispatchEpoll(int epollMillis) {
-    const int maxEvents = 256;
+    constexpr int maxEvents = 256;
     epoll_event events[maxEvents];
 
     std::list<Connection*> toBeDeleted;
@@ -282,10 +339,10 @@
     }
     if (numEvents == maxEvents) {
         static time_t lastWarnTime = 0;
-        time_t now = time(NULL);
+        time_t now = time(nullptr);
         if (now - lastWarnTime >= 60) {
             LS_WARNING(_logger, "Full event queue; may start starving connections. "
-                    "Will warn at most once a minute");
+                                "Will warn at most once a minute");
             lastWarnTime = now;
         }
     }
@@ -293,7 +350,7 @@
         if (events[i].data.ptr == this) {
             if (events[i].events & ~EPOLLIN) {
                 LS_SEVERE(_logger, "Got unexpected event on listening socket ("
-                        << EventBits(events[i].events) << ") - terminating");
+                                       << EventBits(events[i].events) << ") - terminating");
                 _terminate = true;
                 break;
             }
@@ -301,25 +358,24 @@
         } else if (events[i].data.ptr == &_eventFd) {
             if (events[i].events & ~EPOLLIN) {
                 LS_SEVERE(_logger, "Got unexpected event on management pipe ("
-                        << EventBits(events[i].events) << ") - terminating");
+                                       << EventBits(events[i].events) << ") - terminating");
                 _terminate = true;
                 break;
             }
             handlePipe();
         } else {
             auto connection = reinterpret_cast<Connection*>(events[i].data.ptr);
-            if (handleConnectionEvents(connection, events[i].events) == Close) {
+            if (handleConnectionEvents(connection, events[i].events) == NewState::Close) {
                 toBeDeleted.push_back(connection);
             }
         }
     }
     // The connections are all deleted at the end so we've processed any other subject's
     // closes etc before we call onDisconnect().
-    for (auto it = toBeDeleted.begin(); it != toBeDeleted.end(); ++it) {
-        auto connection = *it;
+    for (auto connection : toBeDeleted) {
         if (_connections.find(connection) == _connections.end()) {
-            LS_SEVERE(_logger, "Attempt to delete connection we didn't know about: " << (void*)connection
-                    << formatAddress(connection->getRemoteAddress()));
+            LS_SEVERE(_logger, "Attempt to delete connection we didn't know about: " << (void*) connection
+                                                                                     << formatAddress(connection->getRemoteAddress()));
             _terminate = true;
             break;
         }
@@ -365,7 +421,8 @@
 
 Server::PollResult Server::poll(int millis) {
     // Grab the thread ID on the first poll.
-    if (_threadId == 0) _threadId = gettid();
+    if (_threadId == 0)
+        _threadId = gettid();
     if (_threadId != gettid()) {
         LS_ERROR(_logger, "poll() called from the wrong thread");
         return PollResult::Error;
@@ -376,7 +433,8 @@
     }
     processEventQueue();
     checkAndDispatchEpoll(millis);
-    if (!_terminate) return PollResult::Continue;
+    if (!_terminate)
+        return PollResult::Continue;
 
     // Reasonable effort to ensure anything enqueued during terminate has a chance to run.
     processEventQueue();
@@ -387,35 +445,41 @@
 }
 
 void Server::processEventQueue() {
-    for (;;) {
-        std::shared_ptr<Runnable> runnable = popNextRunnable();
-        if (!runnable) break;
-        runnable->run();
-    }
-    time_t now = time(NULL);
-    if (now >= _nextDeadConnectionCheck) {
-        std::list<Connection*> toRemove;
-        for (auto it = _connections.cbegin(); it != _connections.cend(); ++it) {
-            time_t numSecondsSinceConnection = now - it->second;
-            auto connection = it->first;
-            if (connection->bytesReceived() == 0 && numSecondsSinceConnection >= _lameConnectionTimeoutSeconds) {
-                LS_INFO(_logger, formatAddress(connection->getRemoteAddress())
-                        << " : Killing lame connection - no bytes received after " << numSecondsSinceConnection << "s");
-                toRemove.push_back(connection);
-            }
-        }
-        for (auto it = toRemove.begin(); it != toRemove.end(); ++it) {
-            delete *it;
+    runExecutables();
+    time_t now = time(nullptr);
+    if (now < _nextDeadConnectionCheck)
+        return;
+    std::list<Connection*> toRemove;
+    for (auto _connection : _connections) {
+        time_t numSecondsSinceConnection = now - _connection.second;
+        auto connection = _connection.first;
+        if (connection->bytesReceived() == 0 && numSecondsSinceConnection >= _lameConnectionTimeoutSeconds) {
+            LS_INFO(_logger, formatAddress(connection->getRemoteAddress())
+                                 << " : Killing lame connection - no bytes received after "
+                                 << numSecondsSinceConnection << "s");
+            toRemove.push_back(connection);
         }
     }
+    for (auto& it : toRemove) {
+        delete it;
+    }
+}
+
+void Server::runExecutables() {
+    decltype(_pendingExecutables) copy;
+    std::unique_lock<decltype(_pendingExecutableMutex)> lock(_pendingExecutableMutex);
+    copy.swap(_pendingExecutables);
+    lock.unlock();
+    for (auto&& ex : copy)
+        ex();
 }
 
 void Server::handleAccept() {
     sockaddr_in address;
     socklen_t addrLen = sizeof(address);
     int fd = ::accept(_listenSock,
-            reinterpret_cast<sockaddr*>(&address),
-            &addrLen);
+                      reinterpret_cast<sockaddr*>(&address),
+                      &addrLen);
     if (fd == -1) {
         LS_ERROR(_logger, "Unable to accept: " << getLastError());
         return;
@@ -426,19 +490,19 @@
     }
     LS_INFO(_logger, formatAddress(address) << " : Accepted on descriptor " << fd);
     Connection* newConnection = new Connection(_logger, *this, fd, address);
-    epoll_event event = { EPOLLIN, { newConnection } };
+    epoll_event event = {EPOLLIN, {newConnection}};
     if (epoll_ctl(_epollFd, EPOLL_CTL_ADD, fd, &event) == -1) {
         LS_ERROR(_logger, "Unable to add socket to epoll: " << getLastError());
         delete newConnection;
         ::close(fd);
         return;
     }
-    _connections.insert(std::make_pair(newConnection, time(NULL)));
+    _connections.insert(std::make_pair(newConnection, time(nullptr)));
 }
 
 void Server::remove(Connection* connection) {
     checkThread();
-    epoll_event event = { 0, { connection } };
+    epoll_event event = {0, {connection}};
     if (epoll_ctl(_epollFd, EPOLL_CTL_DEL, connection->getFd(), &event) == -1) {
         LS_ERROR(_logger, "Unable to remove from epoll: " << getLastError());
     }
@@ -446,7 +510,7 @@
 }
 
 bool Server::subscribeToWriteEvents(Connection* connection) {
-    epoll_event event = { EPOLLIN | EPOLLOUT, { connection } };
+    epoll_event event = {EPOLLIN | EPOLLOUT, {connection}};
     if (epoll_ctl(_epollFd, EPOLL_CTL_MOD, connection->getFd(), &event) == -1) {
         LS_ERROR(_logger, "Unable to subscribe to write events: " << getLastError());
         return false;
@@ -455,7 +519,7 @@
 }
 
 bool Server::unsubscribeFromWriteEvents(Connection* connection) {
-    epoll_event event = { EPOLLIN, { connection } };
+    epoll_event event = {EPOLLIN, {connection}};
     if (epoll_ctl(_epollFd, EPOLL_CTL_MOD, connection->getFd(), &event) == -1) {
         LS_ERROR(_logger, "Unable to unsubscribe from write events: " << getLastError());
         return false;
@@ -464,15 +528,15 @@
 }
 
 void Server::addWebSocketHandler(const char* endpoint, std::shared_ptr<WebSocket::Handler> handler,
-        bool allowCrossOriginRequests) {
-    _webSocketHandlerMap[endpoint] = { handler, allowCrossOriginRequests };
+                                 bool allowCrossOriginRequests) {
+    _webSocketHandlerMap[endpoint] = {handler, allowCrossOriginRequests};
 }
 
 void Server::addPageHandler(std::shared_ptr<PageHandler> handler) {
     _pageHandlers.emplace_back(handler);
 }
 
-bool Server::isCrossOriginAllowed(const std::string &endpoint) const {
+bool Server::isCrossOriginAllowed(const std::string& endpoint) const {
     auto splits = split(endpoint, '?');
     auto iter = _webSocketHandlerMap.find(splits[0]);
     if (iter == _webSocketHandlerMap.end()) {
@@ -491,8 +555,12 @@
 }
 
 void Server::execute(std::shared_ptr<Runnable> runnable) {
-    std::unique_lock<decltype(_pendingRunnableMutex)> lock(_pendingRunnableMutex);
-    _pendingRunnables.push_back(runnable);
+    execute([runnable] { runnable->run(); });
+}
+
+void Server::execute(std::function<void()> toExecute) {
+    std::unique_lock<decltype(_pendingExecutableMutex)> lock(_pendingExecutableMutex);
+    _pendingExecutables.emplace_back(std::move(toExecute));
     lock.unlock();
 
     uint64_t one = 1;
@@ -503,36 +571,24 @@
     }
 }
 
-std::shared_ptr<Server::Runnable> Server::popNextRunnable() {
-    std::lock_guard<decltype(_pendingRunnableMutex)> lock(_pendingRunnableMutex);
-    std::shared_ptr<Runnable> runnable;
-    if (!_pendingRunnables.empty()) {
-        runnable = _pendingRunnables.front();
-        _pendingRunnables.pop_front();
-    }
-    return runnable;
-}
-
 std::string Server::getStatsDocument() const {
     std::ostringstream doc;
-    doc << "clear();" << std::endl;
-    for (auto it = _connections.begin(); it != _connections.end(); ++it) {
+    doc << "clear();\n";
+    for (auto _connection : _connections) {
         doc << "connection({";
-        auto connection = it->first;
+        auto connection = _connection.first;
         jsonKeyPairToStream(doc,
-                "since", EpochTimeAsLocal(it->second),
-                "fd", connection->getFd(),
-                "id", reinterpret_cast<uint64_t>(connection),
-                "uri", connection->getRequestUri(),
-                "addr", formatAddress(connection->getRemoteAddress()),
-                "user", connection->credentials() ?
-                        connection->credentials()->username : "(not authed)",
-                "input", connection->inputBufferSize(),
-                "read", connection->bytesReceived(),
-                "output", connection->outputBufferSize(),
-                "written", connection->bytesSent()
-        );
-        doc << "});" << std::endl;
+                            "since", EpochTimeAsLocal(_connection.second),
+                            "fd", connection->getFd(),
+                            "id", reinterpret_cast<uint64_t>(connection),
+                            "uri", connection->getRequestUri(),
+                            "addr", formatAddress(connection->getRemoteAddress()),
+                            "user", connection->credentials() ? connection->credentials()->username : "(not authed)",
+                            "input", connection->inputBufferSize(),
+                            "read", connection->bytesReceived(),
+                            "output", connection->outputBufferSize(),
+                            "written", connection->bytesSent());
+        doc << "});\n";
     }
     return doc.str();
 }
@@ -547,6 +603,15 @@
     _maxKeepAliveDrops = maxKeepAliveDrops;
 }
 
+void Server::setPerMessageDeflateEnabled(bool enabled) {
+    if (!Config::deflateEnabled) {
+        LS_ERROR(_logger, "Ignoring request to enable deflate as Seasocks was compiled without support");
+        return;
+    }
+    LS_INFO(_logger, "Setting per-message deflate to " << (enabled ? "enabled" : "disabled"));
+    _perMessageDeflateEnabled = enabled;
+}
+
 void Server::checkThread() const {
     auto thisTid = gettid();
     if (thisTid != _threadId) {
@@ -557,12 +622,18 @@
     }
 }
 
-std::shared_ptr<Response> Server::handle(const Request &request) {
-    for (auto handler : _pageHandlers) {
+std::shared_ptr<Response> Server::handle(const Request& request) {
+    for (const auto& handler : _pageHandlers) {
         auto result = handler->handle(request);
-        if (result != Response::unhandled()) return result;
+        if (result != Response::unhandled())
+            return result;
     }
     return Response::unhandled();
 }
 
-}  // namespace seasocks
+void Server::setClientBufferSize(size_t bytesToBuffer) {
+    LS_INFO(_logger, "Setting client buffer size to " << bytesToBuffer << " bytes");
+    _clientBufferSize = bytesToBuffer;
+}
+
+} // namespace seasocks
diff --git a/src/main/c/StringUtil.cpp b/src/main/c/StringUtil.cpp
index b7177ce..958caef 100644
--- a/src/main/c/StringUtil.cpp
+++ b/src/main/c/StringUtil.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2013, Matt Godbolt
+// Copyright (c) 2013-2017, Matt Godbolt
 // All rights reserved.
 //
 // Redistribution and use in source and binary forms, with or without
@@ -34,7 +34,8 @@
 namespace seasocks {
 
 char* skipWhitespace(char* str) {
-    while (isspace(*str)) ++str;
+    while (isspace(*str))
+        ++str;
     return str;
 }
 
@@ -46,13 +47,13 @@
 }
 
 char* shift(char*& str) {
-    if (str == NULL) {
-        return NULL;
+    if (str == nullptr) {
+        return nullptr;
     }
     char* startOfWord = skipWhitespace(str);
     if (*startOfWord == 0) {
         str = startOfWord;
-        return NULL;
+        return nullptr;
     }
     char* endOfWord = skipNonWhitespace(startOfWord);
     if (*endOfWord != 0) {
@@ -62,9 +63,21 @@
     return startOfWord;
 }
 
-std::string getLastError(){
+std::string trimWhitespace(const std::string& str) {
+    auto* start = str.c_str();
+    while (isspace(*start))
+        ++start;
+    auto* end = &str.back();
+    while (end >= start && isspace(*end))
+        --end;
+    return std::string(start, end - start + 1);
+}
+
+std::string getLastError() {
     char errbuf[1024];
-    return strerror_r(errno, errbuf, sizeof(errbuf));
+    const auto ignore = strerror_r(errno, errbuf, sizeof(errbuf));
+    static_cast<void>(ignore);
+    return errbuf;
 }
 
 std::string formatAddress(const sockaddr_in& address) {
@@ -80,7 +93,8 @@
 }
 
 std::vector<std::string> split(const std::string& input, char splitChar) {
-    if (input.empty()) return std::vector<std::string>();
+    if (input.empty())
+        return std::vector<std::string>();
     std::vector<std::string> result;
     size_t pos = 0;
     size_t newPos;
@@ -92,7 +106,12 @@
     return result;
 }
 
-void replace(std::string& string, const std::string& find, const std::string& replace) {
+void replace(std::string& string, const std::string& find,
+             const std::string& replace) {
+    if (find.empty()) {
+        return;
+    }
+
     size_t pos = 0;
     const size_t findLen = find.length();
     const size_t replaceLen = replace.length();
@@ -102,8 +121,21 @@
     }
 }
 
-bool caseInsensitiveSame(const std::string &lhs, const std::string &rhs) {
+bool caseInsensitiveSame(const std::string& lhs, const std::string& rhs) {
     return strcasecmp(lhs.c_str(), rhs.c_str()) == 0;
 }
 
+std::string webtime(time_t time) {
+    struct tm timeValue;
+    gmtime_r(&time, &timeValue);
+    char buf[1024];
+    // Wed, 20 Apr 2011 17:31:28 GMT
+    strftime(buf, sizeof(buf) - 1, "%a, %d %b %Y %H:%M:%S %Z", &timeValue);
+    return buf;
+}
+
+std::string now() {
+    return webtime(time(nullptr));
+}
+
 }
diff --git a/src/main/c/internal/.gitignore b/src/main/c/internal/.gitignore
deleted file mode 100644
index 40ee485..0000000
--- a/src/main/c/internal/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/Config.h
diff --git a/src/main/c/internal/Base64.cpp b/src/main/c/internal/Base64.cpp
index 3b1cf20..6d6cfdd 100644
--- a/src/main/c/internal/Base64.cpp
+++ b/src/main/c/internal/Base64.cpp
@@ -1,26 +1,26 @@
-// Copyright (c) 2013, Matt Godbolt
+// Copyright (c) 2013-2017, Matt Godbolt
 // All rights reserved.
-// 
-// Redistribution and use in source and binary forms, with or without 
+//
+// 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 
+//
+// 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 
+//
+// 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 
+//
+// 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/Base64.h"
@@ -28,17 +28,18 @@
 #include <cstdint>
 
 namespace seasocks {
+namespace {
+const char cb64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+}
 
-const char cb64[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-
-std::string base64Encode(const void* dataVoid, size_t length) {
+std::string base64Encode(const void* data, size_t length) {
     std::string output;
-    auto data = reinterpret_cast<const uint8_t*>(dataVoid);
+    const auto dataPtr = reinterpret_cast<const uint8_t*>(data);
     for (auto i = 0u; i < length; i += 3) {
-        auto bytesLeft = length - i;
-        auto b0 = data[i];
-        auto b1 = bytesLeft > 1 ? data[i + 1] : 0;
-        auto b2 = bytesLeft > 2 ? data[i + 2] : 0;
+        const auto bytesLeft = length - i;
+        const auto b0 = dataPtr[i];
+        const auto b1 = bytesLeft > 1 ? dataPtr[i + 1] : 0;
+        const auto b2 = bytesLeft > 2 ? dataPtr[i + 2] : 0;
         output.push_back(cb64[b0 >> 2]);
         output.push_back(cb64[((b0 & 0x03) << 4) | ((b1 & 0xf0) >> 4)]);
         output.push_back((bytesLeft > 1 ? cb64[((b1 & 0x0f) << 2) | ((b2 & 0xc0) >> 6)] : '='));
diff --git a/src/main/c/internal/Base64.h b/src/main/c/internal/Base64.h
index b6dc014..1ea578e 100644
--- a/src/main/c/internal/Base64.h
+++ b/src/main/c/internal/Base64.h
@@ -1,26 +1,26 @@
-// Copyright (c) 2013, Matt Godbolt
+// Copyright (c) 2013-2017, Matt Godbolt
 // All rights reserved.
-// 
-// Redistribution and use in source and binary forms, with or without 
+//
+// 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 
+//
+// 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 
+//
+// 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 
+//
+// 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
diff --git a/src/main/c/internal/ConcreteResponse.h b/src/main/c/internal/ConcreteResponse.h
index d4a68b3..cf56187 100644
--- a/src/main/c/internal/ConcreteResponse.h
+++ b/src/main/c/internal/ConcreteResponse.h
@@ -1,65 +1,69 @@
-// Copyright (c) 2013, Matt Godbolt
+// Copyright (c) 2013-2017, Matt Godbolt
 // All rights reserved.
-// 
-// Redistribution and use in source and binary forms, with or without 
+//
+// 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 
+//
+// 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 
+//
+// 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 
+//
+// 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/Response.h"
+#include "seasocks/SynchronousResponse.h"
 
 namespace seasocks {
 
-class ConcreteResponse : public Response {
+class ConcreteResponse : public SynchronousResponse {
     ResponseCode _responseCode;
     const std::string _payload;
     const std::string _contentType;
     const Headers _headers;
     const bool _keepAlive;
-public:
-    ConcreteResponse(ResponseCode responseCode, const std::string& payload, const std::string& contentType, const Headers& headers, bool keepAlive) :
-        _responseCode(responseCode), _payload(payload), _contentType(contentType), _headers(headers), _keepAlive(keepAlive) {}
 
-    virtual ResponseCode responseCode() const {
+public:
+    ConcreteResponse(ResponseCode responseCode, const std::string& payload,
+                     const std::string& contentType, const Headers& headers, bool keepAlive)
+            : _responseCode(responseCode), _payload(payload), _contentType(contentType),
+              _headers(headers), _keepAlive(keepAlive) {
+    }
+
+    virtual ResponseCode responseCode() const override {
         return _responseCode;
     }
 
-    virtual const char* payload() const {
+    virtual const char* payload() const override {
         return _payload.c_str();
     }
 
-    virtual size_t payloadSize() const {
+    virtual size_t payloadSize() const override {
         return _payload.size();
     }
 
-    virtual bool keepConnectionAlive() const {
+    virtual bool keepConnectionAlive() const override {
         return _keepAlive;
     }
 
-    virtual std::string contentType() const {
+    virtual std::string contentType() const override {
         return _contentType;
     }
 
-    virtual Headers getAdditionalHeaders() const {
+    virtual Headers getAdditionalHeaders() const override {
         return _headers;
     }
 };
diff --git a/src/main/c/internal/Config.h.in b/src/main/c/internal/Config.h.in
deleted file mode 100644
index 48c5421..0000000
--- a/src/main/c/internal/Config.h.in
+++ /dev/null
@@ -1,219 +0,0 @@
-/* src/main/c/internal/Config.h.in.  Generated from configure.ac by autoheader.  */
-
-/* Define to 1 if you have the <arpa/inet.h> header file. */
-#undef HAVE_ARPA_INET_H
-
-/* define if the compiler supports basic C++11 syntax */
-#undef HAVE_CXX11
-
-/* Define to 1 if you have the declaration of `strerror_r', and to 0 if you
-   don't. */
-#undef HAVE_DECL_STRERROR_R
-
-/* Define to 1 if you have the `dup2' function. */
-#undef HAVE_DUP2
-
-/* Define to 1 if you have the `eventfd' function. */
-#undef HAVE_EVENTFD
-
-/* Define to 1 if you have the <fcntl.h> header file. */
-#undef HAVE_FCNTL_H
-
-/* Define to 1 if you have the `fork' function. */
-#undef HAVE_FORK
-
-/* Define to 1 if you have the `gethostname' function. */
-#undef HAVE_GETHOSTNAME
-
-/* Define to 1 if you have the `getopt' function. */
-#undef HAVE_GETOPT
-
-/* Define to 1 if you have the <getopt.h> header file. */
-#undef HAVE_GETOPT_H
-
-/* Define to 1 if you have the <inttypes.h> header file. */
-#undef HAVE_INTTYPES_H
-
-/* Define to 1 if you have the <limits.h> header file. */
-#undef HAVE_LIMITS_H
-
-/* Define to 1 if your system has a GNU libc compatible `malloc' function, and
-   to 0 otherwise. */
-#undef HAVE_MALLOC
-
-/* Define to 1 if you have the <memory.h> header file. */
-#undef HAVE_MEMORY_H
-
-/* Define to 1 if you have the `memset' function. */
-#undef HAVE_MEMSET
-
-/* Define to 1 if you have the <netinet/in.h> header file. */
-#undef HAVE_NETINET_IN_H
-
-/* Define to 1 if the system has the type `ptrdiff_t'. */
-#undef HAVE_PTRDIFF_T
-
-/* Define to 1 if you have the `rmdir' function. */
-#undef HAVE_RMDIR
-
-/* Define to 1 if you have the `socket' function. */
-#undef HAVE_SOCKET
-
-/* Define to 1 if you have the `sqrt' function. */
-#undef HAVE_SQRT
-
-/* Define to 1 if stdbool.h conforms to C99. */
-#undef HAVE_STDBOOL_H
-
-/* Define to 1 if you have the <stddef.h> header file. */
-#undef HAVE_STDDEF_H
-
-/* Define to 1 if you have the <stdint.h> header file. */
-#undef HAVE_STDINT_H
-
-/* Define to 1 if you have the <stdlib.h> header file. */
-#undef HAVE_STDLIB_H
-
-/* Define to 1 if you have the `strcasecmp' function. */
-#undef HAVE_STRCASECMP
-
-/* Define to 1 if you have the `strchr' function. */
-#undef HAVE_STRCHR
-
-/* Define to 1 if you have the `strdup' function. */
-#undef HAVE_STRDUP
-
-/* Define to 1 if you have the `strerror' function. */
-#undef HAVE_STRERROR
-
-/* Define to 1 if you have the `strerror_r' function. */
-#undef HAVE_STRERROR_R
-
-/* Define to 1 if you have the <strings.h> header file. */
-#undef HAVE_STRINGS_H
-
-/* Define to 1 if you have the <string.h> header file. */
-#undef HAVE_STRING_H
-
-/* Define to 1 if you have the `syscall' function. */
-#undef HAVE_SYSCALL
-
-/* Define to 1 if you have the <sys/ioctl.h> header file. */
-#undef HAVE_SYS_IOCTL_H
-
-/* Define to 1 if you have the <sys/socket.h> header file. */
-#undef HAVE_SYS_SOCKET_H
-
-/* Define to 1 if you have the <sys/stat.h> header file. */
-#undef HAVE_SYS_STAT_H
-
-/* Define to 1 if you have the <sys/types.h> header file. */
-#undef HAVE_SYS_TYPES_H
-
-/* Define to 1 if you have the <unistd.h> header file. */
-#undef HAVE_UNISTD_H
-
-/* define if unordered_map supports emplace */
-#undef HAVE_UNORDERED_MAP_EMPLACE
-
-/* Define to 1 if you have the `vfork' function. */
-#undef HAVE_VFORK
-
-/* Define to 1 if you have the <vfork.h> header file. */
-#undef HAVE_VFORK_H
-
-/* Define to 1 if `fork' works. */
-#undef HAVE_WORKING_FORK
-
-/* Define to 1 if `vfork' works. */
-#undef HAVE_WORKING_VFORK
-
-/* Define to 1 if the system has the type `_Bool'. */
-#undef HAVE__BOOL
-
-/* Define to the address where bug reports for this package should be sent. */
-#undef PACKAGE_BUGREPORT
-
-/* Define to the full name of this package. */
-#undef PACKAGE_NAME
-
-/* Define to the full name and version of this package. */
-#undef PACKAGE_STRING
-
-/* Define to the one symbol short name of this package. */
-#undef PACKAGE_TARNAME
-
-/* Define to the home page for this package. */
-#undef PACKAGE_URL
-
-/* Define to the version of this package. */
-#undef PACKAGE_VERSION
-
-/* Define to 1 if you have the ANSI C header files. */
-#undef STDC_HEADERS
-
-/* Define to 1 if strerror_r returns char *. */
-#undef STRERROR_R_CHAR_P
-
-/* Define for Solaris 2.5.1 so the uint32_t typedef from <sys/synch.h>,
-   <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
-   #define below would cause a syntax error. */
-#undef _UINT32_T
-
-/* Define for Solaris 2.5.1 so the uint64_t typedef from <sys/synch.h>,
-   <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
-   #define below would cause a syntax error. */
-#undef _UINT64_T
-
-/* Define for Solaris 2.5.1 so the uint8_t typedef from <sys/synch.h>,
-   <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
-   #define below would cause a syntax error. */
-#undef _UINT8_T
-
-/* Define to `__inline__' or `__inline' if that's what the C compiler
-   calls it, or to nothing if 'inline' is not supported under any name.  */
-#ifndef __cplusplus
-#undef inline
-#endif
-
-/* Define to rpl_malloc if the replacement function should be used. */
-#undef malloc
-
-/* Define to `int' if <sys/types.h> does not define. */
-#undef pid_t
-
-/* Define to the equivalent of the C99 'restrict' keyword, or to
-   nothing if this is not supported.  Do not define if restrict is
-   supported directly.  */
-#undef restrict
-/* Work around a bug in Sun C++: it does not support _Restrict or
-   __restrict__, even though the corresponding Sun C compiler ends up with
-   "#define restrict _Restrict" or "#define restrict __restrict__" in the
-   previous line.  Perhaps some future version of Sun C++ will work with
-   restrict; if so, hopefully it defines __RESTRICT like Sun C does.  */
-#if defined __SUNPRO_CC && !defined __RESTRICT
-# define _Restrict
-# define __restrict__
-#endif
-
-/* Define to `unsigned int' if <sys/types.h> does not define. */
-#undef size_t
-
-/* Define to the type of an unsigned integer type of width exactly 16 bits if
-   such a type exists and the standard includes do not define it. */
-#undef uint16_t
-
-/* Define to the type of an unsigned integer type of width exactly 32 bits if
-   such a type exists and the standard includes do not define it. */
-#undef uint32_t
-
-/* Define to the type of an unsigned integer type of width exactly 64 bits if
-   such a type exists and the standard includes do not define it. */
-#undef uint64_t
-
-/* Define to the type of an unsigned integer type of width exactly 8 bits if
-   such a type exists and the standard includes do not define it. */
-#undef uint8_t
-
-/* Define as `fork' if `vfork' does not work. */
-#undef vfork
diff --git a/src/main/c/internal/Debug.h b/src/main/c/internal/Debug.h
index 304e141..731029d 100644
--- a/src/main/c/internal/Debug.h
+++ b/src/main/c/internal/Debug.h
@@ -1,29 +1,29 @@
-// Copyright (c) 2013, Matt Godbolt
+// Copyright (c) 2013-2017, Matt Godbolt
 // All rights reserved.
-// 
-// Redistribution and use in source and binary forms, with or without 
+//
+// 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 
+//
+// 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 
+//
+// 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 
+//
+// 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
 
-// Uncomment to actually log at DEBUG level.
+// Uncomment to actually log at Debug level.
 //#define LOG_DEBUG_INFO
diff --git a/src/main/c/internal/Embedded.h b/src/main/c/internal/Embedded.h
index 5741c62..e53b3ca 100644
--- a/src/main/c/internal/Embedded.h
+++ b/src/main/c/internal/Embedded.h
@@ -1,26 +1,26 @@
-// Copyright (c) 2013, Matt Godbolt
+// Copyright (c) 2013-2017, Matt Godbolt
 // All rights reserved.
-// 
-// Redistribution and use in source and binary forms, with or without 
+//
+// 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 
+//
+// 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 
+//
+// 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 
+//
+// 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
@@ -28,8 +28,8 @@
 #include <string>
 
 struct EmbeddedContent {
-   const char* data;
-   size_t length;
+    const char* data;
+    size_t length;
 };
 
 const EmbeddedContent* findEmbeddedContent(const std::string& name);
diff --git a/src/main/c/internal/HeaderMap.h b/src/main/c/internal/HeaderMap.h
index ba5ed35..33e233d 100644
--- a/src/main/c/internal/HeaderMap.h
+++ b/src/main/c/internal/HeaderMap.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2013, Matt Godbolt
+// Copyright (c) 2013-2017, Matt Godbolt
 // All rights reserved.
 //
 // Redistribution and use in source and binary forms, with or without
@@ -33,9 +33,9 @@
 namespace seasocks {
 
 struct CaseInsensitiveHash {
-    size_t operator()(const std::string &string) const {
+    size_t operator()(const std::string& string) const {
         size_t h = 0;
-        for (auto c: string) {
+        for (auto c : string) {
             h = h * 13 + tolower(c);
         }
         return h;
@@ -43,12 +43,12 @@
 };
 
 struct CaseInsensitiveComparison {
-    bool operator()(const std::string &lhs, const std::string &rhs) const {
+    bool operator()(const std::string& lhs, const std::string& rhs) const {
         return strcasecmp(lhs.c_str(), rhs.c_str()) == 0;
     }
 };
 
 using HeaderMap = std::unordered_map<std::string, std::string,
-        CaseInsensitiveHash, CaseInsensitiveComparison>;
+                                     CaseInsensitiveHash, CaseInsensitiveComparison>;
 
 }
diff --git a/src/main/c/internal/HybiAccept.h b/src/main/c/internal/HybiAccept.h
index d02833b..379d9ea 100644
--- a/src/main/c/internal/HybiAccept.h
+++ b/src/main/c/internal/HybiAccept.h
@@ -1,26 +1,26 @@
-// Copyright (c) 2013, Matt Godbolt
+// Copyright (c) 2013-2017, Matt Godbolt
 // All rights reserved.
-// 
-// Redistribution and use in source and binary forms, with or without 
+//
+// 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 
+//
+// 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 
+//
+// 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 
+//
+// 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
diff --git a/src/main/c/internal/HybiPacketDecoder.h b/src/main/c/internal/HybiPacketDecoder.h
index 528bbf9..5ad9526 100644
--- a/src/main/c/internal/HybiPacketDecoder.h
+++ b/src/main/c/internal/HybiPacketDecoder.h
@@ -1,32 +1,33 @@
-// Copyright (c) 2013, Matt Godbolt
+// Copyright (c) 2013-2017, Matt Godbolt
 // All rights reserved.
-// 
-// Redistribution and use in source and binary forms, with or without 
+//
+// 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 
+//
+// 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 
+//
+// 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 
+//
+// 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/Logger.h"
 
+#include <cstdint>
 #include <memory>
 #include <string>
 #include <vector>
@@ -37,27 +38,33 @@
     Logger& _logger;
     const std::vector<uint8_t>& _buffer;
     size_t _messageStart;
+
 public:
     HybiPacketDecoder(Logger& logger, const std::vector<uint8_t>& buffer);
 
-    enum {
-        OPCODE_CONT = 0x0,  // Deprecated in latest hybi spec, here anyway.
-        OPCODE_TEXT = 0x1,
-        OPCODE_BINARY = 0x2,
-        OPCODE_CLOSE = 0x8,
-        OPCODE_PING = 0x9,
-        OPCODE_PONG = 0xA,
+    enum class Opcode : uint8_t {
+        Cont = 0x0, // Deprecated in latest hybi spec, here anyway.
+        Text = 0x1,
+        Binary = 0x2,
+        Close = 0x8,
+        Ping = 0x9,
+        Pong = 0xA,
     };
 
-    enum MessageState {
+    enum class MessageState {
         NoMessage,
         TextMessage,
         BinaryMessage,
         Error,
         Ping,
+        Pong,
         Close
     };
-    MessageState decodeNextMessage(std::vector<uint8_t>& messageOut);
+    MessageState decodeNextMessage(std::vector<uint8_t>& messageOut, bool& deflateNeeded);
+    MessageState decodeNextMessage(std::vector<uint8_t>& messageOut) {
+        bool ignore;
+        return decodeNextMessage(messageOut, ignore);
+    }
 
     size_t numBytesDecoded() const;
 };
diff --git a/src/main/c/internal/LogStream.h b/src/main/c/internal/LogStream.h
index 16612ba..e8b9a65 100644
--- a/src/main/c/internal/LogStream.h
+++ b/src/main/c/internal/LogStream.h
@@ -1,26 +1,26 @@
-// Copyright (c) 2013, Matt Godbolt
+// Copyright (c) 2013-2017, Matt Godbolt
 // All rights reserved.
-// 
-// Redistribution and use in source and binary forms, with or without 
+//
+// 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 
+//
+// 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 
+//
+// 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 
+//
+// 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
@@ -30,16 +30,16 @@
 // Internal stream helpers for logging.
 #include <sstream>
 
-#define LS_LOG(LOG, LEVEL, STUFF) \
-{ \
-    std::ostringstream o; \
-    o << STUFF; \
-    (LOG)->log(Logger::LEVEL, o.str().c_str()); \
-}
+#define LS_LOG(LOG, LEVEL, STUFF)                            \
+    {                                                        \
+        std::ostringstream os_;                              \
+        os_ << STUFF;                                        \
+        (LOG)->log(Logger::Level::LEVEL, os_.str().c_str()); \
+    }
 
-#define LS_DEBUG(LOG, STUFF)     LS_LOG(LOG, DEBUG, STUFF)
-#define LS_ACCESS(LOG, STUFF)     LS_LOG(LOG, ACCESS, STUFF)
-#define LS_INFO(LOG, STUFF)     LS_LOG(LOG, INFO, STUFF)
-#define LS_WARNING(LOG, STUFF)     LS_LOG(LOG, WARNING, STUFF)
-#define LS_ERROR(LOG, STUFF)     LS_LOG(LOG, ERROR, STUFF)
-#define LS_SEVERE(LOG, STUFF)     LS_LOG(LOG, SEVERE, STUFF)
+#define LS_DEBUG(LOG, STUFF) LS_LOG(LOG, Debug, STUFF)
+#define LS_ACCESS(LOG, STUFF) LS_LOG(LOG, Access, STUFF)
+#define LS_INFO(LOG, STUFF) LS_LOG(LOG, Info, STUFF)
+#define LS_WARNING(LOG, STUFF) LS_LOG(LOG, Warning, STUFF)
+#define LS_ERROR(LOG, STUFF) LS_LOG(LOG, Error, STUFF)
+#define LS_SEVERE(LOG, STUFF) LS_LOG(LOG, Severe, STUFF)
diff --git a/src/main/c/internal/PageRequest.h b/src/main/c/internal/PageRequest.h
index 6fea7fb..a8c8eb6 100644
--- a/src/main/c/internal/PageRequest.h
+++ b/src/main/c/internal/PageRequest.h
@@ -1,26 +1,26 @@
-// Copyright (c) 2013, Matt Godbolt
+// Copyright (c) 2013-2017, Matt Godbolt
 // All rights reserved.
-// 
-// Redistribution and use in source and binary forms, with or without 
+//
+// 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 
+//
+// 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 
+//
+// 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 
+//
+// 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
@@ -37,6 +37,7 @@
     std::shared_ptr<Credentials> _credentials;
     const sockaddr_in _remoteAddress;
     const std::string _requestUri;
+    Server& _server;
     const Verb _verb;
     std::vector<uint8_t> _content;
     HeaderMap _headers;
@@ -44,47 +45,52 @@
 
 public:
     PageRequest(
-            const sockaddr_in& remoteAddress,
-            const std::string& requestUri,
-            Verb verb,
-            HeaderMap&& headers);
+        const sockaddr_in& remoteAddress,
+        const std::string& requestUri,
+        Server& server,
+        Verb verb,
+        HeaderMap&& headers);
 
-    virtual Verb verb() const {
+    virtual Server& server() const override {
+        return _server;
+    }
+
+    virtual Verb verb() const override {
         return _verb;
     }
 
-    virtual std::shared_ptr<Credentials> credentials() const {
+    virtual std::shared_ptr<Credentials> credentials() const override {
         return _credentials;
     }
 
-    virtual const sockaddr_in& getRemoteAddress() const {
+    virtual const sockaddr_in& getRemoteAddress() const override {
         return _remoteAddress;
     }
 
-    virtual const std::string& getRequestUri() const {
+    virtual const std::string& getRequestUri() const override {
         return _requestUri;
     }
 
-    virtual size_t contentLength() const {
+    virtual size_t contentLength() const override {
         return _contentLength;
     }
 
-    virtual const uint8_t* content() const {
-        return _contentLength > 0 ? &_content[0] : NULL;
+    virtual const uint8_t* content() const override {
+        return _contentLength > 0 ? &_content[0] : nullptr;
     }
 
-    virtual bool hasHeader(const std::string& name) const {
+    virtual bool hasHeader(const std::string& name) const override {
         return _headers.find(name) != _headers.end();
     }
 
-    virtual std::string getHeader(const std::string& name) const {
+    virtual std::string getHeader(const std::string& name) const override {
         auto iter = _headers.find(name);
         return iter == _headers.end() ? std::string() : iter->second;
     }
 
     bool consumeContent(std::vector<uint8_t>& buffer);
 
-    int getIntHeader(const std::string& name) const;
+    size_t getUintHeader(const std::string& name) const;
 };
 
-}  // namespace seasocks
+} // namespace seasocks
diff --git a/src/main/c/internal/Version.h b/src/main/c/internal/RaiiFd.h
similarity index 62%
copy from src/main/c/internal/Version.h
copy to src/main/c/internal/RaiiFd.h
index 366f8ea..0b93bd5 100644
--- a/src/main/c/internal/Version.h
+++ b/src/main/c/internal/RaiiFd.h
@@ -1,31 +1,58 @@
-// Copyright (c) 2013, Matt Godbolt
+// Copyright (c) 2013-2017, Matt Godbolt
 // All rights reserved.
-// 
-// Redistribution and use in source and binary forms, with or without 
+//
+// 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 
+//
+// 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 
+//
+// 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.
+//
+// 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 SUnamespace seasocks {
 
 #pragma once
 
-#ifndef SEASOCKS_VERSION_STRING
-// This stops Eclipse freaking out as it doesn't know this is set on GCC command line.
-#define SEASOCKS_VERSION_STRING "SeaSocks/unversioned"
-#endif
\ No newline at end of file
+#include <unistd.h>
+
+namespace seasocks {
+
+class RaiiFd {
+    int fd_;
+
+public:
+    explicit RaiiFd(int fd)
+            : fd_(fd) {
+    }
+
+    RaiiFd(const RaiiFd&) = delete;
+    RaiiFd& operator=(const RaiiFd&) = delete;
+
+    ~RaiiFd() {
+        if (fd_ != -1) {
+            ::close(fd_);
+        }
+    }
+
+    bool ok() const {
+        return fd_ != -1;
+    }
+
+    operator int() const {
+        return fd_;
+    }
+};
+
+}
diff --git a/src/main/c/md5/md5.cpp b/src/main/c/md5/md5.cpp
index 4ad3606..64c1f02 100644
--- a/src/main/c/md5/md5.cpp
+++ b/src/main/c/md5/md5.cpp
@@ -1,26 +1,26 @@
-// Copyright (c) 2013, Matt Godbolt
+// Copyright (c) 2013-2017, Matt Godbolt
 // All rights reserved.
-// 
-// Redistribution and use in source and binary forms, with or without 
+//
+// 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 
+//
+// 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 
+//
+// 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 
+//
+// 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.
 
 /*
@@ -78,88 +78,88 @@
 
 #include "md5/md5.h"
 
-#include <string.h>
+#include <cstring>
 
-#undef BYTE_ORDER    /* 1 = big-endian, -1 = little-endian, 0 = unknown */
+#undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */
 #ifdef ARCH_IS_BIG_ENDIAN
-#  define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1)
+#define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1)
 #else
-#  define BYTE_ORDER 0
+#define BYTE_ORDER 0
 #endif
 
-#define T_MASK ((md5_word_t)~0)
+#define T_MASK ((md5_word_t) ~0)
 #define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87)
 #define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9)
-#define T3    0x242070db
+#define T3 0x242070db
 #define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111)
 #define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050)
-#define T6    0x4787c62a
+#define T6 0x4787c62a
 #define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec)
 #define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe)
-#define T9    0x698098d8
+#define T9 0x698098d8
 #define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850)
 #define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e)
 #define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841)
-#define T13    0x6b901122
+#define T13 0x6b901122
 #define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c)
 #define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71)
-#define T16    0x49b40821
+#define T16 0x49b40821
 #define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d)
 #define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf)
-#define T19    0x265e5a51
+#define T19 0x265e5a51
 #define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855)
 #define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2)
-#define T22    0x02441453
+#define T22 0x02441453
 #define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e)
 #define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437)
-#define T25    0x21e1cde6
+#define T25 0x21e1cde6
 #define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829)
 #define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278)
-#define T28    0x455a14ed
+#define T28 0x455a14ed
 #define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa)
 #define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07)
-#define T31    0x676f02d9
+#define T31 0x676f02d9
 #define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375)
 #define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd)
 #define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e)
-#define T35    0x6d9d6122
+#define T35 0x6d9d6122
 #define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3)
 #define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb)
-#define T38    0x4bdecfa9
+#define T38 0x4bdecfa9
 #define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f)
 #define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f)
-#define T41    0x289b7ec6
+#define T41 0x289b7ec6
 #define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805)
 #define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a)
-#define T44    0x04881d05
+#define T44 0x04881d05
 #define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6)
 #define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a)
-#define T47    0x1fa27cf8
+#define T47 0x1fa27cf8
 #define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a)
 #define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb)
-#define T50    0x432aff97
+#define T50 0x432aff97
 #define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58)
 #define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6)
-#define T53    0x655b59c3
+#define T53 0x655b59c3
 #define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d)
 #define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82)
 #define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e)
-#define T57    0x6fa87e4f
+#define T57 0x6fa87e4f
 #define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f)
 #define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb)
-#define T60    0x4e0811a1
+#define T60 0x4e0811a1
 #define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d)
 #define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca)
-#define T63    0x2ad7d2bb
+#define T63 0x2ad7d2bb
 #define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e)
 
 
 static void
-md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/)
-{
+md5_process(md5_state_t* pms, const md5_byte_t* data /*[64]*/) {
     md5_word_t
-    a = pms->abcd[0], b = pms->abcd[1],
-    c = pms->abcd[2], d = pms->abcd[3];
+        a = pms->abcd[0],
+        b = pms->abcd[1],
+        c = pms->abcd[2], d = pms->abcd[3];
     md5_word_t t;
 #if BYTE_ORDER > 0
     /* Define storage only for big-endian CPUs. */
@@ -167,56 +167,56 @@
 #else
     /* Define storage for little-endian or both types of CPUs. */
     md5_word_t xbuf[16];
-    const md5_word_t *X;
+    const md5_word_t* X;
 #endif
 
     {
 #if BYTE_ORDER == 0
-    /*
+        /*
      * Determine dynamically whether this is a big-endian or
      * little-endian machine, since we can use a more efficient
      * algorithm on the latter.
      */
-    static const int w = 1;
+        static const int w = 1;
 
-    if (*((const md5_byte_t *)&w)) /* dynamic little-endian */
+        if (*((const md5_byte_t*) &w)) /* dynamic little-endian */
 #endif
-#if BYTE_ORDER <= 0        /* little-endian */
-    {
-        /*
+#if BYTE_ORDER <= 0 /* little-endian */
+        {
+            /*
          * On little-endian machines, we can process properly aligned
          * data without copying it.
          */
-        if (!((data - (const md5_byte_t *)0) & 3)) {
-        /* data are properly aligned */
-        X = (const md5_word_t *)data;
-        } else {
-        /* not aligned */
-        memcpy(xbuf, data, 64);
-        X = xbuf;
+            if (!((data - (const md5_byte_t*) 0) & 3)) {
+                /* data are properly aligned */
+                X = (const md5_word_t*) data;
+            } else {
+                /* not aligned */
+                memcpy(xbuf, data, 64);
+                X = xbuf;
+            }
         }
-    }
 #endif
 #if BYTE_ORDER == 0
-    else            /* dynamic big-endian */
+        else /* dynamic big-endian */
 #endif
-#if BYTE_ORDER >= 0        /* big-endian */
-    {
-        /*
+#if BYTE_ORDER >= 0 /* big-endian */
+        {
+            /*
          * On big-endian machines, we must arrange the bytes in the
          * right order.
          */
-        const md5_byte_t *xp = data;
-        int i;
+            const md5_byte_t* xp = data;
+            int i;
 
-#  if BYTE_ORDER == 0
-        X = xbuf;        /* (dynamic only) */
-#  else
-#    define xbuf X        /* (static only) */
-#  endif
-        for (i = 0; i < 16; ++i, xp += 4)
-        xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24);
-    }
+#if BYTE_ORDER == 0
+            X = xbuf; /* (dynamic only) */
+#else
+#define xbuf X /* (static only) */
+#endif
+            for (i = 0; i < 16; ++i, xp += 4)
+                xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24);
+        }
 #endif
     }
 
@@ -226,107 +226,107 @@
     /* Let [abcd k s i] denote the operation
        a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
 #define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
-#define SET(a, b, c, d, k, s, Ti)\
-  t = a + F(b,c,d) + X[k] + Ti;\
-  a = ROTATE_LEFT(t, s) + b
+#define SET(a, b, c, d, k, s, Ti)   \
+    t = a + F(b, c, d) + X[k] + Ti; \
+    a = ROTATE_LEFT(t, s) + b
     /* Do the following 16 operations. */
-    SET(a, b, c, d,  0,  7,  T1);
-    SET(d, a, b, c,  1, 12,  T2);
-    SET(c, d, a, b,  2, 17,  T3);
-    SET(b, c, d, a,  3, 22,  T4);
-    SET(a, b, c, d,  4,  7,  T5);
-    SET(d, a, b, c,  5, 12,  T6);
-    SET(c, d, a, b,  6, 17,  T7);
-    SET(b, c, d, a,  7, 22,  T8);
-    SET(a, b, c, d,  8,  7,  T9);
-    SET(d, a, b, c,  9, 12, T10);
+    SET(a, b, c, d, 0, 7, T1);
+    SET(d, a, b, c, 1, 12, T2);
+    SET(c, d, a, b, 2, 17, T3);
+    SET(b, c, d, a, 3, 22, T4);
+    SET(a, b, c, d, 4, 7, T5);
+    SET(d, a, b, c, 5, 12, T6);
+    SET(c, d, a, b, 6, 17, T7);
+    SET(b, c, d, a, 7, 22, T8);
+    SET(a, b, c, d, 8, 7, T9);
+    SET(d, a, b, c, 9, 12, T10);
     SET(c, d, a, b, 10, 17, T11);
     SET(b, c, d, a, 11, 22, T12);
-    SET(a, b, c, d, 12,  7, T13);
+    SET(a, b, c, d, 12, 7, T13);
     SET(d, a, b, c, 13, 12, T14);
     SET(c, d, a, b, 14, 17, T15);
     SET(b, c, d, a, 15, 22, T16);
 #undef SET
 
-     /* Round 2. */
-     /* Let [abcd k s i] denote the operation
+    /* Round 2. */
+    /* Let [abcd k s i] denote the operation
           a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
 #define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
-#define SET(a, b, c, d, k, s, Ti)\
-  t = a + G(b,c,d) + X[k] + Ti;\
-  a = ROTATE_LEFT(t, s) + b
-     /* Do the following 16 operations. */
-    SET(a, b, c, d,  1,  5, T17);
-    SET(d, a, b, c,  6,  9, T18);
+#define SET(a, b, c, d, k, s, Ti)   \
+    t = a + G(b, c, d) + X[k] + Ti; \
+    a = ROTATE_LEFT(t, s) + b
+    /* Do the following 16 operations. */
+    SET(a, b, c, d, 1, 5, T17);
+    SET(d, a, b, c, 6, 9, T18);
     SET(c, d, a, b, 11, 14, T19);
-    SET(b, c, d, a,  0, 20, T20);
-    SET(a, b, c, d,  5,  5, T21);
-    SET(d, a, b, c, 10,  9, T22);
+    SET(b, c, d, a, 0, 20, T20);
+    SET(a, b, c, d, 5, 5, T21);
+    SET(d, a, b, c, 10, 9, T22);
     SET(c, d, a, b, 15, 14, T23);
-    SET(b, c, d, a,  4, 20, T24);
-    SET(a, b, c, d,  9,  5, T25);
-    SET(d, a, b, c, 14,  9, T26);
-    SET(c, d, a, b,  3, 14, T27);
-    SET(b, c, d, a,  8, 20, T28);
-    SET(a, b, c, d, 13,  5, T29);
-    SET(d, a, b, c,  2,  9, T30);
-    SET(c, d, a, b,  7, 14, T31);
+    SET(b, c, d, a, 4, 20, T24);
+    SET(a, b, c, d, 9, 5, T25);
+    SET(d, a, b, c, 14, 9, T26);
+    SET(c, d, a, b, 3, 14, T27);
+    SET(b, c, d, a, 8, 20, T28);
+    SET(a, b, c, d, 13, 5, T29);
+    SET(d, a, b, c, 2, 9, T30);
+    SET(c, d, a, b, 7, 14, T31);
     SET(b, c, d, a, 12, 20, T32);
 #undef SET
 
-     /* Round 3. */
-     /* Let [abcd k s t] denote the operation
+    /* Round 3. */
+    /* Let [abcd k s t] denote the operation
           a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
 #define H(x, y, z) ((x) ^ (y) ^ (z))
-#define SET(a, b, c, d, k, s, Ti)\
-  t = a + H(b,c,d) + X[k] + Ti;\
-  a = ROTATE_LEFT(t, s) + b
-     /* Do the following 16 operations. */
-    SET(a, b, c, d,  5,  4, T33);
-    SET(d, a, b, c,  8, 11, T34);
+#define SET(a, b, c, d, k, s, Ti)   \
+    t = a + H(b, c, d) + X[k] + Ti; \
+    a = ROTATE_LEFT(t, s) + b
+    /* Do the following 16 operations. */
+    SET(a, b, c, d, 5, 4, T33);
+    SET(d, a, b, c, 8, 11, T34);
     SET(c, d, a, b, 11, 16, T35);
     SET(b, c, d, a, 14, 23, T36);
-    SET(a, b, c, d,  1,  4, T37);
-    SET(d, a, b, c,  4, 11, T38);
-    SET(c, d, a, b,  7, 16, T39);
+    SET(a, b, c, d, 1, 4, T37);
+    SET(d, a, b, c, 4, 11, T38);
+    SET(c, d, a, b, 7, 16, T39);
     SET(b, c, d, a, 10, 23, T40);
-    SET(a, b, c, d, 13,  4, T41);
-    SET(d, a, b, c,  0, 11, T42);
-    SET(c, d, a, b,  3, 16, T43);
-    SET(b, c, d, a,  6, 23, T44);
-    SET(a, b, c, d,  9,  4, T45);
+    SET(a, b, c, d, 13, 4, T41);
+    SET(d, a, b, c, 0, 11, T42);
+    SET(c, d, a, b, 3, 16, T43);
+    SET(b, c, d, a, 6, 23, T44);
+    SET(a, b, c, d, 9, 4, T45);
     SET(d, a, b, c, 12, 11, T46);
     SET(c, d, a, b, 15, 16, T47);
-    SET(b, c, d, a,  2, 23, T48);
+    SET(b, c, d, a, 2, 23, T48);
 #undef SET
 
-     /* Round 4. */
-     /* Let [abcd k s t] denote the operation
+    /* Round 4. */
+    /* Let [abcd k s t] denote the operation
           a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
 #define I(x, y, z) ((y) ^ ((x) | ~(z)))
-#define SET(a, b, c, d, k, s, Ti)\
-  t = a + I(b,c,d) + X[k] + Ti;\
-  a = ROTATE_LEFT(t, s) + b
-     /* Do the following 16 operations. */
-    SET(a, b, c, d,  0,  6, T49);
-    SET(d, a, b, c,  7, 10, T50);
+#define SET(a, b, c, d, k, s, Ti)   \
+    t = a + I(b, c, d) + X[k] + Ti; \
+    a = ROTATE_LEFT(t, s) + b
+    /* Do the following 16 operations. */
+    SET(a, b, c, d, 0, 6, T49);
+    SET(d, a, b, c, 7, 10, T50);
     SET(c, d, a, b, 14, 15, T51);
-    SET(b, c, d, a,  5, 21, T52);
-    SET(a, b, c, d, 12,  6, T53);
-    SET(d, a, b, c,  3, 10, T54);
+    SET(b, c, d, a, 5, 21, T52);
+    SET(a, b, c, d, 12, 6, T53);
+    SET(d, a, b, c, 3, 10, T54);
     SET(c, d, a, b, 10, 15, T55);
-    SET(b, c, d, a,  1, 21, T56);
-    SET(a, b, c, d,  8,  6, T57);
+    SET(b, c, d, a, 1, 21, T56);
+    SET(a, b, c, d, 8, 6, T57);
     SET(d, a, b, c, 15, 10, T58);
-    SET(c, d, a, b,  6, 15, T59);
+    SET(c, d, a, b, 6, 15, T59);
     SET(b, c, d, a, 13, 21, T60);
-    SET(a, b, c, d,  4,  6, T61);
+    SET(a, b, c, d, 4, 6, T61);
     SET(d, a, b, c, 11, 10, T62);
-    SET(c, d, a, b,  2, 15, T63);
-    SET(b, c, d, a,  9, 21, T64);
+    SET(c, d, a, b, 2, 15, T63);
+    SET(b, c, d, a, 9, 21, T64);
 #undef SET
 
-     /* Then perform the following additions. (That is increment each
+    /* Then perform the following additions. (That is increment each
         of the four registers by the value it had before this block
         was started.) */
     pms->abcd[0] += a;
@@ -336,13 +336,10 @@
 }
 
 #ifdef __cplusplus
-extern "C"
-{
+extern "C" {
 #endif
 
-void
-md5_init(md5_state_t *pms)
-{
+void md5_init(md5_state_t* pms) {
     pms->count[0] = pms->count[1] = 0;
     pms->abcd[0] = 0x67452301;
     pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476;
@@ -350,67 +347,62 @@
     pms->abcd[3] = 0x10325476;
 }
 
-void
-md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes)
-{
-    const md5_byte_t *p = data;
+void md5_append(md5_state_t* pms, const md5_byte_t* data, int nbytes) {
+    const md5_byte_t* p = data;
     int left = nbytes;
     int offset = (pms->count[0] >> 3) & 63;
     md5_word_t nbits = (md5_word_t)(nbytes << 3);
 
     if (nbytes <= 0)
-    return;
+        return;
 
     /* Update the message length. */
     pms->count[1] += nbytes >> 29;
     pms->count[0] += nbits;
     if (pms->count[0] < nbits)
-    pms->count[1]++;
+        pms->count[1]++;
 
     /* Process an initial partial block. */
     if (offset) {
-    int copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
+        int copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
 
-    memcpy(pms->buf + offset, p, copy);
-    if (offset + copy < 64)
-        return;
-    p += copy;
-    left -= copy;
-    md5_process(pms, pms->buf);
+        memcpy(pms->buf + offset, p, copy);
+        if (offset + copy < 64)
+            return;
+        p += copy;
+        left -= copy;
+        md5_process(pms, pms->buf);
     }
 
     /* Process full blocks. */
     for (; left >= 64; p += 64, left -= 64)
-    md5_process(pms, p);
+        md5_process(pms, p);
 
     /* Process a final partial block. */
     if (left)
-    memcpy(pms->buf, p, left);
+        memcpy(pms->buf, p, left);
 }
 
-void
-md5_finish(md5_state_t *pms, md5_byte_t digest[16])
-{
+void md5_finish(md5_state_t* pms, md5_byte_t digest[16]) {
     static const md5_byte_t pad[64] = {
-    0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
-    };
+        0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
     md5_byte_t data[8];
     int i;
 
     /* Save the length before padding. */
     for (i = 0; i < 8; ++i)
-    data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3));
+        data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3));
     /* Pad to 56 bytes mod 64. */
     md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
     /* Append the length. */
     md5_append(pms, data, 8);
     for (i = 0; i < 16; ++i)
-    digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
+        digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
 }
 
 #ifdef __cplusplus
-}  /* end extern "C" */
+} /* end extern "C" */
 #endif
diff --git a/src/main/c/md5/md5.h b/src/main/c/md5/md5.h
index 3b88587..11ebd36 100644
--- a/src/main/c/md5/md5.h
+++ b/src/main/c/md5/md5.h
@@ -1,26 +1,26 @@
-// Copyright (c) 2013, Matt Godbolt
+// Copyright (c) 2013-2017, Matt Godbolt
 // All rights reserved.
-// 
-// Redistribution and use in source and binary forms, with or without 
+//
+// 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 
+//
+// 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 
+//
+// 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 
+//
+// 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.
 
 /*
@@ -73,7 +73,7 @@
  */
 
 #ifndef md5_INCLUDED
-#  define md5_INCLUDED
+#define md5_INCLUDED
 
 /*
  * This package supports both compile-time and run-time determination of CPU
@@ -85,32 +85,31 @@
  * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined.
  */
 
-typedef unsigned char md5_byte_t; /* 8-bit byte */
-typedef unsigned int md5_word_t; /* 32-bit word */
+using md5_byte_t = unsigned char; /* 8-bit byte */
+using md5_word_t = unsigned int;  /* 32-bit word */
 
 /* Define the state of the MD5 Algorithm. */
 typedef struct md5_state_s {
-    md5_word_t count[2];    /* message length in bits, lsw first */
-    md5_word_t abcd[4];        /* digest buffer */
-    md5_byte_t buf[64];        /* accumulate block */
+    md5_word_t count[2]; /* message length in bits, lsw first */
+    md5_word_t abcd[4];  /* digest buffer */
+    md5_byte_t buf[64];  /* accumulate block */
 } md5_state_t;
 
 #ifdef __cplusplus
-extern "C" 
-{
+extern "C" {
 #endif
 
 /* Initialize the algorithm. */
-void md5_init(md5_state_t *pms);
+void md5_init(md5_state_t* pms);
 
 /* Append a string to the message. */
-void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes);
+void md5_append(md5_state_t* pms, const md5_byte_t* data, int nbytes);
 
 /* Finish the message and return the digest. */
-void md5_finish(md5_state_t *pms, md5_byte_t digest[16]);
+void md5_finish(md5_state_t* pms, md5_byte_t digest[16]);
 
 #ifdef __cplusplus
-}  /* end extern "C" */
+} /* end extern "C" */
 #endif
 
 #endif /* md5_INCLUDED */
diff --git a/src/main/c/seasocks/Connection.h b/src/main/c/seasocks/Connection.h
index c1aa996..3ffb5a1 100644
--- a/src/main/c/seasocks/Connection.h
+++ b/src/main/c/seasocks/Connection.h
@@ -1,43 +1,47 @@
-// Copyright (c) 2013, Matt Godbolt
+// Copyright (c) 2013-2017, Matt Godbolt
 // All rights reserved.
-// 
-// Redistribution and use in source and binary forms, with or without 
+//
+// 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 
+//
+// 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 
+//
+// 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 
+//
+// 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/ResponseCode.h"
 #include "seasocks/WebSocket.h"
+#include "seasocks/ResponseWriter.h"
+#include "seasocks/TransferEncoding.h"
+#include "seasocks/ZlibContext.h"
 
 #include <netinet/in.h>
 
 #include <sys/socket.h>
 
-#include <inttypes.h>
+#include <cinttypes>
 #include <list>
 #include <memory>
 #include <string>
 #include <vector>
 
+
 namespace seasocks {
 
 class Logger;
@@ -48,17 +52,19 @@
 class Connection : public WebSocket {
 public:
     Connection(
-            std::shared_ptr<Logger> logger,
-            ServerImpl& server,
-            int fd,
-            const sockaddr_in& address);
+        std::shared_ptr<Logger> logger,
+        ServerImpl& server,
+        int fd,
+        const sockaddr_in& address);
     virtual ~Connection();
 
     bool write(const void* data, size_t size, bool flush);
     void handleDataReadyForRead();
     void handleDataReadyForWrite();
 
-    int getFd() const { return _fd; }
+    int getFd() const {
+        return _fd;
+    }
 
     // From WebSocket.
     virtual void send(const char* webSocketResponse) override;
@@ -67,24 +73,43 @@
 
     // From Request.
     virtual std::shared_ptr<Credentials> credentials() const override;
-    virtual const sockaddr_in& getRemoteAddress() const override { return _address; }
+    virtual const sockaddr_in& getRemoteAddress() const override {
+        return _address;
+    }
     virtual const std::string& getRequestUri() const override;
-    virtual Request::Verb verb() const override { return Request::WebSocket; }
-    virtual size_t contentLength() const override { return 0; }
-    virtual const uint8_t* content() const override { return NULL; }
+    virtual Request::Verb verb() const override {
+        return Request::Verb::WebSocket;
+    }
+    virtual size_t contentLength() const override {
+        return 0;
+    }
+    virtual const uint8_t* content() const override {
+        return nullptr;
+    }
     virtual bool hasHeader(const std::string&) const override;
     virtual std::string getHeader(const std::string&) const override;
+    virtual Server& server() const override;
 
     void setLinger();
 
-    size_t inputBufferSize() const { return _inBuf.size(); }
-    size_t outputBufferSize() const { return _outBuf.size(); }
+    size_t inputBufferSize() const {
+        return _inBuf.size();
+    }
+    size_t outputBufferSize() const {
+        return _outBuf.size();
+    }
 
-    size_t bytesReceived() const { return _bytesReceived; }
-    size_t bytesSent() const { return _bytesSent; }
+    size_t bytesReceived() const {
+        return _bytesReceived;
+    }
+    size_t bytesSent() const {
+        return _bytesSent;
+    }
 
     // For testing:
-    std::vector<uint8_t>& getInputBuffer() { return _inBuf; }
+    std::vector<uint8_t>& getInputBuffer() {
+        return _inBuf;
+    }
     void handleHixieWebSocket();
     void handleHybiWebSocket();
     void setHandler(std::shared_ptr<WebSocket::Handler> handler) {
@@ -92,6 +117,11 @@
     }
     void handleNewData();
 
+
+    Connection(Connection& other) = delete;
+    Connection& operator=(Connection& other) = delete;
+
+
 private:
     void finalise();
     bool closed() const;
@@ -121,31 +151,45 @@
     bool sendBadRequest(const std::string& reason);
     bool sendISE(const std::string& error);
 
-    void sendHybi(int opcode, const uint8_t* webSocketResponse, size_t messageLength);
+    void sendHybi(uint8_t opcode, const uint8_t* webSocketResponse,
+                  size_t messageLength);
+    void sendHybiData(const uint8_t* webSocketResponse, size_t messageLength);
+
 
     bool sendResponse(std::shared_ptr<Response> response);
 
     bool processHeaders(uint8_t* first, uint8_t* last);
     bool sendData(const std::string& type, const char* start, size_t size);
+    bool sendHeader(const std::string& type, size_t size);
+
+    // Delegated from ResponseWriter.
+    struct Writer;
+    void begin(ResponseCode responseCode, TransferEncoding encoding);
+    void header(const std::string& header, const std::string& value);
+    void payload(const void* data, size_t size, bool flush);
+    void finish(bool keepConnectionOpen);
+    void error(ResponseCode responseCode, const std::string& payload);
 
     struct Range {
         long start;
         long end;
-        size_t length() const { return end - start + 1; }
+        size_t length() const {
+            return end - start + 1;
+        }
     };
 
     bool parseRange(const std::string& rangeStr, Range& range) const;
     bool parseRanges(const std::string& range, std::list<Range>& ranges) const;
     bool sendStaticData();
 
-    int safeSend(const void* data, size_t size);
+    ssize_t safeSend(const void* data, size_t size);
 
     void bufferResponseAndCommonHeaders(ResponseCode code);
 
     std::list<Range> processRangesForStaticData(const std::list<Range>& ranges, long fileSize);
 
     std::shared_ptr<Logger> _logger;
-    ServerImpl &_server;
+    ServerImpl& _server;
     int _fd;
     bool _shutdown;
     bool _hadSendError;
@@ -159,19 +203,31 @@
     std::shared_ptr<WebSocket::Handler> _webSocketHandler;
     bool _shutdownByUser;
     std::unique_ptr<PageRequest> _request;
+    std::shared_ptr<Response> _response;
+    TransferEncoding _transferEncoding;
+    unsigned _chunk;
+    std::shared_ptr<Writer> _writer;
 
-    enum State {
+    void parsePerMessageDeflateHeader(const std::string& header);
+    bool _perMessageDeflate = false;
+    ZlibContext zlibContext;
+
+    void pickProtocol();
+
+    enum class State {
         INVALID,
         READING_HEADERS,
         READING_WEBSOCKET_KEY3,
         HANDLING_HIXIE_WEBSOCKET,
         HANDLING_HYBI_WEBSOCKET,
         BUFFERING_POST_DATA,
+        AWAITING_RESPONSE_BEGIN,
+        SENDING_RESPONSE_HEADERS,
+        SENDING_RESPONSE_BODY
     };
     State _state;
 
-    Connection(Connection& other) = delete;
-    Connection& operator =(Connection& other) = delete;
+    void writeChunkHeader(size_t size);
 };
 
-}  // namespace seasocks
+} // namespace seasocks
diff --git a/src/main/c/seasocks/Credentials.h b/src/main/c/seasocks/Credentials.h
index 06532c2..385f55b 100644
--- a/src/main/c/seasocks/Credentials.h
+++ b/src/main/c/seasocks/Credentials.h
@@ -1,26 +1,26 @@
-// Copyright (c) 2013, Matt Godbolt
+// Copyright (c) 2013-2017, Matt Godbolt
 // All rights reserved.
-// 
-// Redistribution and use in source and binary forms, with or without 
+//
+// 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 
+//
+// 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 
+//
+// 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 
+//
+// 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
@@ -53,21 +53,25 @@
      */
     std::map<std::string, std::string> attributes;
 
-    Credentials(): authenticated(false) {}
+    Credentials()
+            : authenticated(false), username(), groups(), attributes() {
+    }
 };
 
-inline std::ostream &operator<<(std::ostream &os, const Credentials& credentials) {
+inline std::ostream& operator<<(std::ostream& os, const Credentials& credentials) {
     os << "{authenticated:" << credentials.authenticated << ", username:'" << credentials.username << "', groups: {";
     for (auto it = credentials.groups.begin(); it != credentials.groups.end(); ++it) {
-        if (it != credentials.groups.begin()) os << ", ";
+        if (it != credentials.groups.begin())
+            os << ", ";
         os << *it;
     }
     os << "}, attrs: {";
     for (auto it = credentials.attributes.begin(); it != credentials.attributes.end(); ++it) {
-        if (it != credentials.attributes.begin()) os << ", ";
+        if (it != credentials.attributes.begin())
+            os << ", ";
         os << it->first << "=" << it->second;
     }
     return os << "}}";
 }
 
-}  // namespace seasocks
+} // namespace seasocks
diff --git a/src/main/c/seasocks/IgnoringLogger.h b/src/main/c/seasocks/IgnoringLogger.h
index c1d4b1e..6e5afa0 100644
--- a/src/main/c/seasocks/IgnoringLogger.h
+++ b/src/main/c/seasocks/IgnoringLogger.h
@@ -1,26 +1,26 @@
-// Copyright (c) 2013, Matt Godbolt
+// Copyright (c) 2013-2017, Matt Godbolt
 // All rights reserved.
-// 
-// Redistribution and use in source and binary forms, with or without 
+//
+// 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 
+//
+// 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 
+//
+// 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 
+//
+// 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
@@ -31,8 +31,8 @@
 
 class IgnoringLogger : public Logger {
 public:
-    virtual void log(Level level, const char* message) {
+    virtual void log(Level /*level*/, const char* /*message*/) override {
     }
 };
 
-}  // namespace seasocks
+} // namespace seasocks
diff --git a/src/main/c/seasocks/Logger.h b/src/main/c/seasocks/Logger.h
index 317c54a..ea06410 100644
--- a/src/main/c/seasocks/Logger.h
+++ b/src/main/c/seasocks/Logger.h
@@ -1,26 +1,26 @@
-// Copyright (c) 2013, Matt Godbolt
+// Copyright (c) 2013-2017, Matt Godbolt
 // All rights reserved.
-// 
-// Redistribution and use in source and binary forms, with or without 
+//
+// 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 
+//
+// 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 
+//
+// 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 
+//
+// 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
@@ -32,15 +32,15 @@
  */
 class Logger {
 public:
-    virtual ~Logger() {}
+    virtual ~Logger() = default;
 
-    enum Level {
-        DEBUG,  // NB DEBUG is usually opted-out of at compile-time.
-        ACCESS, // Used to log page requests etc
-        INFO,
-        WARNING,
-        ERROR,
-        SEVERE,
+    enum class Level {
+        Debug,  // NB Debug is usually opted-out of at compile-time.
+        Access, // Used to log page requests etc
+        Info,
+        Warning,
+        Error,
+        Severe,
     };
 
     virtual void log(Level level, const char* message) = 0;
@@ -54,15 +54,22 @@
 
     static const char* levelToString(Level level) {
         switch (level) {
-        case DEBUG: return "debug";
-        case ACCESS: return "access";
-        case INFO: return "info";
-        case WARNING: return "warning";
-        case ERROR: return "ERROR";
-        case SEVERE: return "SEVERE";
-        default: return "???";
+            case Level::Debug:
+                return "debug";
+            case Level::Access:
+                return "access";
+            case Level::Info:
+                return "info";
+            case Level::Warning:
+                return "warning";
+            case Level::Error:
+                return "ERROR";
+            case Level::Severe:
+                return "SEVERE";
+            default:
+                return "???";
         }
     }
 };
 
-}  // namespace seasocks
+} // namespace seasocks
diff --git a/src/main/c/seasocks/PageHandler.h b/src/main/c/seasocks/PageHandler.h
index d9e671d..cc43c21 100644
--- a/src/main/c/seasocks/PageHandler.h
+++ b/src/main/c/seasocks/PageHandler.h
@@ -1,26 +1,26 @@
-// Copyright (c) 2013, Matt Godbolt
+// Copyright (c) 2013-2017, Matt Godbolt
 // All rights reserved.
-// 
-// Redistribution and use in source and binary forms, with or without 
+//
+// 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 
+//
+// 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 
+//
+// 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 
+//
+// 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
@@ -37,7 +37,7 @@
 
 class PageHandler {
 public:
-    virtual ~PageHandler() {}
+    virtual ~PageHandler() = default;
 
     virtual std::shared_ptr<Response> handle(const Request& request) = 0;
 };
diff --git a/src/main/c/seasocks/PrintfLogger.h b/src/main/c/seasocks/PrintfLogger.h
index f08b1b0..c134896 100644
--- a/src/main/c/seasocks/PrintfLogger.h
+++ b/src/main/c/seasocks/PrintfLogger.h
@@ -1,45 +1,45 @@
-// Copyright (c) 2013, Matt Godbolt
+// Copyright (c) 2013-2017, Matt Godbolt
 // All rights reserved.
-// 
-// Redistribution and use in source and binary forms, with or without 
+//
+// 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 
+//
+// 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 
+//
+// 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 
+//
+// 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/Logger.h"
 
-#include <stdio.h>
+#include <cstdio>
 
 namespace seasocks {
 
 class PrintfLogger : public Logger {
 public:
-    PrintfLogger(Level minLevelToLog = Level::DEBUG) : minLevelToLog(minLevelToLog) {
+    explicit PrintfLogger(Level minLevelToLog_ = Level::Debug)
+            : minLevelToLog(minLevelToLog_) {
     }
 
-    ~PrintfLogger() {
-    }
+    ~PrintfLogger() = default;
 
-    virtual void log(Level level, const char* message) {
+    virtual void log(Level level, const char* message) override {
         if (level >= minLevelToLog) {
             printf("%s: %s\n", levelToString(level), message);
         }
@@ -48,4 +48,4 @@
     Level minLevelToLog;
 };
 
-}  // namespace seasocks
+} // namespace seasocks
diff --git a/src/main/c/seasocks/Request.cpp b/src/main/c/seasocks/Request.cpp
index 63be5a6..6d8b5a8 100644
--- a/src/main/c/seasocks/Request.cpp
+++ b/src/main/c/seasocks/Request.cpp
@@ -1,26 +1,26 @@
-// Copyright (c) 2013, Matt Godbolt
+// Copyright (c) 2013-2017, Matt Godbolt
 // All rights reserved.
-// 
-// Redistribution and use in source and binary forms, with or without 
+//
+// 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 
+//
+// 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 
+//
+// 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 
+//
+// 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/Request.h"
@@ -30,23 +30,38 @@
 namespace seasocks {
 
 const char* Request::name(Verb v) {
-    switch(v) {
-    case Invalid: return "Invalid";
-    case WebSocket: return "WebSocket";
-    case Get: return "Get";
-    case Put: return "Put";
-    case Post: return "Post";
-    case Delete: return "Delete";
-    default: return "???";
+    switch (v) {
+        case Verb::Invalid:
+            return "Invalid";
+        case Verb::WebSocket:
+            return "WebSocket";
+        case Verb::Get:
+            return "Get";
+        case Verb::Put:
+            return "Put";
+        case Verb::Post:
+            return "Post";
+        case Verb::Delete:
+            return "Delete";
+        case Verb::Head:
+            return "Head";
+        default:
+            return "???";
     }
 }
 
 Request::Verb Request::verb(const char* verb) {
-    if (std::strcmp(verb, "GET") == 0) return Request::Get;
-    if (std::strcmp(verb, "PUT") == 0) return Request::Put;
-    if (std::strcmp(verb, "POST") == 0) return Request::Post;
-    if (std::strcmp(verb, "DELETE") == 0) return Request::Delete;
-    return Request::Invalid;
+    if (std::strcmp(verb, "GET") == 0)
+        return Request::Verb::Get;
+    if (std::strcmp(verb, "PUT") == 0)
+        return Request::Verb::Put;
+    if (std::strcmp(verb, "POST") == 0)
+        return Request::Verb::Post;
+    if (std::strcmp(verb, "DELETE") == 0)
+        return Request::Verb::Delete;
+    if (std::strcmp(verb, "HEAD") == 0)
+        return Request::Verb::Head;
+    return Request::Verb::Invalid;
 }
 
-}  // namespace seasocks
+} // namespace seasocks
diff --git a/src/main/c/seasocks/Request.h b/src/main/c/seasocks/Request.h
index 7c0e78a..d7d9402 100644
--- a/src/main/c/seasocks/Request.h
+++ b/src/main/c/seasocks/Request.h
@@ -1,26 +1,26 @@
-// Copyright (c) 2013, Matt Godbolt
+// Copyright (c) 2013-2017, Matt Godbolt
 // All rights reserved.
-// 
-// Redistribution and use in source and binary forms, with or without 
+//
+// 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 
+//
+// 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 
+//
+// 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 
+//
+// 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
@@ -34,23 +34,28 @@
 
 namespace seasocks {
 
+class Server;
+
 class Request {
 public:
-    virtual ~Request() {}
+    virtual ~Request() = default;
 
-    enum Verb {
+    enum class Verb {
         Invalid,
         WebSocket,
         Get,
         Put,
         Post,
         Delete,
+        Head,
     };
 
+    virtual Server& server() const = 0;
+
     virtual Verb verb() const = 0;
 
     static const char* name(Verb v);
-    static Verb verb(const char *verb);
+    static Verb verb(const char* verb);
 
     /**
      * Returns the credentials associated with this request.
@@ -70,4 +75,4 @@
     virtual std::string getHeader(const std::string& name) const = 0;
 };
 
-}  // namespace seasocks
+} // namespace seasocks
diff --git a/src/main/c/seasocks/Response.h b/src/main/c/seasocks/Response.h
index 02cf03b..df523ce 100644
--- a/src/main/c/seasocks/Response.h
+++ b/src/main/c/seasocks/Response.h
@@ -1,26 +1,26 @@
-// Copyright (c) 2013, Matt Godbolt
+// Copyright (c) 2013-2015, Matt Godbolt
 // All rights reserved.
-// 
-// Redistribution and use in source and binary forms, with or without 
+//
+// 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 
+//
+// 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 
+//
+// 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 
+//
+// 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
@@ -33,20 +33,15 @@
 
 namespace seasocks {
 
+class ResponseWriter;
+
 class Response {
 public:
-    virtual ~Response() {}
-    virtual ResponseCode responseCode() const = 0;
+    virtual ~Response() = default;
+    virtual void handle(std::shared_ptr<ResponseWriter> writer) = 0;
 
-    virtual const char* payload() const = 0;
-    virtual size_t payloadSize() const = 0;
-
-    virtual std::string contentType() const = 0;
-
-    virtual bool keepConnectionAlive() const = 0;
-
-    typedef std::multimap<std::string, std::string> Headers;
-    virtual Headers getAdditionalHeaders() const = 0;
+    // Called when the connection this response is being sent on is closed.
+    virtual void cancel() = 0;
 
     static std::shared_ptr<Response> unhandled();
 
diff --git a/src/main/c/seasocks/ResponseBuilder.cpp b/src/main/c/seasocks/ResponseBuilder.cpp
index f9f9b29..bd1dcc2 100644
--- a/src/main/c/seasocks/ResponseBuilder.cpp
+++ b/src/main/c/seasocks/ResponseBuilder.cpp
@@ -1,26 +1,26 @@
-// Copyright (c) 2013, Matt Godbolt
+// Copyright (c) 2013-2017, Matt Godbolt
 // All rights reserved.
-// 
-// Redistribution and use in source and binary forms, with or without 
+//
+// 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 
+//
+// 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 
+//
+// 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 
+//
+// 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/ResponseBuilder.h"
@@ -29,12 +29,11 @@
 
 namespace seasocks {
 
-ResponseBuilder::ResponseBuilder(ResponseCode code) :
-        _code(code),
-        _contentType("text/plain"),
-        _keepAlive(true),
-        _stream(new std::ostringstream) {
-
+ResponseBuilder::ResponseBuilder(ResponseCode code)
+        : _code(code),
+          _contentType("text/plain"),
+          _keepAlive(true),
+          _stream(std::make_shared<std::ostringstream>()) {
 }
 
 ResponseBuilder& ResponseBuilder::asHtml() {
@@ -84,7 +83,7 @@
 }
 
 std::shared_ptr<Response> ResponseBuilder::build() {
-    return std::shared_ptr<Response>(new ConcreteResponse(_code, _stream->str(), _contentType, _headers, _keepAlive));
+    return std::make_shared<ConcreteResponse>(_code, _stream->str(), _contentType, _headers, _keepAlive);
 }
 
 }
diff --git a/src/main/c/seasocks/ResponseBuilder.h b/src/main/c/seasocks/ResponseBuilder.h
index 0a268e8..40484c1 100644
--- a/src/main/c/seasocks/ResponseBuilder.h
+++ b/src/main/c/seasocks/ResponseBuilder.h
@@ -1,31 +1,32 @@
-// Copyright (c) 2013, Matt Godbolt
+// Copyright (c) 2013-2017, Matt Godbolt
 // All rights reserved.
-// 
-// Redistribution and use in source and binary forms, with or without 
+//
+// 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 
+//
+// 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 
+//
+// 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 
+//
+// 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/Response.h"
+#include "seasocks/SynchronousResponse.h"
+#include "seasocks/ToString.h"
 
 #include <sstream>
 #include <string>
@@ -36,10 +37,11 @@
     ResponseCode _code;
     std::string _contentType;
     bool _keepAlive;
-    Response::Headers _headers;
+    SynchronousResponse::Headers _headers;
     std::shared_ptr<std::ostringstream> _stream;
+
 public:
-    ResponseBuilder(ResponseCode code = ResponseCode::Ok);
+    explicit ResponseBuilder(ResponseCode code = ResponseCode::Ok);
 
     ResponseBuilder& asHtml();
     ResponseBuilder& asText();
@@ -54,20 +56,20 @@
     ResponseBuilder& setsCookie(const std::string& cookie, const std::string& value);
 
     ResponseBuilder& withHeader(const std::string& name, const std::string& value);
-    template<typename T>
+    template <typename T>
     ResponseBuilder& withHeader(const std::string& name, const T& t) {
         return withHeader(name, toString(t));
     }
 
     ResponseBuilder& addHeader(const std::string& name, const std::string& value);
-    template<typename T>
+    template <typename T>
     ResponseBuilder& addHeader(const std::string& name, const T& t) {
         return addHeader(name, toString(t));
     }
 
-    template<typename T>
-    ResponseBuilder& operator << (T&& t) {
-        (*_stream) << std::forward(t);
+    template <typename T>
+    ResponseBuilder& operator<<(T&& t) {
+        (*_stream) << std::forward<T>(t);
         return *this;
     }
 
diff --git a/src/main/c/seasocks/ResponseCode.cpp b/src/main/c/seasocks/ResponseCode.cpp
index 71e88f6..fed00ad 100644
--- a/src/main/c/seasocks/ResponseCode.cpp
+++ b/src/main/c/seasocks/ResponseCode.cpp
@@ -1,26 +1,26 @@
-// Copyright (c) 2013, Matt Godbolt
+// Copyright (c) 2013-2017, Matt Godbolt
 // All rights reserved.
-// 
-// Redistribution and use in source and binary forms, with or without 
+//
+// 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 
+//
+// 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 
+//
+// 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 
+//
+// 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/ResponseCode.h"
@@ -33,7 +33,9 @@
 
 const char* name(ResponseCode code) {
     switch (code) {
-#define SEASOCKS_DEFINE_RESPONSECODE(CODE,SYMBOLICNAME,STRINGNAME) case ResponseCode::SYMBOLICNAME: return STRINGNAME;
+#define SEASOCKS_DEFINE_RESPONSECODE(CODE, SYMBOLICNAME, STRINGNAME) \
+    case ResponseCode::SYMBOLICNAME:                                 \
+        return STRINGNAME;
 #include "seasocks/ResponseCodeDefs.h"
 
 #undef SEASOCKS_DEFINE_RESPONSECODE
diff --git a/src/main/c/seasocks/ResponseCode.h b/src/main/c/seasocks/ResponseCode.h
index eab3463..2d1916b 100644
--- a/src/main/c/seasocks/ResponseCode.h
+++ b/src/main/c/seasocks/ResponseCode.h
@@ -1,26 +1,26 @@
-// Copyright (c) 2013, Matt Godbolt
+// Copyright (c) 2013-2017, Matt Godbolt
 // All rights reserved.
-// 
-// Redistribution and use in source and binary forms, with or without 
+//
+// 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 
+//
+// 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 
+//
+// 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 
+//
+// 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
@@ -29,7 +29,7 @@
 
 namespace seasocks {
 
-#define SEASOCKS_DEFINE_RESPONSECODE(CODE,SYMBOLICNAME,STRINGNAME) SYMBOLICNAME = CODE,
+#define SEASOCKS_DEFINE_RESPONSECODE(CODE, SYMBOLICNAME, STRINGNAME) SYMBOLICNAME = CODE,
 enum class ResponseCode {
 #include "seasocks/ResponseCodeDefs.h"
 
diff --git a/src/main/c/seasocks/ResponseCodeDefs.h b/src/main/c/seasocks/ResponseCodeDefs.h
index 9b9878d..382af8c 100644
--- a/src/main/c/seasocks/ResponseCodeDefs.h
+++ b/src/main/c/seasocks/ResponseCodeDefs.h
@@ -1,32 +1,32 @@
-// Copyright (c) 2013, Matt Godbolt
+// Copyright (c) 2013-2017, Matt Godbolt
 // All rights reserved.
-// 
-// Redistribution and use in source and binary forms, with or without 
+//
+// 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 
+//
+// 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 
+//
+// 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 
+//
+// 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.
 
 // Not a normal header file - no header guards on purpose.
 // Do not directly include! Use ResponseCode.h instead
 
-#ifdef SEASOCKS_DEFINE_RESPONSECODE  // workaround for header check
+#ifdef SEASOCKS_DEFINE_RESPONSECODE // workaround for header check
 
 SEASOCKS_DEFINE_RESPONSECODE(100, Continue, "Continue")
 SEASOCKS_DEFINE_RESPONSECODE(101, WebSocketProtocolHandshake, "WebSocket Protocol Handshake")
diff --git a/src/main/c/seasocks/ResponseWriter.h b/src/main/c/seasocks/ResponseWriter.h
new file mode 100644
index 0000000..a0d2982
--- /dev/null
+++ b/src/main/c/seasocks/ResponseWriter.h
@@ -0,0 +1,71 @@
+// Copyright (c) 2013-2017, 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/ResponseCode.h"
+#include "seasocks/TransferEncoding.h"
+
+#include <string>
+
+namespace seasocks {
+
+// An interface to write a response to a Request. All methods must be called
+// from the Seasocks main thread. Safe in the presence of closed connections:
+// writes to connections that have closed are silently dropped. Responses that
+// wish to take note of closed connections must use their cancel() callback.
+class ResponseWriter {
+public:
+    virtual ~ResponseWriter() = default;
+
+    // Begins a request with the given code. If you want to generate a "normal"
+    // seasocks error, see 'error' below. After beginning, it is expected you
+    // will call 'header' zero or more times, then 'payload' zero or more times,
+    // then 'finish' exactly once. Using a transfer encoding other than Raw will
+    // automatically cause the appropriate header to be sent.
+    virtual void begin(ResponseCode responseCode,
+                       TransferEncoding encoding = TransferEncoding::Raw) = 0;
+    // Add a header. Must be called after 'begin' and before 'payload'. May be
+    // called as many times as needed.
+    virtual void header(const std::string& header, const std::string& value) = 0;
+    // Add some payload data. Must be called after 'begin' and any 'header' calls.
+    // May be called multiple times. The flush parameter controls whether the
+    // data should be sent immediately, or buffered to be sent with a subsequent
+    // call to 'payload', or 'finish'.
+    virtual void payload(const void* data, size_t size, bool flush = true) = 0;
+    // Finish a response.
+    virtual void finish(bool keepConnectionOpen) = 0;
+
+    // Send an error back to the user. No calls to 'header' or 'payload'
+    // or 'finish' should be executed. If you wish to serve your own error document
+    // then use the normal 'begin'/'header'/'payload'/'finish' process but with
+    // an error code. This routine is to get Seasocks to generate its own error.
+    virtual void error(ResponseCode responseCode, const std::string& payload) = 0;
+    // Check whether this writer is still active; i.e. the underlying connection
+    // is still open.
+    virtual bool isActive() const = 0;
+};
+
+}
diff --git a/src/main/c/seasocks/Server.h b/src/main/c/seasocks/Server.h
index 4629a43..63e54ab 100644
--- a/src/main/c/seasocks/Server.h
+++ b/src/main/c/seasocks/Server.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2013, Matt Godbolt
+// Copyright (c) 2013-2017, Matt Godbolt
 // All rights reserved.
 //
 // Redistribution and use in source and binary forms, with or without
@@ -32,6 +32,7 @@
 
 #include <atomic>
 #include <cstdint>
+#include <functional>
 #include <list>
 #include <map>
 #include <memory>
@@ -49,24 +50,13 @@
 
 class Server : private ServerImpl {
 public:
-    Server(std::shared_ptr<Logger> logger);
+    explicit Server(std::shared_ptr<Logger> logger);
     virtual ~Server();
 
     void addPageHandler(std::shared_ptr<PageHandler> handler);
 
     void addWebSocketHandler(const char* endpoint, std::shared_ptr<WebSocket::Handler> handler,
-            bool allowCrossOriginRequests = false);
-
-    // If we haven't heard anything ever on a connection for this long, kill it.
-    // This is possibly caused by bad WebSocket implementation in Chrome.
-    void setLameConnectionTimeoutSeconds(int seconds);
-
-    // Sets the maximum number of TCP level keepalives that we can miss before
-    // we let the OS consider the connection dead. We configure keepalives every second,
-    // so this is also the minimum number of seconds it takes to notice a badly-behaved
-    // dead connection, e.g. a laptop going into sleep mode or a hard-crashed machine.
-    // A value of 0 disables keep alives, which is the default.
-    void setMaxKeepAliveDrops(int maxKeepAliveDrops);
+                             bool allowCrossOriginRequests = false);
 
     // Serves static content from the given port on the current thread, until terminate is called.
     // Roughly equivalent to startListening(port); setStaticPath(staticPath); loop();
@@ -81,6 +71,10 @@
     // Returns true if all was ok.
     bool startListening(int port);
 
+    // Starts listening on a unix domain socket.
+    // Returns true if all was ok.
+    bool startListeningUnix(const char* socketPath);
+
     // Sets the path to server static content from.
     void setStaticPath(const char* staticPath);
 
@@ -102,42 +96,77 @@
     // Returns a file descriptor that can be polled for changes (e.g. by
     // placing it in an epoll set. The poll() method above only need be called
     // when this file descriptor is readable.
-    int fd() const { return _epollFd; }
+    int fd() const {
+        return _epollFd;
+    }
 
     // Terminate any loop() or poll(). May be called from any thread.
     void terminate();
 
+    // If we haven't heard anything ever on a connection for this long, kill it.
+    // This is possibly caused by bad WebSocket implementation in Chrome.
+    void setLameConnectionTimeoutSeconds(int seconds);
+
+    // Sets the maximum number of TCP level keepalives that we can miss before
+    // we let the OS consider the connection dead. We configure keepalives every second,
+    // so this is also the minimum number of seconds it takes to notice a badly-behaved
+    // dead connection, e.g. a laptop going into sleep mode or a hard-crashed machine.
+    // A value of 0 disables keep alives, which is the default.
+    void setMaxKeepAliveDrops(int maxKeepAliveDrops);
+
+    // Set the maximum amount of data we'll buffer for a client before we close the
+    // connection assuming the client can't keep up with the data rate. Default
+    // is available here too.
+    static constexpr size_t DefaultClientBufferSize = 16 * 1024 * 1024u;
+    void setClientBufferSize(size_t bytesToBuffer);
+    size_t clientBufferSize() const override {
+        return _clientBufferSize;
+    }
+
+    void setPerMessageDeflateEnabled(bool enabled);
+    bool getPerMessageDeflateEnabled() {
+        return _perMessageDeflateEnabled;
+    }
+
     class Runnable {
     public:
-        virtual ~Runnable() {}
+        virtual ~Runnable() = default;
         virtual void run() = 0;
     };
-    // Execute a task on the SeaSocks thread.
+    // Execute a task on the Seasocks thread.
     void execute(std::shared_ptr<Runnable> runnable);
+    using Executable = std::function<void()>;
+    void execute(Executable toExecute);
 
 private:
     // From ServerImpl
     virtual void remove(Connection* connection) override;
     virtual bool subscribeToWriteEvents(Connection* connection) override;
     virtual bool unsubscribeFromWriteEvents(Connection* connection) override;
-    virtual const std::string& getStaticPath() const override { return _staticPath; }
+    virtual const std::string& getStaticPath() const override {
+        return _staticPath;
+    }
     virtual std::shared_ptr<WebSocket::Handler> getWebSocketHandler(const char* endpoint) const override;
-    virtual bool isCrossOriginAllowed(const std::string &endpoint) const override;
-    virtual std::shared_ptr<Response> handle(const Request &request) override;
+    virtual bool isCrossOriginAllowed(const std::string& endpoint) const override;
+    virtual std::shared_ptr<Response> handle(const Request& request) override;
     virtual std::string getStatsDocument() const override;
     virtual void checkThread() const override;
+    virtual Server& server() override {
+        return *this;
+    }
 
     bool makeNonBlocking(int fd) const;
     bool configureSocket(int fd) const;
     void handleAccept();
-    std::shared_ptr<Runnable> popNextRunnable();
     void processEventQueue();
+    void runExecutables();
 
     void shutdown();
 
     void checkAndDispatchEpoll(int epollMillis);
     void handlePipe();
-    enum NewState { KeepOpen, Close };
+    enum class NewState { KeepOpen,
+                          Close };
     NewState handleConnectionEvents(Connection* connection, uint32_t events);
 
     // Connections, mapped to initial connection time.
@@ -148,8 +177,12 @@
     int _eventFd;
     int _maxKeepAliveDrops;
     int _lameConnectionTimeoutSeconds;
+    size_t _clientBufferSize;
     time_t _nextDeadConnectionCheck;
 
+    // Compression settings
+    bool _perMessageDeflateEnabled = false;
+
     struct WebSocketHandlerEntry {
         std::shared_ptr<WebSocket::Handler> handler;
         bool allowCrossOrigin;
@@ -159,8 +192,8 @@
 
     std::list<std::shared_ptr<PageHandler>> _pageHandlers;
 
-    std::mutex _pendingRunnableMutex;
-    std::list<std::shared_ptr<Runnable>> _pendingRunnables;
+    std::mutex _pendingExecutableMutex;
+    std::list<Executable> _pendingExecutables;
 
     pid_t _threadId;
 
@@ -169,4 +202,4 @@
     std::atomic<bool> _expectedTerminate;
 };
 
-}  // namespace seasocks
+} // namespace seasocks
diff --git a/src/main/c/seasocks/ServerImpl.h b/src/main/c/seasocks/ServerImpl.h
index e7dd46a..20b4780 100644
--- a/src/main/c/seasocks/ServerImpl.h
+++ b/src/main/c/seasocks/ServerImpl.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2013, Matt Godbolt
+// Copyright (c) 2013-2017, Matt Godbolt
 // All rights reserved.
 //
 // Redistribution and use in source and binary forms, with or without
@@ -34,21 +34,24 @@
 class Connection;
 class Request;
 class Response;
+class Server;
 
 // Internal implementation used to give access to internals to Connections.
 class ServerImpl {
 public:
-    virtual ~ServerImpl() {}
+    virtual ~ServerImpl() = default;
 
     virtual void remove(Connection* connection) = 0;
     virtual bool subscribeToWriteEvents(Connection* connection) = 0;
     virtual bool unsubscribeFromWriteEvents(Connection* connection) = 0;
     virtual const std::string& getStaticPath() const = 0;
     virtual std::shared_ptr<WebSocket::Handler> getWebSocketHandler(const char* endpoint) const = 0;
-    virtual bool isCrossOriginAllowed(const std::string &endpoint) const = 0;
-    virtual std::shared_ptr<Response> handle(const Request &request) = 0;
+    virtual bool isCrossOriginAllowed(const std::string& endpoint) const = 0;
+    virtual std::shared_ptr<Response> handle(const Request& request) = 0;
     virtual std::string getStatsDocument() const = 0;
     virtual void checkThread() const = 0;
+    virtual Server& server() = 0;
+    virtual size_t clientBufferSize() const = 0;
 };
 
 }
diff --git a/src/main/c/seasocks/SimpleResponse.h b/src/main/c/seasocks/SimpleResponse.h
new file mode 100644
index 0000000..b627ca5
--- /dev/null
+++ b/src/main/c/seasocks/SimpleResponse.h
@@ -0,0 +1,82 @@
+// Copyright (c) 2013-2017, Martin Charles
+// 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/StreamingResponse.h"
+
+namespace seasocks {
+
+class SimpleResponse : public StreamingResponse {
+    ResponseCode _responseCode;
+    std::shared_ptr<std::istream> _stream;
+    const Headers _headers;
+    const bool _keepAlive;
+    const bool _flushInstantly;
+    const size_t _bufferSize;
+    const TransferEncoding _transferEncoding;
+
+public:
+    static constexpr size_t DefaultBufferSize = 16 * 1024 * 1024u;
+
+    SimpleResponse(ResponseCode responseCode, std::shared_ptr<std::istream> stream,
+                   const Headers& headers, bool keepAlive = true, bool flushInstantly = false,
+                   size_t bufferSize = DefaultBufferSize,
+                   TransferEncoding transferEncoding = TransferEncoding::Raw)
+            : _responseCode(responseCode), _stream(stream), _headers(headers),
+              _keepAlive(keepAlive), _flushInstantly(flushInstantly),
+              _bufferSize(bufferSize), _transferEncoding(transferEncoding) {
+    }
+
+    virtual std::shared_ptr<std::istream> getStream() const override {
+        return _stream;
+    }
+
+    virtual Headers getHeaders() const override {
+        return _headers;
+    }
+
+    virtual ResponseCode responseCode() const override {
+        return _responseCode;
+    }
+
+    virtual bool keepConnectionAlive() const override {
+        return _keepAlive;
+    }
+
+    virtual bool flushInstantly() const override {
+        return _flushInstantly;
+    }
+
+    virtual size_t getBufferSize() const override {
+        return _bufferSize;
+    }
+
+    virtual TransferEncoding transferEncoding() const override {
+        return _transferEncoding;
+    }
+};
+
+}
diff --git a/src/main/c/seasocks/StreamingResponse.cpp b/src/main/c/seasocks/StreamingResponse.cpp
new file mode 100644
index 0000000..939b710
--- /dev/null
+++ b/src/main/c/seasocks/StreamingResponse.cpp
@@ -0,0 +1,69 @@
+// Copyright (c) 2013-2017, Martin Charles
+// 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/StreamingResponse.h"
+#include "seasocks/ToString.h"
+#include "seasocks/StringUtil.h"
+
+using namespace seasocks;
+
+void StreamingResponse::handle(std::shared_ptr<ResponseWriter> writer) {
+    writer->begin(responseCode(), transferEncoding());
+
+    auto headers = getHeaders();
+    for (auto& header : headers) {
+        writer->header(header.first, header.second);
+    }
+
+    std::shared_ptr<std::istream> stream = getStream();
+
+    auto bufSize = getBufferSize();
+    bool flush = flushInstantly();
+    std::unique_ptr<char[]> buffer(new char[bufSize]);
+
+    while (!closed) {
+        // blocks until buffer is full or eof is reached
+        stream->read(buffer.get(), bufSize);
+
+        bool isEof = stream->eof();
+        bool isGood = stream->good();
+        if (isGood || isEof) {
+            // everything is fine, push data to client
+            writer->payload(buffer.get(), stream->gcount(), flush);
+        }
+
+        if (!isGood) {
+            // EOF or error occured
+            // ignore the error since we can't access it
+            closed = true;
+        }
+    };
+
+    writer->finish(keepConnectionAlive());
+}
+
+void StreamingResponse::cancel() {
+    closed = true;
+}
diff --git a/src/main/c/seasocks/StreamingResponse.h b/src/main/c/seasocks/StreamingResponse.h
new file mode 100644
index 0000000..becd5e0
--- /dev/null
+++ b/src/main/c/seasocks/StreamingResponse.h
@@ -0,0 +1,60 @@
+// Copyright (c) 2013-2017, Martin Charles
+// 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/Response.h"
+#include "seasocks/ResponseWriter.h"
+#include "seasocks/TransferEncoding.h"
+
+#include <iosfwd>
+
+namespace seasocks {
+
+// A low level way to write create responses. This class doesn't meddle with or
+// assume anything based on the headers.
+class StreamingResponse : public Response {
+    bool closed = false;
+
+public:
+    virtual ~StreamingResponse() = default;
+    virtual void handle(std::shared_ptr<ResponseWriter> writer) override;
+    virtual void cancel() override;
+
+    virtual std::shared_ptr<std::istream> getStream() const = 0;
+
+    typedef std::multimap<std::string, std::string> Headers;
+    virtual Headers getHeaders() const = 0;
+
+    virtual ResponseCode responseCode() const = 0;
+
+    virtual bool keepConnectionAlive() const = 0;
+    virtual bool flushInstantly() const = 0;
+
+    virtual size_t getBufferSize() const = 0;
+    virtual TransferEncoding transferEncoding() const = 0;
+};
+
+}
diff --git a/src/main/c/seasocks/StringUtil.h b/src/main/c/seasocks/StringUtil.h
index 3109104..df2d38a 100644
--- a/src/main/c/seasocks/StringUtil.h
+++ b/src/main/c/seasocks/StringUtil.h
@@ -1,32 +1,33 @@
-// Copyright (c) 2013, Matt Godbolt
+// Copyright (c) 2013-2017, Matt Godbolt
 // All rights reserved.
-// 
-// Redistribution and use in source and binary forms, with or without 
+//
+// 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 
+//
+// 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 
+//
+// 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 
+//
+// 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 <netinet/in.h>
 
+#include <ctime>
 #include <string>
 #include <vector>
 
@@ -35,6 +36,7 @@
 char* skipWhitespace(char* str);
 char* skipNonWhitespace(char* str);
 char* shift(char*& str);
+std::string trimWhitespace(const std::string& str);
 
 std::string getLastError();
 std::string formatAddress(const sockaddr_in& address);
@@ -43,6 +45,11 @@
 
 void replace(std::string& string, const std::string& find, const std::string& replace);
 
-bool caseInsensitiveSame(const std::string &lhs, const std::string &rhs);
+bool caseInsensitiveSame(const std::string& lhs, const std::string& rhs);
 
-}  // namespace seasocks
+std::string webtime(time_t time);
+
+std::string now();
+
+
+} // namespace seasocks
diff --git a/src/main/c/seasocks/SynchronousResponse.cpp b/src/main/c/seasocks/SynchronousResponse.cpp
new file mode 100644
index 0000000..99ecf80
--- /dev/null
+++ b/src/main/c/seasocks/SynchronousResponse.cpp
@@ -0,0 +1,67 @@
+// Copyright (c) 2013-2017, 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/SynchronousResponse.h"
+#include "seasocks/ToString.h"
+#include "seasocks/StringUtil.h"
+
+using namespace seasocks;
+
+void SynchronousResponse::handle(std::shared_ptr<ResponseWriter> writer) {
+    auto rc = responseCode();
+    if (!isOk(rc)) {
+        writer->error(rc, std::string(payload(), payloadSize()));
+        return;
+    }
+
+    writer->begin(responseCode());
+
+    writer->header("Content-Length", toString(payloadSize()));
+    writer->header("Content-Type", contentType());
+    writer->header("Connection", keepConnectionAlive() ? "keep-alive" : "close");
+    writer->header("Last-Modified", now());
+    writer->header("Pragma", "no-cache");
+
+    auto headers = getAdditionalHeaders();
+
+    if (headers.find("Cache-Control") == headers.end()) {
+        writer->header("Cache-Control", "no-store");
+    }
+
+    if (headers.find("Expires") == headers.end()) {
+        writer->header("Expires", now());
+    }
+
+    for (auto& header : headers) {
+        writer->header(header.first, header.second);
+    }
+
+    writer->payload(payload(), payloadSize());
+
+    writer->finish(keepConnectionAlive());
+}
+
+void SynchronousResponse::cancel() {
+}
diff --git a/src/main/c/seasocks/SynchronousResponse.h b/src/main/c/seasocks/SynchronousResponse.h
new file mode 100644
index 0000000..c145a48
--- /dev/null
+++ b/src/main/c/seasocks/SynchronousResponse.h
@@ -0,0 +1,52 @@
+// Copyright (c) 2013-2017, 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/Response.h"
+#include "seasocks/ResponseWriter.h"
+
+namespace seasocks {
+
+class SynchronousResponse : public Response {
+public:
+    virtual ~SynchronousResponse() = default;
+    virtual void handle(std::shared_ptr<ResponseWriter> writer) override;
+    virtual void cancel() override;
+
+    virtual ResponseCode responseCode() const = 0;
+
+    virtual const char* payload() const = 0;
+    virtual size_t payloadSize() const = 0;
+
+    virtual std::string contentType() const = 0;
+
+    virtual bool keepConnectionAlive() const = 0;
+
+    typedef std::multimap<std::string, std::string> Headers;
+    virtual Headers getAdditionalHeaders() const = 0;
+};
+
+}
\ No newline at end of file
diff --git a/src/main/c/seasocks/ToString.h b/src/main/c/seasocks/ToString.h
index 40f607e..2ccb747 100644
--- a/src/main/c/seasocks/ToString.h
+++ b/src/main/c/seasocks/ToString.h
@@ -1,40 +1,59 @@
-// Copyright (c) 2013, Matt Godbolt
+// Copyright (c) 2013-2017, Matt Godbolt
 // All rights reserved.
-// 
-// Redistribution and use in source and binary forms, with or without 
+//
+// 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 
+//
+// 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 
+//
+// 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 
+//
+// 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 <sstream>
 #include <string>
+#include <type_traits>
 
 namespace seasocks {
 
-template<typename T>
+template <typename T, typename std::enable_if<!std::is_integral<typename std::decay<T>::type>::value, int>::type = 0>
 std::string toString(const T& obj) {
     std::stringstream str;
+    str.imbue(std::locale("C"));
     str << obj;
     return str.str();
 }
 
+template <typename T, typename std::enable_if<std::is_integral<typename std::decay<T>::type>::value, int>::type = 0>
+inline std::string toString(T&& value) {
+    return std::to_string(std::forward<T>(value));
+}
+
+inline std::string toString(const char* str) {
+    return str;
+}
+
+inline std::string toString(const std::string& str) {
+    return str;
+}
+
+inline std::string toString(char c) {
+    return std::string(1, c);
+}
+
 }
diff --git a/src/main/c/internal/Version.h b/src/main/c/seasocks/TransferEncoding.h
similarity index 73%
rename from src/main/c/internal/Version.h
rename to src/main/c/seasocks/TransferEncoding.h
index 366f8ea..40b8abb 100644
--- a/src/main/c/internal/Version.h
+++ b/src/main/c/seasocks/TransferEncoding.h
@@ -1,31 +1,31 @@
-// Copyright (c) 2013, Matt Godbolt
+// Copyright (c) 2013-2017, Matt Godbolt
 // All rights reserved.
-// 
-// Redistribution and use in source and binary forms, with or without 
+//
+// 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 
+//
+// 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 
+//
+// 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 
+//
+// 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
 
-#ifndef SEASOCKS_VERSION_STRING
-// This stops Eclipse freaking out as it doesn't know this is set on GCC command line.
-#define SEASOCKS_VERSION_STRING "SeaSocks/unversioned"
-#endif
\ No newline at end of file
+enum class TransferEncoding {
+    Raw,
+    Chunked
+};
diff --git a/src/main/c/seasocks/WebSocket.h b/src/main/c/seasocks/WebSocket.h
index f52509a..ef4e7e9 100644
--- a/src/main/c/seasocks/WebSocket.h
+++ b/src/main/c/seasocks/WebSocket.h
@@ -1,32 +1,33 @@
-// Copyright (c) 2013, Matt Godbolt
+// Copyright (c) 2013-2017, Matt Godbolt
 // All rights reserved.
-// 
-// Redistribution and use in source and binary forms, with or without 
+//
+// 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 
+//
+// 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 
+//
+// 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 
+//
+// 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/Request.h"
 
+#include <string>
 #include <vector>
 
 namespace seasocks {
@@ -40,6 +41,14 @@
      */
     virtual void send(const char* data) = 0;
     /**
+     * Send the given text data. Must be called on the seasocks thread.
+     * See Server::execute for how to run work on the seasocks
+     * thread externally.
+     */
+    void send(const std::string& data) {
+        send(data.c_str());
+    }
+    /**
      * Send the given binary data. Must be called on the seasocks thread.
      * See Server::execute for how to run work on the seasocks
      * thread externally.
@@ -57,7 +66,7 @@
      */
     class Handler {
     public:
-        virtual ~Handler() { }
+        virtual ~Handler() = default;
 
         /**
          * Called on the seasocks thread during initial connection.
@@ -66,21 +75,30 @@
         /**
          * Called on the seasocks thread with upon receipt of a full text WebSocket message.
          */
-        virtual void onData(WebSocket* connection, const char* data) {}
+        virtual void onData(WebSocket*, const char*) {
+        }
         /**
          * Called on the seasocks thread with upon receipt of a full binary WebSocket message.
          */
-        virtual void onData(WebSocket* connection, const uint8_t* data, size_t length) {}
+        virtual void onData(WebSocket*, const uint8_t*, size_t) {
+        }
         /**
          * Called on the seasocks thread when the socket has been
          */
         virtual void onDisconnect(WebSocket* connection) = 0;
+        /**
+         * Choose a protocol before accepting a connection: return < 0 to reject the connection, else return the ordinal
+         * in the vector of string protocols.
+         */
+        virtual ssize_t chooseProtocol(const std::vector<std::string>&) const {
+            return 0;
+        }
     };
 
 protected:
     // To delete a WebSocket, just close it. It is owned by the Server, and
     // the server will delete it when it's finished.
-    virtual ~WebSocket() {}
+    virtual ~WebSocket() = default;
 };
 
-}  // namespace seasocks
+} // namespace seasocks
diff --git a/src/main/c/seasocks/ZlibContext.cpp b/src/main/c/seasocks/ZlibContext.cpp
new file mode 100644
index 0000000..8933d4d
--- /dev/null
+++ b/src/main/c/seasocks/ZlibContext.cpp
@@ -0,0 +1,144 @@
+// Copyright (c) 2013-2017, 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/ZlibContext.h"
+
+#include <memory>
+#include <stdexcept>
+
+#include <zlib.h>
+
+namespace seasocks {
+
+struct ZlibContext::Impl {
+    z_stream deflateStream;
+    z_stream inflateStream;
+    bool streamsInitialised = false;
+    uint8_t buffer[16384];
+
+    Impl(int deflateBits, int inflateBits, int memLevel) {
+        int ret;
+
+        deflateStream.zalloc = Z_NULL;
+        deflateStream.zfree = Z_NULL;
+        deflateStream.opaque = Z_NULL;
+
+        ret = ::deflateInit2(
+            &deflateStream,
+            Z_DEFAULT_COMPRESSION,
+            Z_DEFLATED,
+            deflateBits * -1,
+            memLevel,
+            Z_DEFAULT_STRATEGY);
+
+        if (ret != Z_OK) {
+            throw std::runtime_error("error initialising zlib deflater");
+        }
+
+        inflateStream.zalloc = Z_NULL;
+        inflateStream.zfree = Z_NULL;
+        inflateStream.opaque = Z_NULL;
+        inflateStream.avail_in = 0;
+        inflateStream.next_in = Z_NULL;
+
+        ret = ::inflateInit2(
+            &inflateStream,
+            inflateBits * -1);
+
+        if (ret != Z_OK) {
+            ::deflateEnd(&deflateStream);
+            throw std::runtime_error("error initialising zlib inflater");
+        }
+
+        streamsInitialised = true;
+    }
+
+    ~Impl() {
+        if (!streamsInitialised)
+            return;
+        ::deflateEnd(&deflateStream);
+        ::inflateEnd(&inflateStream);
+    }
+
+    void deflate(const uint8_t* input, size_t inputLen, std::vector<uint8_t>& output) {
+        deflateStream.next_in = (unsigned char*) input;
+        deflateStream.avail_in = inputLen;
+
+        do {
+            deflateStream.next_out = buffer;
+            deflateStream.avail_out = sizeof(buffer);
+
+            (void) ::deflate(&deflateStream, Z_SYNC_FLUSH);
+
+            output.insert(output.end(), buffer, buffer + sizeof(buffer) - deflateStream.avail_out);
+        } while (deflateStream.avail_out == 0);
+
+        // Remove 4-byte tail end prior to transmission (see RFC 7692, section 7.2.1)
+        output.resize(output.size() - 4);
+    }
+
+    bool inflate(std::vector<uint8_t>& input, std::vector<uint8_t>& output, int& zlibError) {
+        // Append 4 octets prior to decompression (see RFC 7692, section 7.2.2)
+        uint8_t tail_end[4] = {0x00, 0x00, 0xff, 0xff};
+        input.insert(input.end(), tail_end, tail_end + 4);
+
+        inflateStream.next_in = input.data();
+        inflateStream.avail_in = input.size();
+
+        do {
+            inflateStream.next_out = buffer;
+            inflateStream.avail_out = sizeof(buffer);
+
+            int ret = ::inflate(&inflateStream, Z_SYNC_FLUSH);
+
+            if (ret != Z_OK && ret != Z_STREAM_END) {
+                zlibError = ret;
+                return false;
+            }
+
+            output.insert(output.end(), buffer, buffer + sizeof(buffer) - inflateStream.avail_out);
+        } while (inflateStream.avail_out == 0);
+
+        return true;
+    }
+};
+
+ZlibContext::ZlibContext() = default;
+
+ZlibContext::~ZlibContext() = default;
+
+void ZlibContext::initialise(int deflateBits, int inflateBits, int memLevel) {
+    _impl = std::make_unique<Impl>(deflateBits, inflateBits, memLevel);
+}
+
+void ZlibContext::deflate(const uint8_t* input, size_t inputLen, std::vector<uint8_t>& output) {
+    return _impl->deflate(input, inputLen, output);
+}
+
+bool ZlibContext::inflate(std::vector<uint8_t>& input, std::vector<uint8_t>& output, int& zlibError) {
+    return _impl->inflate(input, output, zlibError);
+}
+
+}
\ No newline at end of file
diff --git a/src/main/c/seasocks/ZlibContext.h b/src/main/c/seasocks/ZlibContext.h
new file mode 100644
index 0000000..6463dc9
--- /dev/null
+++ b/src/main/c/seasocks/ZlibContext.h
@@ -0,0 +1,29 @@
+#pragma once
+
+#include <cstdint>
+#include <cstdlib>
+#include <memory>
+#include <vector>
+
+namespace seasocks {
+
+class ZlibContext {
+public:
+    ZlibContext(const ZlibContext&) = delete;
+    ZlibContext& operator=(const ZlibContext&) = delete;
+
+    ZlibContext();
+    ~ZlibContext();
+    void initialise(int deflateBits = 15, int inflateBits = 15, int memLevel = 6);
+
+    void deflate(const uint8_t* input, size_t inputLen, std::vector<uint8_t>& output);
+
+    // WARNING: inflate() alters input
+    bool inflate(std::vector<uint8_t>& input, std::vector<uint8_t>& output, int& zlibError);
+
+private:
+    struct Impl;
+    std::unique_ptr<Impl> _impl;
+};
+
+} // namespace seasocks
diff --git a/src/main/c/seasocks/ZlibContextDisabled.cpp b/src/main/c/seasocks/ZlibContextDisabled.cpp
new file mode 100644
index 0000000..744d024
--- /dev/null
+++ b/src/main/c/seasocks/ZlibContextDisabled.cpp
@@ -0,0 +1,53 @@
+// Copyright (c) 2013-2017, 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/ZlibContext.h"
+
+#include <stdexcept>
+
+namespace seasocks {
+
+struct ZlibContext::Impl {
+};
+
+ZlibContext::ZlibContext() {
+}
+
+ZlibContext::~ZlibContext() {
+}
+
+void ZlibContext::initialise(int, int, int) {
+    throw std::runtime_error("Not compiled with zlib support");
+}
+
+void ZlibContext::deflate(const uint8_t*, size_t, std::vector<uint8_t>&) {
+    throw std::runtime_error("Not compiled with zlib support");
+}
+
+bool ZlibContext::inflate(std::vector<uint8_t>&, std::vector<uint8_t>&, int&) {
+    throw std::runtime_error("Not compiled with zlib support");
+}
+
+}
\ No newline at end of file
diff --git a/src/main/c/seasocks/util/CrackedUri.h b/src/main/c/seasocks/util/CrackedUri.h
index 681ea42..85982c9 100644
--- a/src/main/c/seasocks/util/CrackedUri.h
+++ b/src/main/c/seasocks/util/CrackedUri.h
@@ -1,26 +1,26 @@
-// Copyright (c) 2013, Matt Godbolt
+// Copyright (c) 2013-2017, Matt Godbolt
 // All rights reserved.
-// 
-// Redistribution and use in source and binary forms, with or without 
+//
+// 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 
+//
+// 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 
+//
+// 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 
+//
+// 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
@@ -36,10 +36,14 @@
     std::unordered_multimap<std::string, std::string> _queryParams;
 
 public:
-    CrackedUri(const std::string& uri);
+    explicit CrackedUri(const std::string& uri);
 
-    const std::vector<std::string>& path() const { return _path; }
-    const std::unordered_multimap<std::string, std::string> queryParams() const { return _queryParams; }
+    const std::vector<std::string>& path() const {
+        return _path;
+    }
+    const std::unordered_multimap<std::string, std::string> queryParams() const {
+        return _queryParams;
+    }
 
     bool hasParam(const std::string& param) const;
 
diff --git a/src/main/c/seasocks/util/CrackedUriPageHandler.h b/src/main/c/seasocks/util/CrackedUriPageHandler.h
index cf8c362..4d6b32d 100644
--- a/src/main/c/seasocks/util/CrackedUriPageHandler.h
+++ b/src/main/c/seasocks/util/CrackedUriPageHandler.h
@@ -1,26 +1,26 @@
-// Copyright (c) 2013, Matt Godbolt
+// Copyright (c) 2013-2017, Matt Godbolt
 // All rights reserved.
-// 
-// Redistribution and use in source and binary forms, with or without 
+//
+// 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 
+//
+// 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 
+//
+// 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 
+//
+// 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
@@ -35,11 +35,11 @@
 
 class CrackedUriPageHandler {
 public:
-    virtual ~CrackedUriPageHandler() {}
+    virtual ~CrackedUriPageHandler() = default;
 
     virtual std::shared_ptr<Response> handle(const CrackedUri& uri, const Request& request) = 0;
 
-    typedef std::shared_ptr<CrackedUriPageHandler> Ptr;
+    using Ptr = std::shared_ptr<CrackedUriPageHandler>;
 };
 
 }
diff --git a/src/main/c/seasocks/util/Html.h b/src/main/c/seasocks/util/Html.h
index 6b3e887..b74beb8 100644
--- a/src/main/c/seasocks/util/Html.h
+++ b/src/main/c/seasocks/util/Html.h
@@ -1,26 +1,26 @@
-// Copyright (c) 2013, Matt Godbolt
+// Copyright (c) 2013-2017, Matt Godbolt
 // All rights reserved.
-// 
-// Redistribution and use in source and binary forms, with or without 
+//
+// 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 
+//
+// 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 
+//
+// 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 
+//
+// 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
@@ -47,30 +47,33 @@
     void append() {
     }
 
-    template<typename T, typename... Args>
-    void append(const T& t, Args&& ...args) {
+    template <typename T, typename... Args>
+    void append(const T& t, Args&&... args) {
         operator<<(t);
         append(std::forward<Args>(args)...);
     }
 
-    Element() : _isTextNode(true), _needsClose(false) {}
+    Element()
+            : _isTextNode(true), _needsClose(false) {
+    }
+
 public:
-    template<typename... Args>
-    Element(const std::string& name, bool needsClose, Args&&... args) :
-        _isTextNode(false),
-        _needsClose(needsClose),
-        _nameOrText(name) {
+    template <typename... Args>
+    Element(const std::string& name, bool needsClose, Args&&... args)
+            : _isTextNode(false),
+              _needsClose(needsClose),
+              _nameOrText(name) {
         append(std::forward<Args>(args)...);
     }
 
-    template<typename T>
+    template <typename T>
     static Element textElement(const T& text) {
         Element e;
         e._nameOrText = toString(text);
         return e;
     }
 
-    template<typename T>
+    template <typename T>
     Element& addAttribute(const char* attr, const T& value) {
         _attributes += std::string(" ") + attr + "=\"" + toString(value) + "\"";
         return *this;
@@ -100,39 +103,39 @@
         return addAttribute("id", id);
     }
 
-    Element& operator <<(const char* child) {
+    Element& operator<<(const char* child) {
         _children.push_back(textElement(child));
         return *this;
     }
 
-    Element& operator <<(const std::string& child) {
+    Element& operator<<(const std::string& child) {
         _children.push_back(textElement(child));
         return *this;
     }
 
-    Element& operator <<(const Element& child) {
+    Element& operator<<(const Element& child) {
         _children.push_back(child);
         return *this;
     }
 
-    template<class T>
+    template <class T>
     typename std::enable_if<std::is_integral<T>::value || std::is_floating_point<T>::value, Element&>::type
-    operator <<(const T& child) {
+    operator<<(const T& child) {
         _children.push_back(textElement(child));
         return *this;
     }
 
-    friend std::ostream& operator << (std::ostream& os, const Element& elem) {
+    friend std::ostream& operator<<(std::ostream& os, const Element& elem) {
         if (elem._isTextNode) {
             os << elem._nameOrText;
-            for (auto it = elem._children.cbegin(); it != elem._children.cend(); ++it) {
-                os << *it;
+            for (const auto& it : elem._children) {
+                os << it;
             }
             return os;
         }
         os << "<" << elem._nameOrText << elem._attributes << ">";
-        for (auto it = elem._children.cbegin(); it != elem._children.cend(); ++it) {
-            os << *it;
+        for (const auto& it : elem._children) {
+            os << it;
         }
         if (elem._needsClose) {
             os << "</" << elem._nameOrText << ">";
@@ -140,7 +143,7 @@
         return os;
     }
 
-    template<typename C, typename F>
+    template <typename C, typename F>
     Element& addAll(const C& container, F functor) {
         for (auto it = container.cbegin(); it != container.cend(); ++it) {
             operator<<(functor(*it));
@@ -155,7 +158,11 @@
     }
 };
 
-#define HTMLELEM(XX) template<typename... Args> inline Element XX(Args&&... args) { return Element(#XX, true, std::forward<Args>(args)...); }
+#define HTMLELEM(XX)                                            \
+    template <typename... Args>                                 \
+    inline Element XX(Args&&... args) {                         \
+        return Element(#XX, true, std::forward<Args>(args)...); \
+    }
 HTMLELEM(html)
 HTMLELEM(head)
 HTMLELEM(title)
@@ -180,27 +187,40 @@
 HTMLELEM(button)
 #undef HTMLELEM
 
-inline Element empty() { return Element::textElement(""); }
+inline Element empty() {
+    return Element::textElement("");
+}
 
-template<typename T>
-inline Element text(const T& t) { return Element::textElement(t); }
+template <typename T>
+inline Element text(const T& t) {
+    return Element::textElement(t);
+}
 
-inline Element img(const std::string& src) { return Element("img", false).addAttribute("src", src); }
+inline Element img(const std::string& src) {
+    return Element("img", false).addAttribute("src", src);
+}
 
-inline Element checkbox() { return Element("input", false).addAttribute("type", "checkbox"); }
+inline Element checkbox() {
+    return Element("input", false).addAttribute("type", "checkbox");
+}
 
-inline Element externalScript(const std::string& src) { return Element("script", true).addAttribute("src", src).addAttribute("type", "text/javascript"); }
+inline Element externalScript(const std::string& src) {
+    return Element("script", true).addAttribute("src", src).addAttribute("type", "text/javascript");
+}
 
-inline Element inlineScript(const std::string& script) { return Element("script", true, script).addAttribute("type", "text/javascript"); }
+inline Element inlineScript(const std::string& script) {
+    return Element("script", true, script).addAttribute("type", "text/javascript");
+}
 
 inline Element link(const std::string& href, const std::string& rel) {
     return Element("link", false).addAttribute("href", href).addAttribute("rel", rel);
 }
 
-template<typename...Args> inline Element a(const std::string& href, Args&&... args) {
+template <typename... Args>
+inline Element a(const std::string& href, Args&&... args) {
     return Element("a", true, std::forward<Args>(args)...).addAttribute("href", href);
 }
 
-}  // namespace html
+} // namespace html
 
-}  // namespace seasocks
+} // namespace seasocks
diff --git a/src/main/c/seasocks/util/Json.h b/src/main/c/seasocks/util/Json.h
index c8a69dc..812e2d1 100644
--- a/src/main/c/seasocks/util/Json.h
+++ b/src/main/c/seasocks/util/Json.h
@@ -1,26 +1,26 @@
-// Copyright (c) 2013, Matt Godbolt
+// Copyright (c) 2013-2017, Matt Godbolt
 // All rights reserved.
-// 
-// Redistribution and use in source and binary forms, with or without 
+//
+// 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 
+//
+// 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 
+//
+// 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 
+//
+// 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
@@ -38,7 +38,8 @@
 
 ///////////////////////////////////
 
-inline void jsonToStream(std::ostream& str) {}
+inline void jsonToStream(std::ostream& /*str*/) {
+}
 
 void jsonToStream(std::ostream& str, const char* t);
 
@@ -48,54 +49,52 @@
     jsonToStream(str, t.c_str());
 }
 
-template<typename O>
+template <typename O>
 class is_jsonable {
-    template<typename OO>
+    template <typename OO>
     static auto test(int)
-    -> decltype(&OO::jsonToStream, std::true_type());
+        -> decltype(&OO::jsonToStream, std::true_type());
 
-    template<typename>
+    template <typename>
     static auto test(...) -> std::false_type;
 
 public:
     static constexpr bool value = decltype(test<O>(0))::value;
 };
 
-template<typename T>
+template <typename T>
 class is_streamable {
-    template<typename TT>
+    template <typename TT>
     static auto test(int)
-    -> decltype(std::declval<std::ostream&>() << std::declval<TT>(), std::true_type());
+        -> decltype(std::declval<std::ostream&>() << std::declval<TT>(), std::true_type());
 
-    template<typename>
+    template <typename>
     static auto test(...) -> std::false_type;
 
 public:
     static constexpr bool value = decltype(test<T>(0))::value;
 };
 
-template<typename T>
+template <typename T>
 typename std::enable_if<std::is_fundamental<T>::value, void>::type
 jsonToStream(std::ostream& str, const T& t) {
     str << t;
 }
 
-template<typename T>
+template <typename T>
 typename std::enable_if<is_jsonable<T>::value, void>::type
 jsonToStream(std::ostream& str, const T& t) {
     t.jsonToStream(str);
 }
 
-template<typename T>
+template <typename T>
 typename std::enable_if<
-    !std::is_fundamental<T>::value
-    && is_streamable<T>::value
-    && !is_jsonable<T>::value, void>::type
+    !std::is_fundamental<T>::value && is_streamable<T>::value && !is_jsonable<T>::value, void>::type
 jsonToStream(std::ostream& str, const T& t) {
     str << '"' << t << '"';
 }
 
-template<typename T, typename ... Args>
+template <typename T, typename... Args>
 void jsonToStream(std::ostream& str, const T& t, Args&&... args) {
     static_assert(sizeof...(Args) > 0, "Cannot stream an object with no jsonToStream or operator<< method.");
     jsonToStream(str, t);
@@ -105,27 +104,28 @@
 
 ///////////////////////////////////
 
-inline void jsonKeyPairToStream(std::ostream& str) {}
+inline void jsonKeyPairToStream(std::ostream& /*str*/) {
+}
 
-template<typename T>
+template <typename T>
 void jsonKeyPairToStream(std::ostream& str, const char* key, const T& value) {
     jsonToStream(str, key);
     str << ":";
     jsonToStream(str, value);
 }
 
-template<typename T>
+template <typename T>
 void jsonKeyPairToStream(std::ostream& str, const std::string& key, const T& value) {
     jsonKeyPairToStream(str, key.c_str(), value);
 }
 
-template<typename T>
-void jsonKeyPairToStream(std::ostream& str, const T&) {
-    static_assert(!std::is_same<T, T>::value,  // To make the assertion depend on T
-            "Requires an even number of parameters. If you're trying to build a map from an existing std::map or similar, use makeMapFromContainer");
+template <typename T>
+void jsonKeyPairToStream(std::ostream&, const T&) {
+    static_assert(!std::is_same<T, T>::value, // To make the assertion depend on T
+                  "Requires an even number of parameters. If you're trying to build a map from an existing std::map or similar, use makeMapFromContainer");
 }
 
-template<typename K, typename V, typename... Args>
+template <typename K, typename V, typename... Args>
 void jsonKeyPairToStream(std::ostream& str, const K& key, const V& value, Args&&... args) {
     jsonKeyPairToStream(str, key, value);
     str << ",";
@@ -133,10 +133,14 @@
 }
 
 struct JsonnedString : std::string {
-    JsonnedString() {}
-    JsonnedString(const std::string& s) : std::string(s) {}
-    JsonnedString(const std::stringstream& str) : std::string(str.str()) {}
-    void jsonToStream(std::ostream &o) const {
+    JsonnedString() = default;
+    JsonnedString(const std::string& s)
+            : std::string(s) {
+    }
+    JsonnedString(const std::stringstream& str)
+            : std::string(str.str()) {
+    }
+    void jsonToStream(std::ostream& o) const {
         o << *this;
     }
 };
@@ -145,12 +149,14 @@
 
 struct EpochTimeAsLocal {
     time_t t;
-    EpochTimeAsLocal(time_t t) : t(t) {}
-    void jsonToStream(std::ostream &o) const;
+    EpochTimeAsLocal(time_t time)
+            : t(time) {
+    }
+    void jsonToStream(std::ostream& o) const;
 };
 static_assert(is_jsonable<EpochTimeAsLocal>::value, "Internal stream problem");
 
-template<typename... Args>
+template <typename... Args>
 JsonnedString makeMap(Args&&... args) {
     std::stringstream str;
     str << '{';
@@ -159,13 +165,14 @@
     return JsonnedString(str);
 }
 
-template<typename T>
+template <typename T>
 JsonnedString makeMapFromContainer(const T& m) {
     std::stringstream str;
     str << "{";
     bool first = true;
-    for (const auto &it : m) {
-        if (!first) str << ",";
+    for (const auto& it : m) {
+        if (!first)
+            str << ",";
         first = false;
         jsonKeyPairToStream(str, it.first, it.second);
     }
@@ -173,7 +180,7 @@
     return JsonnedString(str);
 }
 
-template<typename ... Args>
+template <typename... Args>
 JsonnedString makeArray(Args&&... args) {
     std::stringstream str;
     str << '[';
@@ -182,12 +189,12 @@
     return JsonnedString(str);
 }
 
-template<typename T>
-JsonnedString makeArrayFromContainer(const T &list) {
+template <typename T>
+JsonnedString makeArrayFromContainer(const T& list) {
     std::stringstream str;
     str << '[';
     bool first = true;
-    for (const auto &s : list) {
+    for (const auto& s : list) {
         if (!first) {
             str << ',';
         }
@@ -198,12 +205,12 @@
     return JsonnedString(str);
 }
 
-template<typename T>
-JsonnedString makeArray(const std::initializer_list<T> &list) {
+template <typename T>
+JsonnedString makeArray(const std::initializer_list<T>& list) {
     std::stringstream str;
     str << '[';
     bool first = true;
-    for (const auto &s : list) {
+    for (const auto& s : list) {
         if (!first) {
             str << ',';
         }
@@ -214,7 +221,7 @@
     return JsonnedString(str);
 }
 
-template<typename ... Args>
+template <typename... Args>
 JsonnedString makeExecString(const char* function, Args&&... args) {
     std::stringstream str;
     str << function << '(';
@@ -223,8 +230,8 @@
     return JsonnedString(str);
 }
 
-template<typename T>
-JsonnedString to_json(const T &obj) {
+template <typename T>
+JsonnedString to_json(const T& obj) {
     std::stringstream str;
     jsonToStream(str, obj);
     return str.str();
diff --git a/src/main/c/seasocks/util/PathHandler.h b/src/main/c/seasocks/util/PathHandler.h
index 275f829..054e544 100644
--- a/src/main/c/seasocks/util/PathHandler.h
+++ b/src/main/c/seasocks/util/PathHandler.h
@@ -1,26 +1,26 @@
-// Copyright (c) 2013, Matt Godbolt
+// Copyright (c) 2013-2017, Matt Godbolt
 // All rights reserved.
-// 
-// Redistribution and use in source and binary forms, with or without 
+//
+// 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 
+//
+// 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 
+//
+// 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 
+//
+// 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
@@ -45,24 +45,30 @@
     std::vector<CrackedUriPageHandler::Ptr> _handlers;
 
 public:
-    PathHandler(const std::string &path) : _path(path) {}
-    template<typename... Args>
-    PathHandler(const std::string &path, Args&&... args) : _path(path) {
+    PathHandler(const std::string& path)
+            : _path(path), _handlers() {
+    }
+    template <typename... Args>
+    PathHandler(const std::string& path, Args&&... args)
+            : _path(path), _handlers() {
         addMany(std::forward<Args>(args)...);
     }
 
     CrackedUriPageHandler::Ptr add(const CrackedUriPageHandler::Ptr& handler);
 
-    void addMany() {}
-    void addMany(const CrackedUriPageHandler::Ptr& handler) { add(handler); }
-    template<typename... Rest>
+    void addMany() {
+    }
+    void addMany(const CrackedUriPageHandler::Ptr& handler) {
+        add(handler);
+    }
+    template <typename... Rest>
     void addMany(const CrackedUriPageHandler::Ptr& handler, Rest&&... rest) {
         add(handler);
         addMany(std::forward<Rest>(rest)...);
     }
 
     virtual std::shared_ptr<Response> handle(
-            const CrackedUri& uri, const Request& request) override;
+        const CrackedUri& uri, const Request& request) override;
 };
 
 }
diff --git a/src/main/c/seasocks/util/RootPageHandler.h b/src/main/c/seasocks/util/RootPageHandler.h
index 2c2ce1a..7fa84c7 100644
--- a/src/main/c/seasocks/util/RootPageHandler.h
+++ b/src/main/c/seasocks/util/RootPageHandler.h
@@ -1,26 +1,26 @@
-// Copyright (c) 2013, Matt Godbolt
+// Copyright (c) 2013-2017, Matt Godbolt
 // All rights reserved.
-// 
-// Redistribution and use in source and binary forms, with or without 
+//
+// 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 
+//
+// 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 
+//
+// 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 
+//
+// 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
diff --git a/src/main/c/seasocks/util/StaticResponseHandler.h b/src/main/c/seasocks/util/StaticResponseHandler.h
index ff6c5e7..61ef847 100644
--- a/src/main/c/seasocks/util/StaticResponseHandler.h
+++ b/src/main/c/seasocks/util/StaticResponseHandler.h
@@ -1,26 +1,26 @@
-// Copyright (c) 2013, Matt Godbolt
+// Copyright (c) 2013-2017, Matt Godbolt
 // All rights reserved.
-// 
-// Redistribution and use in source and binary forms, with or without 
+//
+// 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 
+//
+// 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 
+//
+// 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 
+//
+// 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/CrackedUriPageHandler.h>
@@ -37,11 +37,12 @@
 
 public:
     StaticResponseHandler(const std::string& path, std::shared_ptr<Response> response)
-    : _path(path), _response(response) {}
+            : _path(path), _response(response) {
+    }
 
     virtual std::shared_ptr<Response> handle(
-            const CrackedUri& uri,
-            const Request&) override {
+        const CrackedUri& uri,
+        const Request&) override {
         if (uri.path().size() != 1 || uri.path()[0] != _path)
             return Response::unhandled();
         return _response;
diff --git a/src/main/c/sha1/sha1.cpp b/src/main/c/sha1/sha1.cpp
index 9a42766..33269c0 100644
--- a/src/main/c/sha1/sha1.cpp
+++ b/src/main/c/sha1/sha1.cpp
@@ -1,26 +1,26 @@
-// Copyright (c) 2013, Matt Godbolt
+// Copyright (c) 2013-2017, Matt Godbolt
 // All rights reserved.
-// 
-// Redistribution and use in source and binary forms, with or without 
+//
+// 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 
+//
+// 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 
+//
+// 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 
+//
+// 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.
 
 // DO NOT REFORMAT <- for tidy
@@ -66,7 +66,7 @@
 
 #include "sha1.h"
 
-/*  
+/*
  *  SHA1
  *
  *  Description:
@@ -81,32 +81,11 @@
  *  Comments:
  *
  */
-SHA1::SHA1()
-{
+SHA1::SHA1() {
     Reset();
 }
 
-/*  
- *  ~SHA1
- *
- *  Description:
- *      This is the destructor for the sha1 class
- *
- *  Parameters:
- *      None.
- *
- *  Returns:
- *      Nothing.
- *
- *  Comments:
- *
- */
-SHA1::~SHA1()
-{
-    // The destructor does nothing
-}
-
-/*  
+/*
  *  Reset
  *
  *  Description:
@@ -122,23 +101,22 @@
  *  Comments:
  *
  */
-void SHA1::Reset()
-{
-    Length_Low          = 0;
-    Length_High         = 0;
+void SHA1::Reset() {
+    Length_Low = 0;
+    Length_High = 0;
     Message_Block_Index = 0;
 
-    H[0]        = 0x67452301;
-    H[1]        = 0xEFCDAB89;
-    H[2]        = 0x98BADCFE;
-    H[3]        = 0x10325476;
-    H[4]        = 0xC3D2E1F0;
+    H[0] = 0x67452301;
+    H[1] = 0xEFCDAB89;
+    H[2] = 0x98BADCFE;
+    H[3] = 0x10325476;
+    H[4] = 0xC3D2E1F0;
 
-    Computed    = false;
-    Corrupted   = false;
+    Computed = false;
+    Corrupted = false;
 }
 
-/*  
+/*
  *  Result
  *
  *  Description:
@@ -156,30 +134,26 @@
  *  Comments:
  *
  */
-bool SHA1::Result(unsigned *message_digest_array)
-{
-    int i;                                  // Counter
+bool SHA1::Result(unsigned* message_digest_array) {
+    int i; // Counter
 
-    if (Corrupted)
-    {
+    if (Corrupted) {
         return false;
     }
 
-    if (!Computed)
-    {
+    if (!Computed) {
         PadMessage();
         Computed = true;
     }
 
-    for(i = 0; i < 5; i++)
-    {
+    for (i = 0; i < 5; i++) {
         message_digest_array[i] = H[i];
     }
 
     return true;
 }
 
-/*  
+/*
  *  Input
  *
  *  Description:
@@ -197,38 +171,31 @@
  *  Comments:
  *
  */
-void SHA1::Input(   const unsigned char *message_array,
-                    unsigned            length)
-{
-    if (!length)
-    {
+void SHA1::Input(const unsigned char* message_array,
+                 unsigned length) {
+    if (!length) {
         return;
     }
 
-    if (Computed || Corrupted)
-    {
+    if (Computed || Corrupted) {
         Corrupted = true;
         return;
     }
 
-    while(length-- && !Corrupted)
-    {
+    while (length-- && !Corrupted) {
         Message_Block[Message_Block_Index++] = (*message_array & 0xFF);
 
         Length_Low += 8;
-        Length_Low &= 0xFFFFFFFF;               // Force it to 32 bits
-        if (Length_Low == 0)
-        {
+        Length_Low &= 0xFFFFFFFF; // Force it to 32 bits
+        if (Length_Low == 0) {
             Length_High++;
-            Length_High &= 0xFFFFFFFF;          // Force it to 32 bits
-            if (Length_High == 0)
-            {
-                Corrupted = true;               // Message is too long
+            Length_High &= 0xFFFFFFFF; // Force it to 32 bits
+            if (Length_High == 0) {
+                Corrupted = true; // Message is too long
             }
         }
 
-        if (Message_Block_Index == 64)
-        {
+        if (Message_Block_Index == 64) {
             ProcessMessageBlock();
         }
 
@@ -236,7 +203,7 @@
     }
 }
 
-/*  
+/*
  *  Input
  *
  *  Description:
@@ -256,13 +223,12 @@
  *  Comments:
  *
  */
-void SHA1::Input(   const char  *message_array,
-                    unsigned    length)
-{
-    Input((unsigned char *) message_array, length);
+void SHA1::Input(const char* message_array,
+                 unsigned length) {
+    Input((unsigned char*) message_array, length);
 }
 
-/*  
+/*
  *  Input
  *
  *  Description:
@@ -278,12 +244,11 @@
  *  Comments:
  *
  */
-void SHA1::Input(unsigned char message_element)
-{
+void SHA1::Input(unsigned char message_element) {
     Input(&message_element, 1);
 }
 
-/*  
+/*
  *  Input
  *
  *  Description:
@@ -299,12 +264,11 @@
  *  Comments:
  *
  */
-void SHA1::Input(char message_element)
-{
-    Input((unsigned char *) &message_element, 1);
+void SHA1::Input(char message_element) {
+    Input((unsigned char*) &message_element, 1);
 }
 
-/*  
+/*
  *  operator<<
  *
  *  Description:
@@ -322,12 +286,10 @@
  *      Each character is assumed to hold 8 bits of information.
  *
  */
-SHA1& SHA1::operator<<(const char *message_array)
-{
-    const char *p = message_array;
+SHA1& SHA1::operator<<(const char* message_array) {
+    const char* p = message_array;
 
-    while(*p)
-    {
+    while (*p) {
         Input(*p);
         p++;
     }
@@ -335,7 +297,7 @@
     return *this;
 }
 
-/*  
+/*
  *  operator<<
  *
  *  Description:
@@ -353,12 +315,10 @@
  *      Each character is assumed to hold 8 bits of information.
  *
  */
-SHA1& SHA1::operator<<(const unsigned char *message_array)
-{
-    const unsigned char *p = message_array;
+SHA1& SHA1::operator<<(const unsigned char* message_array) {
+    const unsigned char* p = message_array;
 
-    while(*p)
-    {
+    while (*p) {
         Input(*p);
         p++;
     }
@@ -366,7 +326,7 @@
     return *this;
 }
 
-/*  
+/*
  *  operator<<
  *
  *  Description:
@@ -383,14 +343,13 @@
  *      The character is assumed to hold 8 bits of information.
  *
  */
-SHA1& SHA1::operator<<(const char message_element)
-{
-    Input((unsigned char *) &message_element, 1);
+SHA1& SHA1::operator<<(const char message_element) {
+    Input((unsigned char*) &message_element, 1);
 
     return *this;
 }
 
-/*  
+/*
  *  operator<<
  *
  *  Description:
@@ -407,14 +366,13 @@
  *      The character is assumed to hold 8 bits of information.
  *
  */
-SHA1& SHA1::operator<<(const unsigned char message_element)
-{
+SHA1& SHA1::operator<<(const unsigned char message_element) {
     Input(&message_element, 1);
 
     return *this;
 }
 
-/*  
+/*
  *  ProcessMessageBlock
  *
  *  Description:
@@ -433,33 +391,29 @@
  *      in the publication.
  *
  */
-void SHA1::ProcessMessageBlock()
-{
-    const unsigned K[] =    {               // Constants defined for SHA-1
-                                0x5A827999,
-                                0x6ED9EBA1,
-                                0x8F1BBCDC,
-                                0xCA62C1D6
-                            };
-    int         t;                          // Loop counter
-    unsigned    temp;                       // Temporary word value
-    unsigned    W[80];                      // Word sequence
-    unsigned    A, B, C, D, E;              // Word buffers
+void SHA1::ProcessMessageBlock() {
+    const unsigned K[] = {// Constants defined for SHA-1
+                          0x5A827999,
+                          0x6ED9EBA1,
+                          0x8F1BBCDC,
+                          0xCA62C1D6};
+    int t;                  // Loop counter
+    unsigned temp;          // Temporary word value
+    unsigned W[80];         // Word sequence
+    unsigned A, B, C, D, E; // Word buffers
 
     /*
      *  Initialize the first 16 words in the array W
      */
-    for(t = 0; t < 16; t++)
-    {
+    for (t = 0; t < 16; t++) {
         W[t] = ((unsigned) Message_Block[t * 4]) << 24;
         W[t] |= ((unsigned) Message_Block[t * 4 + 1]) << 16;
         W[t] |= ((unsigned) Message_Block[t * 4 + 2]) << 8;
         W[t] |= ((unsigned) Message_Block[t * 4 + 3]);
     }
 
-    for(t = 16; t < 80; t++)
-    {
-       W[t] = CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);
+    for (t = 16; t < 80; t++) {
+        W[t] = CircularShift(1, W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16]);
     }
 
     A = H[0];
@@ -468,47 +422,43 @@
     D = H[3];
     E = H[4];
 
-    for(t = 0; t < 20; t++)
-    {
-        temp = CircularShift(5,A) + ((B & C) | ((~B) & D)) + E + W[t] + K[0];
+    for (t = 0; t < 20; t++) {
+        temp = CircularShift(5, A) + ((B & C) | ((~B) & D)) + E + W[t] + K[0];
         temp &= 0xFFFFFFFF;
         E = D;
         D = C;
-        C = CircularShift(30,B);
+        C = CircularShift(30, B);
         B = A;
         A = temp;
     }
 
-    for(t = 20; t < 40; t++)
-    {
-        temp = CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1];
+    for (t = 20; t < 40; t++) {
+        temp = CircularShift(5, A) + (B ^ C ^ D) + E + W[t] + K[1];
         temp &= 0xFFFFFFFF;
         E = D;
         D = C;
-        C = CircularShift(30,B);
+        C = CircularShift(30, B);
         B = A;
         A = temp;
     }
 
-    for(t = 40; t < 60; t++)
-    {
-        temp = CircularShift(5,A) +
+    for (t = 40; t < 60; t++) {
+        temp = CircularShift(5, A) +
                ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2];
         temp &= 0xFFFFFFFF;
         E = D;
         D = C;
-        C = CircularShift(30,B);
+        C = CircularShift(30, B);
         B = A;
         A = temp;
     }
 
-    for(t = 60; t < 80; t++)
-    {
-        temp = CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3];
+    for (t = 60; t < 80; t++) {
+        temp = CircularShift(5, A) + (B ^ C ^ D) + E + W[t] + K[3];
         temp &= 0xFFFFFFFF;
         E = D;
         D = C;
-        C = CircularShift(30,B);
+        C = CircularShift(30, B);
         B = A;
         A = temp;
     }
@@ -522,7 +472,7 @@
     Message_Block_Index = 0;
 }
 
-/*  
+/*
  *  PadMessage
  *
  *  Description:
@@ -543,36 +493,28 @@
  *  Comments:
  *
  */
-void SHA1::PadMessage()
-{
+void SHA1::PadMessage() {
     /*
      *  Check to see if the current message block is too small to hold
      *  the initial padding bits and length.  If so, we will pad the
      *  block, process it, and then continue padding into a second block.
      */
-    if (Message_Block_Index > 55)
-    {
+    if (Message_Block_Index > 55) {
         Message_Block[Message_Block_Index++] = 0x80;
-        while(Message_Block_Index < 64)
-        {
+        while (Message_Block_Index < 64) {
             Message_Block[Message_Block_Index++] = 0;
         }
 
         ProcessMessageBlock();
 
-        while(Message_Block_Index < 56)
-        {
+        while (Message_Block_Index < 56) {
             Message_Block[Message_Block_Index++] = 0;
         }
-    }
-    else
-    {
+    } else {
         Message_Block[Message_Block_Index++] = 0x80;
-        while(Message_Block_Index < 56)
-        {
+        while (Message_Block_Index < 56) {
             Message_Block[Message_Block_Index++] = 0;
         }
-
     }
 
     /*
@@ -581,17 +523,17 @@
     Message_Block[56] = (Length_High >> 24) & 0xFF;
     Message_Block[57] = (Length_High >> 16) & 0xFF;
     Message_Block[58] = (Length_High >> 8) & 0xFF;
-    Message_Block[59] = (Length_High) & 0xFF;
+    Message_Block[59] = (Length_High) &0xFF;
     Message_Block[60] = (Length_Low >> 24) & 0xFF;
     Message_Block[61] = (Length_Low >> 16) & 0xFF;
     Message_Block[62] = (Length_Low >> 8) & 0xFF;
-    Message_Block[63] = (Length_Low) & 0xFF;
+    Message_Block[63] = (Length_Low) &0xFF;
 
     ProcessMessageBlock();
 }
 
 
-/*  
+/*
  *  CircularShift
  *
  *  Description:
@@ -609,7 +551,6 @@
  *  Comments:
  *
  */
-unsigned SHA1::CircularShift(int bits, unsigned word)
-{
-    return ((word << bits) & 0xFFFFFFFF) | ((word & 0xFFFFFFFF) >> (32-bits));
+unsigned SHA1::CircularShift(int bits, unsigned word) {
+    return ((word << bits) & 0xFFFFFFFF) | ((word & 0xFFFFFFFF) >> (32 - bits));
 }
diff --git a/src/main/c/sha1/sha1.h b/src/main/c/sha1/sha1.h
index 3c7d896..c9d6b29 100644
--- a/src/main/c/sha1/sha1.h
+++ b/src/main/c/sha1/sha1.h
@@ -1,26 +1,26 @@
-// Copyright (c) 2013, Matt Godbolt
+// Copyright (c) 2013-2017, Matt Godbolt
 // All rights reserved.
-// 
-// Redistribution and use in source and binary forms, with or without 
+//
+// 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 
+//
+// 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 
+//
+// 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 
+//
+// 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.
 
 // DO NOT REFORMAT <- for tidy
@@ -50,65 +50,60 @@
 #ifndef _SHA1_H_
 #define _SHA1_H_
 
-class SHA1
-{
+class SHA1 {
 
-    public:
+public:
+    SHA1();
 
-        SHA1();
-        virtual ~SHA1();
-
-        /*
+    /*
          *  Re-initialize the class
          */
-        void Reset();
+    void Reset();
 
-        /*
+    /*
          *  Returns the message digest
          */
-        bool Result(unsigned *message_digest_array);
+    bool Result(unsigned* message_digest_array);
 
-        /*
+    /*
          *  Provide input to SHA1
          */
-        void Input( const unsigned char *message_array,
-                    unsigned            length);
-        void Input( const char  *message_array,
-                    unsigned    length);
-        void Input(unsigned char message_element);
-        void Input(char message_element);
-        SHA1& operator<<(const char *message_array);
-        SHA1& operator<<(const unsigned char *message_array);
-        SHA1& operator<<(const char message_element);
-        SHA1& operator<<(const unsigned char message_element);
+    void Input(const unsigned char* message_array,
+               unsigned length);
+    void Input(const char* message_array,
+               unsigned length);
+    void Input(unsigned char message_element);
+    void Input(char message_element);
+    SHA1& operator<<(const char* message_array);
+    SHA1& operator<<(const unsigned char* message_array);
+    SHA1& operator<<(const char message_element);
+    SHA1& operator<<(const unsigned char message_element);
 
-    private:
-
-        /*
+private:
+    /*
          *  Process the next 512 bits of the message
          */
-        void ProcessMessageBlock();
+    void ProcessMessageBlock();
 
-        /*
+    /*
          *  Pads the current message block to 512 bits
          */
-        void PadMessage();
+    void PadMessage();
 
-        /*
+    /*
          *  Performs a circular left shift operation
          */
-        inline unsigned CircularShift(int bits, unsigned word);
+    inline unsigned CircularShift(int bits, unsigned word);
 
-        unsigned H[5];                      // Message digest buffers
+    unsigned H[5]; // Message digest buffers
 
-        unsigned Length_Low;                // Message length in bits
-        unsigned Length_High;               // Message length in bits
+    unsigned Length_Low;  // Message length in bits
+    unsigned Length_High; // Message length in bits
 
-        unsigned char Message_Block[64];    // 512-bit message blocks
-        int Message_Block_Index;            // Index into message block array
+    unsigned char Message_Block[64]; // 512-bit message blocks
+    int Message_Block_Index;         // Index into message block array
 
-        bool Computed;                      // Is the digest computed?
-        bool Corrupted;                     // Is the message digest corruped?
-
+    bool Computed;  // Is the digest computed?
+    bool Corrupted; // Is the message digest corruped?
 };
 #endif
diff --git a/src/main/c/util/CrackedUri.cpp b/src/main/c/util/CrackedUri.cpp
index 1da757f..7f0b242 100644
--- a/src/main/c/util/CrackedUri.cpp
+++ b/src/main/c/util/CrackedUri.cpp
@@ -1,26 +1,26 @@
-// Copyright (c) 2013, Matt Godbolt
+// Copyright (c) 2013-2017, Matt Godbolt
 // All rights reserved.
-// 
-// Redistribution and use in source and binary forms, with or without 
+//
+// 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 
+//
+// 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 
+//
+// 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 
+//
+// 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/StringUtil.h"
@@ -30,10 +30,10 @@
 #include <sstream>
 #include <stdexcept>
 
-#define THROW(stuff) \
-    do {\
-        std::ostringstream err; \
-        err << stuff; \
+#define THROW(stuff)                         \
+    do {                                     \
+        std::ostringstream err;              \
+        err << stuff;                        \
         throw std::runtime_error(err.str()); \
     } while (0);
 
@@ -49,7 +49,9 @@
     size_t pos = 0;
     while (pos < uri.size()) {
         pos = uri.find('%', pos);
-        if (pos == uri.npos) break;
+        if (pos == std::string::npos) {
+            break;
+        }
         if (pos + 2 > uri.size()) {
             THROW("Truncated uri: '" << uri << "'");
         }
@@ -74,7 +76,7 @@
     auto endOfPath = uri.find('?');
     std::string path;
     std::string remainder;
-    if (endOfPath == uri.npos) {
+    if (endOfPath == std::string::npos) {
         path = uri.substr(1);
     } else {
         path = uri.substr(1, endOfPath - 1);
@@ -85,9 +87,11 @@
     std::transform(_path.begin(), _path.end(), _path.begin(), unescape);
 
     auto splitRemainder = split(remainder, '&');
-    for (auto iter = splitRemainder.cbegin(); iter != splitRemainder.cend(); ++iter) {
-        if (iter->empty()) continue;
-        auto split = seasocks::split(*iter, '=');
+    for (const auto& iter : splitRemainder) {
+        if (iter.empty()) {
+            continue;
+        }
+        auto split = seasocks::split(iter, '=');
         std::transform(split.begin(), split.end(), split.begin(), unescape);
         if (split.size() == 1) {
             _queryParams.insert(std::make_pair(split[0], std::string()));
@@ -110,8 +114,9 @@
 
 std::vector<std::string> CrackedUri::allQueryParams(const std::string& param) const {
     std::vector<std::string> params;
-    for (auto iter = _queryParams.find(param); iter != _queryParams.end() && iter->first == param; ++iter)
+    for (auto iter = _queryParams.find(param); iter != _queryParams.end() && iter->first == param; ++iter) {
         params.push_back(iter->second);
+    }
     return params;
 }
 
diff --git a/src/main/c/util/Json.cpp b/src/main/c/util/Json.cpp
index 48c74d7..7fcf64e 100644
--- a/src/main/c/util/Json.cpp
+++ b/src/main/c/util/Json.cpp
@@ -1,26 +1,26 @@
-// Copyright (c) 2013, Matt Godbolt
+// Copyright (c) 2013-2017, Matt Godbolt
 // All rights reserved.
-// 
-// Redistribution and use in source and binary forms, with or without 
+//
+// 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 
+//
+// 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 
+//
+// 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 
+//
+// 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"
@@ -33,23 +33,34 @@
     str << '"';
     for (; *t; ++t) {
         switch (*t) {
-        default:
-            if (*t >= 32) {
+            default:
+                if (static_cast<unsigned char>(*t) >= 32) {
+                    str << *t;
+                } else {
+                    str << "\\u" << std::setw(4)
+                        << std::setfill('0') << std::hex << static_cast<int>(*t);
+                }
+                break;
+            case 8:
+                str << "\\b";
+                break;
+            case 9:
+                str << "\\t";
+                break;
+            case 10:
+                str << "\\n";
+                break;
+            case 12:
+                str << "\\f";
+                break;
+            case 13:
+                str << "\\r";
+                break;
+            case '"':
+            case '\\':
+                str << '\\';
                 str << *t;
-            } else {
-                str << "\\u" << std::setw(4)
-                    << std::setfill('0') << std::hex << (int)*t;
-            }
-            break;
-        case 8: str << "\\b"; break;
-        case 9: str << "\\t"; break;
-        case 10: str << "\\n"; break;
-        case 12: str << "\\f"; break;
-        case 13: str << "\\r"; break;
-        case '"': case '\\':
-            str << '\\';
-            str << *t;
-            break;
+                break;
         }
     }
     str << '"';
@@ -59,7 +70,7 @@
     str << (b ? "true" : "false");
 }
 
-void EpochTimeAsLocal::jsonToStream(std::ostream &o) const {
+void EpochTimeAsLocal::jsonToStream(std::ostream& o) const {
     o << "new Date(" << t * 1000 << ").toLocaleString()";
 }
 
diff --git a/src/main/c/util/PathHandler.cpp b/src/main/c/util/PathHandler.cpp
index 4f9b60f..75a0f14 100644
--- a/src/main/c/util/PathHandler.cpp
+++ b/src/main/c/util/PathHandler.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2013, Matt Godbolt
+// Copyright (c) 2013-2017, Matt Godbolt
 // All rights reserved.
 //
 // Redistribution and use in source and binary forms, with or without
@@ -37,15 +37,19 @@
 }
 
 std::shared_ptr<Response> PathHandler::handle(
-        const CrackedUri& uri, const Request& request) {
-    const auto &path = uri.path();
-    if (path.empty() || path[0] != _path) return Response::unhandled();
+    const CrackedUri& uri, const Request& request) {
+    const auto& path = uri.path();
+    if (path.empty() || path[0] != _path) {
+        return Response::unhandled();
+    }
 
     auto shiftedUri = uri.shift();
 
-    for (const auto &it : _handlers) {
+    for (const auto& it : _handlers) {
         auto response = it->handle(shiftedUri, request);
-        if (response != Response::unhandled()) return response;
+        if (response != Response::unhandled()) {
+            return response;
+        }
     }
     return Response::unhandled();
 }
diff --git a/src/main/c/util/RootPageHandler.cpp b/src/main/c/util/RootPageHandler.cpp
index f6b4f52..b639654 100644
--- a/src/main/c/util/RootPageHandler.cpp
+++ b/src/main/c/util/RootPageHandler.cpp
@@ -1,26 +1,26 @@
-// Copyright (c) 2013, Matt Godbolt
+// Copyright (c) 2013-2017, Matt Godbolt
 // All rights reserved.
-// 
-// Redistribution and use in source and binary forms, with or without 
+//
+// 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 
+//
+// 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 
+//
+// 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 
+//
+// 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/RootPageHandler.h"
@@ -31,11 +31,9 @@
 
 using namespace seasocks;
 
-RootPageHandler::RootPageHandler() {
-}
+RootPageHandler::RootPageHandler() = default;
 
-RootPageHandler::~RootPageHandler() {
-}
+RootPageHandler::~RootPageHandler() = default;
 
 CrackedUriPageHandler::Ptr RootPageHandler::add(const CrackedUriPageHandler::Ptr& handler) {
     _handlers.emplace_back(handler);
@@ -44,9 +42,11 @@
 
 std::shared_ptr<Response> RootPageHandler::handle(const Request& request) {
     CrackedUri uri(request.getRequestUri());
-    for (const auto &it : _handlers) {
+    for (const auto& it : _handlers) {
         auto response = it->handle(uri, request);
-        if (response != Response::unhandled()) return response;
+        if (response != Response::unhandled()) {
+            return response;
+        }
     }
     return Response::unhandled();
 }