Squashed 'third_party/seasocks/' content from commit 016dc60
Change-Id: I195fa5bfd0c0e3cc66fbbefcc7b5170bafcf7a36
git-subtree-dir: third_party/seasocks
git-subtree-split: 016dc60b247e0d1d563aea6d22a9075e6884ab9f
diff --git a/src/main/c/seasocks/Connection.h b/src/main/c/seasocks/Connection.h
new file mode 100644
index 0000000..c1aa996
--- /dev/null
+++ b/src/main/c/seasocks/Connection.h
@@ -0,0 +1,177 @@
+// Copyright (c) 2013, Matt Godbolt
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+#include "seasocks/ResponseCode.h"
+#include "seasocks/WebSocket.h"
+
+#include <netinet/in.h>
+
+#include <sys/socket.h>
+
+#include <inttypes.h>
+#include <list>
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace seasocks {
+
+class Logger;
+class ServerImpl;
+class PageRequest;
+class Response;
+
+class Connection : public WebSocket {
+public:
+ Connection(
+ 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; }
+
+ // From WebSocket.
+ virtual void send(const char* webSocketResponse) override;
+ virtual void send(const uint8_t* webSocketResponse, size_t length) override;
+ virtual void close() override;
+
+ // From Request.
+ virtual std::shared_ptr<Credentials> credentials() const override;
+ 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 bool hasHeader(const std::string&) const override;
+ virtual std::string getHeader(const std::string&) const override;
+
+ void setLinger();
+
+ 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; }
+
+ // For testing:
+ std::vector<uint8_t>& getInputBuffer() { return _inBuf; }
+ void handleHixieWebSocket();
+ void handleHybiWebSocket();
+ void setHandler(std::shared_ptr<WebSocket::Handler> handler) {
+ _webSocketHandler = handler;
+ }
+ void handleNewData();
+
+private:
+ void finalise();
+ bool closed() const;
+
+ void closeWhenEmpty();
+ void closeInternal();
+
+ void handleHeaders();
+ void handleWebSocketKey3();
+ void handleWebSocketTextMessage(const char* message);
+ void handleWebSocketBinaryMessage(const std::vector<uint8_t>& message);
+ void handleBufferingPostData();
+ bool handlePageRequest();
+
+ bool bufferLine(const char* line);
+ bool bufferLine(const std::string& line);
+ bool flush();
+
+ bool handleHybiHandshake(int webSocketVersion, const std::string& webSocketKey);
+
+ // Send an error document. Returns 'true' for convenience in handle*() routines.
+ bool sendError(ResponseCode errorCode, const std::string& document);
+
+ // Send individual errors. Again all return true for convenience.
+ bool sendUnsupportedError(const std::string& reason);
+ bool send404();
+ bool sendBadRequest(const std::string& reason);
+ bool sendISE(const std::string& error);
+
+ void sendHybi(int opcode, 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);
+
+ struct Range {
+ long start;
+ long end;
+ 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);
+
+ void bufferResponseAndCommonHeaders(ResponseCode code);
+
+ std::list<Range> processRangesForStaticData(const std::list<Range>& ranges, long fileSize);
+
+ std::shared_ptr<Logger> _logger;
+ ServerImpl &_server;
+ int _fd;
+ bool _shutdown;
+ bool _hadSendError;
+ bool _closeOnEmpty;
+ bool _registeredForWriteEvents;
+ sockaddr_in _address;
+ size_t _bytesSent;
+ size_t _bytesReceived;
+ std::vector<uint8_t> _inBuf;
+ std::vector<uint8_t> _outBuf;
+ std::shared_ptr<WebSocket::Handler> _webSocketHandler;
+ bool _shutdownByUser;
+ std::unique_ptr<PageRequest> _request;
+
+ enum State {
+ INVALID,
+ READING_HEADERS,
+ READING_WEBSOCKET_KEY3,
+ HANDLING_HIXIE_WEBSOCKET,
+ HANDLING_HYBI_WEBSOCKET,
+ BUFFERING_POST_DATA,
+ };
+ State _state;
+
+ Connection(Connection& other) = delete;
+ Connection& operator =(Connection& other) = delete;
+};
+
+} // namespace seasocks
diff --git a/src/main/c/seasocks/Credentials.h b/src/main/c/seasocks/Credentials.h
new file mode 100644
index 0000000..06532c2
--- /dev/null
+++ b/src/main/c/seasocks/Credentials.h
@@ -0,0 +1,73 @@
+// Copyright (c) 2013, Matt Godbolt
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+#include <iostream>
+#include <map>
+#include <set>
+#include <string>
+
+namespace seasocks {
+
+struct Credentials {
+ /**
+ * Whether user was successfuly authenticated.
+ */
+ bool authenticated;
+
+ /**
+ * e.g. "mgodbolt" (or "" for unauthenticated users)
+ */
+ std::string username;
+
+ /**
+ * Groups the user is in.
+ */
+ std::set<std::string> groups;
+
+ /**
+ * Attributes for the user.
+ */
+ std::map<std::string, std::string> attributes;
+
+ Credentials(): authenticated(false) {}
+};
+
+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 << ", ";
+ os << *it;
+ }
+ os << "}, attrs: {";
+ for (auto it = credentials.attributes.begin(); it != credentials.attributes.end(); ++it) {
+ if (it != credentials.attributes.begin()) os << ", ";
+ os << it->first << "=" << it->second;
+ }
+ return os << "}}";
+}
+
+} // namespace seasocks
diff --git a/src/main/c/seasocks/IgnoringLogger.h b/src/main/c/seasocks/IgnoringLogger.h
new file mode 100644
index 0000000..c1d4b1e
--- /dev/null
+++ b/src/main/c/seasocks/IgnoringLogger.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2013, Matt Godbolt
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+#include "seasocks/Logger.h"
+
+namespace seasocks {
+
+class IgnoringLogger : public Logger {
+public:
+ virtual void log(Level level, const char* message) {
+ }
+};
+
+} // namespace seasocks
diff --git a/src/main/c/seasocks/Logger.h b/src/main/c/seasocks/Logger.h
new file mode 100644
index 0000000..317c54a
--- /dev/null
+++ b/src/main/c/seasocks/Logger.h
@@ -0,0 +1,68 @@
+// Copyright (c) 2013, Matt Godbolt
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+namespace seasocks {
+
+/**
+ * Class to send debug logging information to.
+ */
+class Logger {
+public:
+ virtual ~Logger() {}
+
+ enum 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;
+
+ void debug(const char* message, ...);
+ void access(const char* message, ...);
+ void info(const char* message, ...);
+ void warning(const char* message, ...);
+ void error(const char* message, ...);
+ void severe(const char* message, ...);
+
+ 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 "???";
+ }
+ }
+};
+
+} // namespace seasocks
diff --git a/src/main/c/seasocks/PageHandler.h b/src/main/c/seasocks/PageHandler.h
new file mode 100644
index 0000000..d9e671d
--- /dev/null
+++ b/src/main/c/seasocks/PageHandler.h
@@ -0,0 +1,45 @@
+// Copyright (c) 2013, Matt Godbolt
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+#include "seasocks/Credentials.h"
+#include "seasocks/Response.h"
+
+#include <memory>
+#include <string>
+
+namespace seasocks {
+
+class Request;
+
+class PageHandler {
+public:
+ virtual ~PageHandler() {}
+
+ 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
new file mode 100644
index 0000000..f08b1b0
--- /dev/null
+++ b/src/main/c/seasocks/PrintfLogger.h
@@ -0,0 +1,51 @@
+// Copyright (c) 2013, Matt Godbolt
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+#include "seasocks/Logger.h"
+
+#include <stdio.h>
+
+namespace seasocks {
+
+class PrintfLogger : public Logger {
+public:
+ PrintfLogger(Level minLevelToLog = Level::DEBUG) : minLevelToLog(minLevelToLog) {
+ }
+
+ ~PrintfLogger() {
+ }
+
+ virtual void log(Level level, const char* message) {
+ if (level >= minLevelToLog) {
+ printf("%s: %s\n", levelToString(level), message);
+ }
+ }
+
+ Level minLevelToLog;
+};
+
+} // namespace seasocks
diff --git a/src/main/c/seasocks/Request.cpp b/src/main/c/seasocks/Request.cpp
new file mode 100644
index 0000000..63be5a6
--- /dev/null
+++ b/src/main/c/seasocks/Request.cpp
@@ -0,0 +1,52 @@
+// Copyright (c) 2013, Matt Godbolt
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+#include "seasocks/Request.h"
+
+#include <cstring>
+
+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 "???";
+ }
+}
+
+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;
+}
+
+} // namespace seasocks
diff --git a/src/main/c/seasocks/Request.h b/src/main/c/seasocks/Request.h
new file mode 100644
index 0000000..7c0e78a
--- /dev/null
+++ b/src/main/c/seasocks/Request.h
@@ -0,0 +1,73 @@
+// Copyright (c) 2013, Matt Godbolt
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+#include "seasocks/Credentials.h"
+
+#include <netinet/in.h>
+
+#include <cstdint>
+#include <memory>
+
+namespace seasocks {
+
+class Request {
+public:
+ virtual ~Request() {}
+
+ enum Verb {
+ Invalid,
+ WebSocket,
+ Get,
+ Put,
+ Post,
+ Delete,
+ };
+
+ virtual Verb verb() const = 0;
+
+ static const char* name(Verb v);
+ static Verb verb(const char *verb);
+
+ /**
+ * Returns the credentials associated with this request.
+ */
+ virtual std::shared_ptr<Credentials> credentials() const = 0;
+
+ virtual const sockaddr_in& getRemoteAddress() const = 0;
+
+ virtual const std::string& getRequestUri() const = 0;
+
+ virtual size_t contentLength() const = 0;
+
+ virtual const uint8_t* content() const = 0;
+
+ virtual bool hasHeader(const std::string& name) const = 0;
+
+ virtual std::string getHeader(const std::string& name) const = 0;
+};
+
+} // namespace seasocks
diff --git a/src/main/c/seasocks/Response.h b/src/main/c/seasocks/Response.h
new file mode 100644
index 0000000..02cf03b
--- /dev/null
+++ b/src/main/c/seasocks/Response.h
@@ -0,0 +1,62 @@
+// Copyright (c) 2013, Matt Godbolt
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+#include "seasocks/ResponseCode.h"
+
+#include <map>
+#include <memory>
+#include <string>
+
+namespace seasocks {
+
+class Response {
+public:
+ virtual ~Response() {}
+ 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;
+
+ static std::shared_ptr<Response> unhandled();
+
+ static std::shared_ptr<Response> notFound();
+
+ static std::shared_ptr<Response> error(ResponseCode code, const std::string& error);
+
+ static std::shared_ptr<Response> textResponse(const std::string& response);
+ static std::shared_ptr<Response> jsonResponse(const std::string& response);
+ static std::shared_ptr<Response> htmlResponse(const std::string& response);
+};
+
+}
diff --git a/src/main/c/seasocks/ResponseBuilder.cpp b/src/main/c/seasocks/ResponseBuilder.cpp
new file mode 100644
index 0000000..f9f9b29
--- /dev/null
+++ b/src/main/c/seasocks/ResponseBuilder.cpp
@@ -0,0 +1,90 @@
+// Copyright (c) 2013, Matt Godbolt
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+#include "seasocks/ResponseBuilder.h"
+
+#include "internal/ConcreteResponse.h"
+
+namespace seasocks {
+
+ResponseBuilder::ResponseBuilder(ResponseCode code) :
+ _code(code),
+ _contentType("text/plain"),
+ _keepAlive(true),
+ _stream(new std::ostringstream) {
+
+}
+
+ResponseBuilder& ResponseBuilder::asHtml() {
+ return withContentType("text/html");
+}
+
+ResponseBuilder& ResponseBuilder::asText() {
+ return withContentType("text/plain");
+}
+
+ResponseBuilder& ResponseBuilder::asJson() {
+ return withContentType("application/json");
+}
+
+ResponseBuilder& ResponseBuilder::withContentType(const std::string& contentType) {
+ _contentType = contentType;
+ return *this;
+}
+
+ResponseBuilder& ResponseBuilder::keepsConnectionAlive() {
+ _keepAlive = true;
+ return *this;
+}
+
+ResponseBuilder& ResponseBuilder::closesConnection() {
+ _keepAlive = false;
+ return *this;
+}
+
+ResponseBuilder& ResponseBuilder::withLocation(const std::string& location) {
+ return withHeader("Location", location);
+}
+
+ResponseBuilder& ResponseBuilder::setsCookie(const std::string& cookie, const std::string& value) {
+ return addHeader("Set-Cookie", cookie + "=" + value);
+}
+
+ResponseBuilder& ResponseBuilder::withHeader(const std::string& name, const std::string& value) {
+ _headers.erase(name);
+ _headers.insert(std::make_pair(name, value));
+ return *this;
+}
+
+ResponseBuilder& ResponseBuilder::addHeader(const std::string& name, const std::string& value) {
+ _headers.insert(std::make_pair(name, value));
+ return *this;
+}
+
+std::shared_ptr<Response> ResponseBuilder::build() {
+ return std::shared_ptr<Response>(new ConcreteResponse(_code, _stream->str(), _contentType, _headers, _keepAlive));
+}
+
+}
diff --git a/src/main/c/seasocks/ResponseBuilder.h b/src/main/c/seasocks/ResponseBuilder.h
new file mode 100644
index 0000000..0a268e8
--- /dev/null
+++ b/src/main/c/seasocks/ResponseBuilder.h
@@ -0,0 +1,77 @@
+// Copyright (c) 2013, Matt Godbolt
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+#include "seasocks/Response.h"
+
+#include <sstream>
+#include <string>
+
+namespace seasocks {
+
+class ResponseBuilder {
+ ResponseCode _code;
+ std::string _contentType;
+ bool _keepAlive;
+ Response::Headers _headers;
+ std::shared_ptr<std::ostringstream> _stream;
+public:
+ ResponseBuilder(ResponseCode code = ResponseCode::Ok);
+
+ ResponseBuilder& asHtml();
+ ResponseBuilder& asText();
+ ResponseBuilder& asJson();
+ ResponseBuilder& withContentType(const std::string& contentType);
+
+ ResponseBuilder& keepsConnectionAlive();
+ ResponseBuilder& closesConnection();
+
+ ResponseBuilder& withLocation(const std::string& location);
+
+ ResponseBuilder& setsCookie(const std::string& cookie, const std::string& value);
+
+ ResponseBuilder& withHeader(const std::string& name, const std::string& value);
+ 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>
+ 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);
+ return *this;
+ }
+
+ std::shared_ptr<Response> build();
+};
+
+}
diff --git a/src/main/c/seasocks/ResponseCode.cpp b/src/main/c/seasocks/ResponseCode.cpp
new file mode 100644
index 0000000..71e88f6
--- /dev/null
+++ b/src/main/c/seasocks/ResponseCode.cpp
@@ -0,0 +1,42 @@
+// Copyright (c) 2013, Matt Godbolt
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+#include "seasocks/ResponseCode.h"
+
+using namespace seasocks;
+
+bool isOk(seasocks::ResponseCode code) {
+ return static_cast<int>(code) < 400;
+}
+
+const char* name(ResponseCode code) {
+ switch (code) {
+#define SEASOCKS_DEFINE_RESPONSECODE(CODE,SYMBOLICNAME,STRINGNAME) case ResponseCode::SYMBOLICNAME: return STRINGNAME;
+#include "seasocks/ResponseCodeDefs.h"
+
+#undef SEASOCKS_DEFINE_RESPONSECODE
+ }
+ return "Unknown";
+}
diff --git a/src/main/c/seasocks/ResponseCode.h b/src/main/c/seasocks/ResponseCode.h
new file mode 100644
index 0000000..eab3463
--- /dev/null
+++ b/src/main/c/seasocks/ResponseCode.h
@@ -0,0 +1,42 @@
+// Copyright (c) 2013, Matt Godbolt
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+#include <string>
+
+namespace seasocks {
+
+#define SEASOCKS_DEFINE_RESPONSECODE(CODE,SYMBOLICNAME,STRINGNAME) SYMBOLICNAME = CODE,
+enum class ResponseCode {
+#include "seasocks/ResponseCodeDefs.h"
+
+};
+#undef SEASOCKS_DEFINE_RESPONSECODE
+
+}
+
+const char* name(seasocks::ResponseCode code);
+bool isOk(seasocks::ResponseCode code);
diff --git a/src/main/c/seasocks/ResponseCodeDefs.h b/src/main/c/seasocks/ResponseCodeDefs.h
new file mode 100644
index 0000000..9b9878d
--- /dev/null
+++ b/src/main/c/seasocks/ResponseCodeDefs.h
@@ -0,0 +1,68 @@
+// Copyright (c) 2013, Matt Godbolt
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+// 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
+
+SEASOCKS_DEFINE_RESPONSECODE(100, Continue, "Continue")
+SEASOCKS_DEFINE_RESPONSECODE(101, WebSocketProtocolHandshake, "WebSocket Protocol Handshake")
+SEASOCKS_DEFINE_RESPONSECODE(102, Processing, "Processing")
+SEASOCKS_DEFINE_RESPONSECODE(103, Checkpoint, "Checkpoint")
+
+SEASOCKS_DEFINE_RESPONSECODE(200, Ok, "OK")
+SEASOCKS_DEFINE_RESPONSECODE(201, Created, "Created")
+SEASOCKS_DEFINE_RESPONSECODE(202, Accepted, "Accepted")
+SEASOCKS_DEFINE_RESPONSECODE(203, NonAuthoritativeInformation, "Non Authoritative Information")
+SEASOCKS_DEFINE_RESPONSECODE(204, NoContent, "No Content")
+SEASOCKS_DEFINE_RESPONSECODE(205, ResetContent, "Reset Content")
+SEASOCKS_DEFINE_RESPONSECODE(206, PartialContent, "Partial Content")
+SEASOCKS_DEFINE_RESPONSECODE(207, MultiStatus, "Multi-Status")
+SEASOCKS_DEFINE_RESPONSECODE(208, AlreadyReported, "Already Reported")
+SEASOCKS_DEFINE_RESPONSECODE(226, IMUsed, "IM Used")
+
+SEASOCKS_DEFINE_RESPONSECODE(300, MultipleChoices, "Multiple Choices")
+SEASOCKS_DEFINE_RESPONSECODE(301, MovedPermanently, "Moved Permanently")
+SEASOCKS_DEFINE_RESPONSECODE(302, Found, "Found")
+SEASOCKS_DEFINE_RESPONSECODE(303, SeeOther, "See Other")
+SEASOCKS_DEFINE_RESPONSECODE(304, NotModified, "Not Modified")
+SEASOCKS_DEFINE_RESPONSECODE(305, UseProxy, "Use Proxy")
+SEASOCKS_DEFINE_RESPONSECODE(306, SwitchProxy, "Switch Proxy")
+SEASOCKS_DEFINE_RESPONSECODE(307, TemporaryRedirect, "Temporary Redirect")
+SEASOCKS_DEFINE_RESPONSECODE(308, ResumeIncomplete, "Resume Incomplete")
+
+SEASOCKS_DEFINE_RESPONSECODE(400, BadRequest, "Bad Request")
+SEASOCKS_DEFINE_RESPONSECODE(401, Unauthorized, "Unauthorized")
+SEASOCKS_DEFINE_RESPONSECODE(402, PaymentRequired, "Payment Required")
+SEASOCKS_DEFINE_RESPONSECODE(403, Forbidden, "Forbidden")
+SEASOCKS_DEFINE_RESPONSECODE(404, NotFound, "Not Found")
+SEASOCKS_DEFINE_RESPONSECODE(405, MethodNotAllowed, "Method Not Allowed")
+// more here...
+
+SEASOCKS_DEFINE_RESPONSECODE(500, InternalServerError, "Internal Server Error")
+SEASOCKS_DEFINE_RESPONSECODE(501, NotImplemented, "Not Implemented")
+
+#endif
diff --git a/src/main/c/seasocks/Server.h b/src/main/c/seasocks/Server.h
new file mode 100644
index 0000000..4629a43
--- /dev/null
+++ b/src/main/c/seasocks/Server.h
@@ -0,0 +1,172 @@
+// Copyright (c) 2013, Matt Godbolt
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+#include "seasocks/ServerImpl.h"
+#include "seasocks/WebSocket.h"
+
+#include <sys/types.h>
+
+#include <atomic>
+#include <cstdint>
+#include <list>
+#include <map>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <unordered_map>
+
+namespace seasocks {
+
+class Connection;
+class Logger;
+class PageHandler;
+class Request;
+class Response;
+
+class Server : private ServerImpl {
+public:
+ 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);
+
+ // Serves static content from the given port on the current thread, until terminate is called.
+ // Roughly equivalent to startListening(port); setStaticPath(staticPath); loop();
+ // Returns whether exiting was expected.
+ bool serve(const char* staticPath, int port);
+
+ // Starts listening on a given interface (in host order) and port.
+ // Returns true if all was ok.
+ bool startListening(uint32_t ipInHostOrder, int port);
+
+ // Starts listening on a port on all interfaces.
+ // Returns true if all was ok.
+ bool startListening(int port);
+
+ // Sets the path to server static content from.
+ void setStaticPath(const char* staticPath);
+
+ // Loop (until terminate called from another thread).
+ // Returns true if terminate() was used to exit the loop, false if there
+ // was an error.
+ bool loop();
+
+ // Runs a single iteration of the main loop, blocking for a given time.
+ // Returns immediately if terminate() has been called. Must be consistently
+ // called from the same thread. Returns an enum describing why it returned.
+ enum class PollResult {
+ Continue,
+ Terminated,
+ Error,
+ };
+ PollResult poll(int millisToBlock);
+
+ // 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; }
+
+ // Terminate any loop() or poll(). May be called from any thread.
+ void terminate();
+
+ class Runnable {
+ public:
+ virtual ~Runnable() {}
+ virtual void run() = 0;
+ };
+ // Execute a task on the SeaSocks thread.
+ void execute(std::shared_ptr<Runnable> runnable);
+
+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 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 std::string getStatsDocument() const override;
+ virtual void checkThread() const override;
+
+ bool makeNonBlocking(int fd) const;
+ bool configureSocket(int fd) const;
+ void handleAccept();
+ std::shared_ptr<Runnable> popNextRunnable();
+ void processEventQueue();
+
+ void shutdown();
+
+ void checkAndDispatchEpoll(int epollMillis);
+ void handlePipe();
+ enum NewState { KeepOpen, Close };
+ NewState handleConnectionEvents(Connection* connection, uint32_t events);
+
+ // Connections, mapped to initial connection time.
+ std::map<Connection*, time_t> _connections;
+ std::shared_ptr<Logger> _logger;
+ int _listenSock;
+ int _epollFd;
+ int _eventFd;
+ int _maxKeepAliveDrops;
+ int _lameConnectionTimeoutSeconds;
+ time_t _nextDeadConnectionCheck;
+
+ struct WebSocketHandlerEntry {
+ std::shared_ptr<WebSocket::Handler> handler;
+ bool allowCrossOrigin;
+ };
+ typedef std::unordered_map<std::string, WebSocketHandlerEntry> WebSocketHandlerMap;
+ WebSocketHandlerMap _webSocketHandlerMap;
+
+ std::list<std::shared_ptr<PageHandler>> _pageHandlers;
+
+ std::mutex _pendingRunnableMutex;
+ std::list<std::shared_ptr<Runnable>> _pendingRunnables;
+
+ pid_t _threadId;
+
+ std::string _staticPath;
+ std::atomic<bool> _terminate;
+ std::atomic<bool> _expectedTerminate;
+};
+
+} // namespace seasocks
diff --git a/src/main/c/seasocks/ServerImpl.h b/src/main/c/seasocks/ServerImpl.h
new file mode 100644
index 0000000..e7dd46a
--- /dev/null
+++ b/src/main/c/seasocks/ServerImpl.h
@@ -0,0 +1,54 @@
+// Copyright (c) 2013, Matt Godbolt
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+#include "seasocks/WebSocket.h"
+
+#include <string>
+
+namespace seasocks {
+
+class Connection;
+class Request;
+class Response;
+
+// Internal implementation used to give access to internals to Connections.
+class ServerImpl {
+public:
+ virtual ~ServerImpl() {}
+
+ 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 std::string getStatsDocument() const = 0;
+ virtual void checkThread() const = 0;
+};
+
+}
diff --git a/src/main/c/seasocks/StringUtil.h b/src/main/c/seasocks/StringUtil.h
new file mode 100644
index 0000000..3109104
--- /dev/null
+++ b/src/main/c/seasocks/StringUtil.h
@@ -0,0 +1,48 @@
+// Copyright (c) 2013, Matt Godbolt
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+#include <netinet/in.h>
+
+#include <string>
+#include <vector>
+
+namespace seasocks {
+
+char* skipWhitespace(char* str);
+char* skipNonWhitespace(char* str);
+char* shift(char*& str);
+
+std::string getLastError();
+std::string formatAddress(const sockaddr_in& address);
+
+std::vector<std::string> split(const std::string& input, char splitChar);
+
+void replace(std::string& string, const std::string& find, const std::string& replace);
+
+bool caseInsensitiveSame(const std::string &lhs, const std::string &rhs);
+
+} // namespace seasocks
diff --git a/src/main/c/seasocks/ToString.h b/src/main/c/seasocks/ToString.h
new file mode 100644
index 0000000..40f607e
--- /dev/null
+++ b/src/main/c/seasocks/ToString.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2013, Matt Godbolt
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+#include <sstream>
+#include <string>
+
+namespace seasocks {
+
+template<typename T>
+std::string toString(const T& obj) {
+ std::stringstream str;
+ str << obj;
+ return str.str();
+}
+
+}
diff --git a/src/main/c/seasocks/WebSocket.h b/src/main/c/seasocks/WebSocket.h
new file mode 100644
index 0000000..f52509a
--- /dev/null
+++ b/src/main/c/seasocks/WebSocket.h
@@ -0,0 +1,86 @@
+// Copyright (c) 2013, Matt Godbolt
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+#include "seasocks/Request.h"
+
+#include <vector>
+
+namespace seasocks {
+
+class WebSocket : public Request {
+public:
+ /**
+ * 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.
+ */
+ virtual void send(const char* data) = 0;
+ /**
+ * 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.
+ */
+ virtual void send(const uint8_t* data, size_t length) = 0;
+ /**
+ * Close the socket. It's invalid to access the socket after
+ * calling close(). The Handler::onDisconnect() call may occur
+ * at a later time.
+ */
+ virtual void close() = 0;
+
+ /**
+ * Interface to dealing with WebSocket connections.
+ */
+ class Handler {
+ public:
+ virtual ~Handler() { }
+
+ /**
+ * Called on the seasocks thread during initial connection.
+ */
+ virtual void onConnect(WebSocket* connection) = 0;
+ /**
+ * Called on the seasocks thread with upon receipt of a full text WebSocket message.
+ */
+ virtual void onData(WebSocket* connection, const char* data) {}
+ /**
+ * 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) {}
+ /**
+ * Called on the seasocks thread when the socket has been
+ */
+ virtual void onDisconnect(WebSocket* connection) = 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() {}
+};
+
+} // namespace seasocks
diff --git a/src/main/c/seasocks/util/CrackedUri.h b/src/main/c/seasocks/util/CrackedUri.h
new file mode 100644
index 0000000..681ea42
--- /dev/null
+++ b/src/main/c/seasocks/util/CrackedUri.h
@@ -0,0 +1,57 @@
+// Copyright (c) 2013, Matt Godbolt
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+namespace seasocks {
+
+class CrackedUri {
+ std::vector<std::string> _path;
+ std::unordered_multimap<std::string, std::string> _queryParams;
+
+public:
+ 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; }
+
+ bool hasParam(const std::string& param) const;
+
+ // Returns the user-supplied default if not present. Returns the first found in the case of multiple params.
+ std::string queryParam(const std::string& param, const std::string& def = std::string()) const;
+
+ std::vector<std::string> allQueryParams(const std::string& param) const;
+
+ // Returns a new uri with the frontmost path item shifted off. The path
+ // is always guaranteed to be non-empty. In the case of shifting the last
+ // path element, returns a path of {""}.
+ CrackedUri shift() const;
+};
+
+}
diff --git a/src/main/c/seasocks/util/CrackedUriPageHandler.h b/src/main/c/seasocks/util/CrackedUriPageHandler.h
new file mode 100644
index 0000000..cf8c362
--- /dev/null
+++ b/src/main/c/seasocks/util/CrackedUriPageHandler.h
@@ -0,0 +1,45 @@
+// Copyright (c) 2013, Matt Godbolt
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+#include "seasocks/Request.h"
+#include "seasocks/Response.h"
+#include "seasocks/util/CrackedUri.h"
+
+#include <memory>
+
+namespace seasocks {
+
+class CrackedUriPageHandler {
+public:
+ virtual ~CrackedUriPageHandler() {}
+
+ virtual std::shared_ptr<Response> handle(const CrackedUri& uri, const Request& request) = 0;
+
+ typedef std::shared_ptr<CrackedUriPageHandler> Ptr;
+};
+
+}
diff --git a/src/main/c/seasocks/util/Html.h b/src/main/c/seasocks/util/Html.h
new file mode 100644
index 0000000..6b3e887
--- /dev/null
+++ b/src/main/c/seasocks/util/Html.h
@@ -0,0 +1,206 @@
+// Copyright (c) 2013, Matt Godbolt
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+#include "seasocks/ToString.h"
+
+#include <functional>
+#include <iostream>
+#include <list>
+#include <sstream>
+#include <string>
+
+namespace seasocks {
+
+namespace html {
+
+class Element {
+ bool _isTextNode;
+ bool _needsClose;
+ std::string _nameOrText;
+ std::string _attributes;
+ std::list<Element> _children;
+
+ void append() {
+ }
+
+ template<typename T, typename... Args>
+ void append(const T& t, Args&& ...args) {
+ operator<<(t);
+ append(std::forward<Args>(args)...);
+ }
+
+ Element() : _isTextNode(true), _needsClose(false) {}
+public:
+ 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>
+ static Element textElement(const T& text) {
+ Element e;
+ e._nameOrText = toString(text);
+ return e;
+ }
+
+ template<typename T>
+ Element& addAttribute(const char* attr, const T& value) {
+ _attributes += std::string(" ") + attr + "=\"" + toString(value) + "\"";
+ return *this;
+ }
+
+ Element& clazz(const std::string& clazz) {
+ return addAttribute("class", clazz);
+ }
+
+ Element& title(const std::string& title) {
+ return addAttribute("title", title);
+ }
+
+ Element& style(const std::string& style) {
+ return addAttribute("style", style);
+ }
+
+ Element& alt(const std::string& alt) {
+ return addAttribute("alt", alt);
+ }
+
+ Element& hidden() {
+ return addAttribute("style", "display:none;");
+ }
+
+ Element& id(const std::string& id) {
+ return addAttribute("id", id);
+ }
+
+ Element& operator <<(const char* child) {
+ _children.push_back(textElement(child));
+ return *this;
+ }
+
+ Element& operator <<(const std::string& child) {
+ _children.push_back(textElement(child));
+ return *this;
+ }
+
+ Element& operator <<(const Element& child) {
+ _children.push_back(child);
+ return *this;
+ }
+
+ template<class T>
+ typename std::enable_if<std::is_integral<T>::value || std::is_floating_point<T>::value, Element&>::type
+ operator <<(const T& child) {
+ _children.push_back(textElement(child));
+ return *this;
+ }
+
+ 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;
+ }
+ return os;
+ }
+ os << "<" << elem._nameOrText << elem._attributes << ">";
+ for (auto it = elem._children.cbegin(); it != elem._children.cend(); ++it) {
+ os << *it;
+ }
+ if (elem._needsClose) {
+ os << "</" << elem._nameOrText << ">";
+ }
+ return os;
+ }
+
+ template<typename C, typename F>
+ Element& addAll(const C& container, F functor) {
+ for (auto it = container.cbegin(); it != container.cend(); ++it) {
+ operator<<(functor(*it));
+ }
+ return *this;
+ }
+
+ std::string str() const {
+ std::ostringstream os;
+ os << *this;
+ return os.str();
+ }
+};
+
+#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)
+HTMLELEM(body)
+HTMLELEM(h1)
+HTMLELEM(h2)
+HTMLELEM(h3)
+HTMLELEM(h4)
+HTMLELEM(h5)
+HTMLELEM(table)
+HTMLELEM(thead)
+HTMLELEM(tbody)
+HTMLELEM(tr)
+HTMLELEM(td)
+HTMLELEM(th)
+HTMLELEM(div)
+HTMLELEM(span)
+HTMLELEM(ul)
+HTMLELEM(ol)
+HTMLELEM(li)
+HTMLELEM(label)
+HTMLELEM(button)
+#undef HTMLELEM
+
+inline Element empty() { return Element::textElement(""); }
+
+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 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 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) {
+ return Element("a", true, std::forward<Args>(args)...).addAttribute("href", href);
+}
+
+} // namespace html
+
+} // namespace seasocks
diff --git a/src/main/c/seasocks/util/Json.h b/src/main/c/seasocks/util/Json.h
new file mode 100644
index 0000000..c8a69dc
--- /dev/null
+++ b/src/main/c/seasocks/util/Json.h
@@ -0,0 +1,233 @@
+// Copyright (c) 2013, Matt Godbolt
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+#include <algorithm>
+#include <ctime>
+#include <iostream>
+#include <map>
+#include <sstream>
+#include <string>
+#include <type_traits>
+#include <vector>
+
+namespace seasocks {
+
+///////////////////////////////////
+
+inline void jsonToStream(std::ostream& str) {}
+
+void jsonToStream(std::ostream& str, const char* t);
+
+void jsonToStream(std::ostream& str, bool b);
+
+inline void jsonToStream(std::ostream& str, const std::string& t) {
+ jsonToStream(str, t.c_str());
+}
+
+template<typename O>
+class is_jsonable {
+ template<typename OO>
+ static auto test(int)
+ -> decltype(&OO::jsonToStream, std::true_type());
+
+ template<typename>
+ static auto test(...) -> std::false_type;
+
+public:
+ static constexpr bool value = decltype(test<O>(0))::value;
+};
+
+template<typename T>
+class is_streamable {
+ template<typename TT>
+ static auto test(int)
+ -> decltype(std::declval<std::ostream&>() << std::declval<TT>(), std::true_type());
+
+ template<typename>
+ static auto test(...) -> std::false_type;
+
+public:
+ static constexpr bool value = decltype(test<T>(0))::value;
+};
+
+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>
+typename std::enable_if<is_jsonable<T>::value, void>::type
+jsonToStream(std::ostream& str, const T& t) {
+ t.jsonToStream(str);
+}
+
+template<typename T>
+typename std::enable_if<
+ !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>
+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);
+ str << ",";
+ jsonToStream(str, std::forward<Args>(args)...);
+}
+
+///////////////////////////////////
+
+inline void jsonKeyPairToStream(std::ostream& str) {}
+
+template<typename T>
+void jsonKeyPairToStream(std::ostream& str, const char* key, const T& value) {
+ jsonToStream(str, key);
+ str << ":";
+ jsonToStream(str, value);
+}
+
+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 K, typename V, typename... Args>
+void jsonKeyPairToStream(std::ostream& str, const K& key, const V& value, Args&&... args) {
+ jsonKeyPairToStream(str, key, value);
+ str << ",";
+ jsonKeyPairToStream(str, std::forward<Args>(args)...);
+}
+
+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 {
+ o << *this;
+ }
+};
+static_assert(is_streamable<JsonnedString>::value, "Internal stream problem");
+static_assert(is_jsonable<JsonnedString>::value, "Internal stream problem");
+
+struct EpochTimeAsLocal {
+ time_t t;
+ EpochTimeAsLocal(time_t t) : t(t) {}
+ void jsonToStream(std::ostream &o) const;
+};
+static_assert(is_jsonable<EpochTimeAsLocal>::value, "Internal stream problem");
+
+template<typename... Args>
+JsonnedString makeMap(Args&&... args) {
+ std::stringstream str;
+ str << '{';
+ jsonKeyPairToStream(str, std::forward<Args>(args)...);
+ str << '}';
+ return JsonnedString(str);
+}
+
+template<typename T>
+JsonnedString makeMapFromContainer(const T& m) {
+ std::stringstream str;
+ str << "{";
+ bool first = true;
+ for (const auto &it : m) {
+ if (!first) str << ",";
+ first = false;
+ jsonKeyPairToStream(str, it.first, it.second);
+ }
+ str << "}";
+ return JsonnedString(str);
+}
+
+template<typename ... Args>
+JsonnedString makeArray(Args&&... args) {
+ std::stringstream str;
+ str << '[';
+ jsonToStream(str, std::forward<Args>(args)...);
+ str << ']';
+ return JsonnedString(str);
+}
+
+template<typename T>
+JsonnedString makeArrayFromContainer(const T &list) {
+ std::stringstream str;
+ str << '[';
+ bool first = true;
+ for (const auto &s : list) {
+ if (!first) {
+ str << ',';
+ }
+ first = false;
+ jsonToStream(str, s);
+ };
+ str << ']';
+ return JsonnedString(str);
+}
+
+template<typename T>
+JsonnedString makeArray(const std::initializer_list<T> &list) {
+ std::stringstream str;
+ str << '[';
+ bool first = true;
+ for (const auto &s : list) {
+ if (!first) {
+ str << ',';
+ }
+ first = false;
+ jsonToStream(str, s);
+ };
+ str << ']';
+ return JsonnedString(str);
+}
+
+template<typename ... Args>
+JsonnedString makeExecString(const char* function, Args&&... args) {
+ std::stringstream str;
+ str << function << '(';
+ jsonToStream(str, std::forward<Args>(args)...);
+ str << ')';
+ return JsonnedString(str);
+}
+
+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
new file mode 100644
index 0000000..275f829
--- /dev/null
+++ b/src/main/c/seasocks/util/PathHandler.h
@@ -0,0 +1,68 @@
+// Copyright (c) 2013, Matt Godbolt
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+#include "seasocks/util/CrackedUriPageHandler.h"
+
+#include <string>
+
+namespace seasocks {
+
+// Handles a subpath of a website. Passes on a shifted path to the registered
+// subhandlers. Useful, for example, to place a bunch of handlers beneath a
+// common subpath on the server, e.g.
+// PathHandler("debug")
+// .add(make_shared<DebugStatsHandler>())
+// .add(make_shared<AnotherDebugThing>())
+// .add(...) etc
+// Each handler's CrackedUri will be shifted (i.e. won't have the "debug"
+// prefix)
+class PathHandler : public CrackedUriPageHandler {
+ std::string _path;
+ 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) {
+ 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(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;
+};
+
+}
diff --git a/src/main/c/seasocks/util/RootPageHandler.h b/src/main/c/seasocks/util/RootPageHandler.h
new file mode 100644
index 0000000..2c2ce1a
--- /dev/null
+++ b/src/main/c/seasocks/util/RootPageHandler.h
@@ -0,0 +1,49 @@
+// Copyright (c) 2013, Matt Godbolt
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+#include "seasocks/PageHandler.h"
+#include "seasocks/util/CrackedUriPageHandler.h"
+
+#include <memory>
+#include <vector>
+
+namespace seasocks {
+
+class RootPageHandler : public PageHandler {
+ std::vector<CrackedUriPageHandler::Ptr> _handlers;
+
+public:
+ RootPageHandler();
+ virtual ~RootPageHandler();
+
+ CrackedUriPageHandler::Ptr add(const CrackedUriPageHandler::Ptr& handler);
+
+ // From PageHandler.
+ virtual std::shared_ptr<Response> handle(const Request& request) override;
+};
+
+}
diff --git a/src/main/c/seasocks/util/StaticResponseHandler.h b/src/main/c/seasocks/util/StaticResponseHandler.h
new file mode 100644
index 0000000..ff6c5e7
--- /dev/null
+++ b/src/main/c/seasocks/util/StaticResponseHandler.h
@@ -0,0 +1,51 @@
+// Copyright (c) 2013, Matt Godbolt
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+#include <seasocks/util/CrackedUriPageHandler.h>
+#include <seasocks/Response.h>
+
+#include <memory>
+
+namespace seasocks {
+
+// Returns a canned response for a given pathname.
+class StaticResponseHandler : public CrackedUriPageHandler {
+ std::string _path;
+ std::shared_ptr<Response> _response;
+
+public:
+ StaticResponseHandler(const std::string& path, std::shared_ptr<Response> response)
+ : _path(path), _response(response) {}
+
+ virtual std::shared_ptr<Response> handle(
+ const CrackedUri& uri,
+ const Request&) override {
+ if (uri.path().size() != 1 || uri.path()[0] != _path)
+ return Response::unhandled();
+ return _response;
+ }
+};
+
+}