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/Connection.cpp b/src/main/c/Connection.cpp
new file mode 100644
index 0000000..9c250a0
--- /dev/null
+++ b/src/main/c/Connection.cpp
@@ -0,0 +1,1075 @@
+// Copyright (c) 2013, Matt Godbolt
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+#include "internal/Config.h"
+#include "internal/Embedded.h"
+#include "internal/HeaderMap.h"
+#include "internal/HybiAccept.h"
+#include "internal/HybiPacketDecoder.h"
+#include "internal/LogStream.h"
+#include "internal/PageRequest.h"
+#include "internal/Version.h"
+
+#include "md5/md5.h"
+
+#include "seasocks/Connection.h"
+#include "seasocks/Credentials.h"
+#include "seasocks/Logger.h"
+#include "seasocks/PageHandler.h"
+#include "seasocks/Server.h"
+#include "seasocks/StringUtil.h"
+#include "seasocks/ToString.h"
+
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <fstream>
+#include <iostream>
+#include <limits>
+#include <sstream>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <unordered_map>
+
+namespace {
+
+uint32_t parseWebSocketKey(const std::string& key) {
+ uint32_t keyNumber = 0;
+ uint32_t numSpaces = 0;
+ for (auto c : key) {
+ if (c >= '0' && c <= '9') {
+ keyNumber = keyNumber * 10 + c - '0';
+ } else if (c == ' ') {
+ ++numSpaces;
+ }
+ }
+ return numSpaces > 0 ? keyNumber / numSpaces : 0;
+}
+
+char* extractLine(uint8_t*& first, uint8_t* last, char** colon = NULL) {
+ 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);
+ }
+ if (colon && ptr[0] == ':' && *colon == NULL) {
+ *colon = reinterpret_cast<char*> (ptr);
+ }
+ }
+ return NULL;
+}
+
+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" },
+};
+
+std::string getExt(const std::string& path) {
+ auto lastDot = path.find_last_of('.');
+ if (lastDot != path.npos) {
+ return path.substr(lastDot + 1);
+ }
+ return "";
+}
+
+const std::string& getContentType(const std::string& path) {
+ auto it = contentTypes.find(getExt(path));
+ if (it != contentTypes.end()) {
+ return it->second;
+ }
+ static const std::string defaultType("text/html");
+ return defaultType;
+}
+
+// Cacheability is only set for resources that *REQUIRE* caching for browser support reasons.
+// It's off for everything else to save on browser reload headaches during development, at
+// least until we support ETags or If-Modified-Since: type checking, which we may never do.
+bool isCacheable(const std::string& path) {
+ std::string extension = getExt(path);
+ if (extension == "mp3" || extension == "wav") {
+ return true;
+ }
+ 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;
+
+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) {}
+
+ virtual void log(Level level, const char* message) {
+ _logger->log(level, (_prefix + message).c_str());
+ }
+};
+
+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);
+ if (seasocks::caseInsensitiveSame(conType, type))
+ return true;
+ }
+ return false;
+}
+
+} // namespace
+
+namespace seasocks {
+
+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) {
+}
+
+Connection::~Connection() {
+ _server.checkThread();
+ finalise();
+}
+
+void Connection::close() {
+ // This is the user-side close requests ONLY! You should Call closeInternal
+ _shutdownByUser = true;
+ closeInternal();
+}
+
+void Connection::closeWhenEmpty() {
+ if (_outBuf.empty()) {
+ closeInternal();
+ } else {
+ _closeOnEmpty = true;
+ }
+}
+
+void Connection::closeInternal() {
+ // It only actually only calls shutdown on the socket,
+ // leaving the close of the FD and the cleanup until the destructor runs.
+ _server.checkThread();
+ if (_fd != -1 && !_shutdown && ::shutdown(_fd, SHUT_RDWR) == -1) {
+ LS_WARNING(_logger, "Unable to shutdown socket : " << getLastError());
+ }
+ _shutdown = true;
+}
+
+
+void Connection::finalise() {
+ if (_webSocketHandler) {
+ _webSocketHandler->onDisconnect(this);
+ _webSocketHandler.reset();
+ }
+ if (_fd != -1) {
+ _server.remove(this);
+ LS_DEBUG(_logger, "Closing socket");
+ ::close(_fd);
+ }
+ _fd = -1;
+}
+
+int 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);
+ if (sendResult == -1) {
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
+ // Treat this as if zero bytes were written.
+ return 0;
+ }
+ LS_WARNING(_logger, "Unable to write to socket : " << getLastError() << " - disabling further writes");
+ closeInternal();
+ } else {
+ _bytesSent += sendResult;
+ }
+ return sendResult;
+}
+
+bool Connection::write(const void* data, size_t size, bool flushIt) {
+ if (closed() || _closeOnEmpty) {
+ return false;
+ }
+ if (size) {
+ int bytesSent = 0;
+ if (_outBuf.empty() && flushIt) {
+ // Attempt fast path, send directly.
+ bytesSent = safeSend(data, size);
+ if (bytesSent == static_cast<int>(size)) {
+ // We sent directly.
+ return true;
+ }
+ if (bytesSent == -1) {
+ return false;
+ }
+ }
+ 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 << ")");
+ closeInternal();
+ return false;
+ }
+ _outBuf.resize(newBufferSize);
+ memcpy(&_outBuf[endOfBuffer], reinterpret_cast<const uint8_t*>(data) + bytesSent, bytesToBuffer);
+ }
+ if (flushIt) {
+ return flush();
+ }
+ return true;
+}
+
+bool Connection::bufferLine(const char* line) {
+ static const char crlf[] = { '\r', '\n' };
+ if (!write(line, strlen(line), false)) return false;
+ return write(crlf, 2, false);
+}
+
+bool Connection::bufferLine(const std::string& line) {
+ std::string lineAndCrlf = line + "\r\n";
+ return write(lineAndCrlf.c_str(), lineAndCrlf.length(), false);
+}
+
+void Connection::handleDataReadyForRead() {
+ if (closed()) {
+ return;
+ }
+ size_t curSize = _inBuf.size();
+ _inBuf.resize(curSize + ReadWriteBufferSize);
+ int result = ::read(_fd, &_inBuf[curSize], ReadWriteBufferSize);
+ if (result == -1) {
+ LS_WARNING(_logger, "Unable to read from socket : " << getLastError());
+ return;
+ }
+ if (result == 0) {
+ LS_DEBUG(_logger, "Remote end closed connection");
+ closeInternal();
+ return;
+ }
+ _bytesReceived += result;
+ _inBuf.resize(curSize + result);
+ handleNewData();
+}
+
+void Connection::handleDataReadyForWrite() {
+ if (closed()) {
+ return;
+ }
+ flush();
+}
+
+bool Connection::flush() {
+ if (_outBuf.empty()) {
+ return true;
+ }
+ int numSent = safeSend(&_outBuf[0], _outBuf.size());
+ if (numSent == -1) {
+ return false;
+ }
+ _outBuf.erase(_outBuf.begin(), _outBuf.begin() + numSent);
+ if (_outBuf.size() > 0 && !_registeredForWriteEvents) {
+ if (!_server.subscribeToWriteEvents(this)) {
+ return false;
+ }
+ _registeredForWriteEvents = true;
+ } else if (_outBuf.empty() && _registeredForWriteEvents) {
+ if (!_server.unsubscribeFromWriteEvents(this)) {
+ return false;
+ }
+ _registeredForWriteEvents = false;
+ }
+ if (_outBuf.empty() && !closed() && _closeOnEmpty) {
+ LS_DEBUG(_logger, "Ready for close, now empty");
+ closeInternal();
+ }
+ return true;
+}
+
+bool Connection::closed() const {
+ return _fd == -1 || _shutdown;
+}
+
+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;
+ }
+}
+
+void Connection::handleHeaders() {
+ if (_inBuf.size() < 4) {
+ return;
+ }
+ 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') {
+ if (!processHeaders(&_inBuf[0], &_inBuf[i + 2])) {
+ closeInternal();
+ return;
+ }
+ _inBuf.erase(_inBuf.begin(), _inBuf.begin() + i + 4);
+ handleNewData();
+ return;
+ }
+ }
+ if (_inBuf.size() > MaxHeadersSize) {
+ sendUnsupportedError("Headers too big");
+ }
+}
+
+void Connection::handleWebSocketKey3() {
+ constexpr auto WebSocketKeyLen = 8u;
+ if (_inBuf.size() < WebSocketKeyLen) {
+ return;
+ }
+
+ struct {
+ uint32_t key1;
+ uint32_t key2;
+ char key3[WebSocketKeyLen];
+ } md5Source;
+
+ auto key1 = parseWebSocketKey(_request->getHeader("Sec-WebSocket-Key1"));
+ auto key2 = parseWebSocketKey(_request->getHeader("Sec-WebSocket-Key2"));
+
+ LS_DEBUG(_logger, "Got a hixie websocket with key1=0x" << std::hex << key1 << ", key2=0x" << key2);
+
+ md5Source.key1 = htonl(key1);
+ md5Source.key2 = htonl(key2);
+ memcpy(&md5Source.key3, &_inBuf[0], WebSocketKeyLen);
+
+ uint8_t digest[16];
+ md5_state_t md5state;
+ md5_init(&md5state);
+ md5_append(&md5state, reinterpret_cast<const uint8_t*>(&md5Source), sizeof(md5Source));
+ md5_finish(&md5state, digest);
+
+ LS_DEBUG(_logger, "Attempting websocket upgrade");
+
+ bufferResponseAndCommonHeaders(ResponseCode::WebSocketProtocolHandshake);
+ bufferLine("Upgrade: websocket");
+ bufferLine("Connection: Upgrade");
+ bool allowCrossOrigin = _server.isCrossOriginAllowed(_request->getRequestUri());
+ if (_request->hasHeader("Origin") && allowCrossOrigin) {
+ bufferLine("Sec-WebSocket-Origin: " + _request->getHeader("Origin"));
+ }
+ if (_request->hasHeader("Host")) {
+ auto host = _request->getHeader("Host");
+ if (!allowCrossOrigin) {
+ bufferLine("Sec-WebSocket-Origin: http://" + host);
+ }
+ bufferLine("Sec-WebSocket-Location: ws://" + host + _request->getRequestUri());
+ }
+ bufferLine("");
+
+ write(&digest, 16, true);
+
+ _state = HANDLING_HIXIE_WEBSOCKET;
+ _inBuf.erase(_inBuf.begin(), _inBuf.begin() + 8);
+ if (_webSocketHandler) {
+ _webSocketHandler->onConnect(this);
+ }
+}
+
+void Connection::handleBufferingPostData() {
+ if (_request->consumeContent(_inBuf)) {
+ _state = READING_HEADERS;
+ if (!handlePageRequest()) {
+ closeInternal();
+ }
+ }
+}
+
+void Connection::send(const char* webSocketResponse) {
+ _server.checkThread();
+ if (_shutdown) {
+ if (_shutdownByUser) {
+ LS_ERROR(_logger, "Server wrote to connection after closing it");
+ }
+ return;
+ }
+ auto messageLength = strlen(webSocketResponse);
+ if (_state == HANDLING_HIXIE_WEBSOCKET) {
+ uint8_t zero = 0;
+ 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);
+}
+
+void Connection::send(const uint8_t* data, size_t length) {
+ _server.checkThread();
+ if (_shutdown) {
+ if (_shutdownByUser) {
+ LS_ERROR(_logger, "Client wrote to connection after closing it");
+ }
+ return;
+ }
+ if (_state == HANDLING_HIXIE_WEBSOCKET) {
+ LS_ERROR(_logger, "Hixie does not support binary");
+ return;
+ }
+ sendHybi(HybiPacketDecoder::OPCODE_BINARY, data, length);
+}
+
+void Connection::sendHybi(int opcode, const uint8_t* webSocketResponse, size_t messageLength) {
+ uint8_t firstByte = 0x80 | opcode;
+ if (!write(&firstByte, 1, false)) return;
+ if (messageLength < 126) {
+ uint8_t nextByte = messageLength; // No MASK bit set.
+ if (!write(&nextByte, 1, false)) return;
+ } else if (messageLength < 65536) {
+ uint8_t nextByte = 126; // No MASK bit set.
+ if (!write(&nextByte, 1, false)) return;
+ auto lengthBytes = htons(messageLength);
+ if (!write(&lengthBytes, 2, false)) return;
+ } else {
+ uint8_t nextByte = 127; // No MASK bit set.
+ if (!write(&nextByte, 1, false)) return;
+ uint64_t lengthBytes = __bswap_64(messageLength);
+ if (!write(&lengthBytes, 8, false)) return;
+ }
+ write(webSocketResponse, messageLength, true);
+}
+
+std::shared_ptr<Credentials> Connection::credentials() const {
+ _server.checkThread();
+ return _request ? _request->credentials() : std::shared_ptr<Credentials>();
+}
+
+void Connection::handleHixieWebSocket() {
+ if (_inBuf.empty()) {
+ return;
+ }
+ size_t messageStart = 0;
+ while (messageStart < _inBuf.size()) {
+ if (_inBuf[messageStart] != 0) {
+ LS_WARNING(_logger, "Error in WebSocket input stream (got " << (int)_inBuf[messageStart] << ")");
+ closeInternal();
+ return;
+ }
+ // TODO: UTF-8
+ size_t endOfMessage = 0;
+ for (size_t i = messageStart + 1; i < _inBuf.size(); ++i) {
+ if (_inBuf[i] == 0xff) {
+ endOfMessage = i;
+ break;
+ }
+ }
+ if (endOfMessage != 0) {
+ _inBuf[endOfMessage] = 0;
+ handleWebSocketTextMessage(reinterpret_cast<const char*>(&_inBuf[messageStart + 1]));
+ messageStart = endOfMessage + 1;
+ } else {
+ break;
+ }
+ }
+ if (messageStart != 0) {
+ _inBuf.erase(_inBuf.begin(), _inBuf.begin() + messageStart);
+ }
+ if (_inBuf.size() > MaxWebsocketMessageSize) {
+ LS_WARNING(_logger, "WebSocket message too long");
+ closeInternal();
+ }
+}
+
+void Connection::handleHybiWebSocket() {
+ if (_inBuf.empty()) {
+ return;
+ }
+ HybiPacketDecoder decoder(*_logger, _inBuf);
+ 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;
+ }
+ }
+ if (decoder.numBytesDecoded() != 0) {
+ _inBuf.erase(_inBuf.begin(), _inBuf.begin() + decoder.numBytesDecoded());
+ }
+ if (_inBuf.size() > MaxWebsocketMessageSize) {
+ LS_WARNING(_logger, "WebSocket message too long");
+ closeInternal();
+ }
+}
+
+void Connection::handleWebSocketTextMessage(const char* message) {
+ LS_DEBUG(_logger, "Got text web socket message: '" << message << "'");
+ if (_webSocketHandler) {
+ _webSocketHandler->onData(this, message);
+ }
+}
+
+void Connection::handleWebSocketBinaryMessage(const std::vector<uint8_t>& message) {
+ LS_DEBUG(_logger, "Got binary web socket message (size: " << message.size() << ")");
+ if (_webSocketHandler) {
+ _webSocketHandler->onData(this, &message[0], message.size());
+ }
+}
+
+bool Connection::sendError(ResponseCode errorCode, const std::string& body) {
+ assert(_state != HANDLING_HIXIE_WEBSOCKET);
+ auto errorNumber = static_cast<int>(errorCode);
+ auto message = ::name(errorCode);
+ bufferResponseAndCommonHeaders(errorCode);
+ auto errorContent = findEmbeddedContent("/_error.html");
+ std::string document;
+ if (errorContent) {
+ document.assign(errorContent->data, errorContent->data + errorContent->length);
+ replace(document, "%%ERRORCODE%%", toString(errorNumber));
+ replace(document, "%%MESSAGE%%", message);
+ replace(document, "%%BODY%%", body);
+ } 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>";
+ document = documentStr.str();
+ }
+ bufferLine("Content-Length: " + toString(document.length()));
+ bufferLine("Connection: close");
+ bufferLine("");
+ bufferLine(document);
+ if (!flush()) {
+ return false;
+ }
+ closeWhenEmpty();
+ return true;
+}
+
+bool Connection::sendUnsupportedError(const std::string& reason) {
+ return sendError(ResponseCode::NotImplemented, reason);
+}
+
+bool Connection::send404() {
+ auto path = getRequestUri();
+ auto embedded = findEmbeddedContent(path);
+ if (embedded) {
+ return sendData(getContentType(path), embedded->data, embedded->length);
+ } else if (strcmp(path.c_str(), "/_livestats.js") == 0) {
+ auto stats = _server.getStatsDocument();
+ return sendData("text/javascript", stats.c_str(), stats.length());
+ } else {
+ return sendError(ResponseCode::NotFound, "Unable to find resource for: " + path);
+ }
+}
+
+bool Connection::sendBadRequest(const std::string& reason) {
+ return sendError(ResponseCode::BadRequest, reason);
+}
+
+bool Connection::sendISE(const std::string& error) {
+ return sendError(ResponseCode::InternalServerError, error);
+}
+
+bool Connection::processHeaders(uint8_t* first, uint8_t* last) {
+ // Ideally we'd copy off [first, last] now into a header structure here.
+ // 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);
+
+ LS_ACCESS(_logger, "Request: " << requestLine);
+
+ const char* verbText = shift(requestLine);
+ if (!verbText) {
+ return sendBadRequest("Malformed request line");
+ }
+ auto verb = Request::verb(verbText);
+ if (verb == Request::Verb::Invalid) {
+ return sendBadRequest("Malformed request line");
+ }
+ const char* requestUri = shift(requestLine);
+ if (requestUri == NULL) {
+ return sendBadRequest("Malformed request line");
+ }
+
+ const char* httpVersion = shift(requestLine);
+ if (httpVersion == NULL) {
+ return sendBadRequest("Malformed request line");
+ }
+ if (strcmp(httpVersion, "HTTP/1.1") != 0) {
+ return sendUnsupportedError("Unsupported HTTP version");
+ }
+ if (*requestLine != 0) {
+ return sendBadRequest("Trailing crap after http version");
+ }
+
+ HeaderMap headers(31);
+ while (first < last) {
+ char* colonPos = NULL;
+ char* headerLine = extractLine(first, last, &colonPos);
+ assert(headerLine != NULL);
+ if (colonPos == NULL) {
+ 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")) {
+ LS_INFO(_logger, "Websocket request for " << requestUri << "'");
+ if (verb != Request::Verb::Get) {
+ return sendBadRequest("Non-GET WebSocket request");
+ }
+ _webSocketHandler = _server.getWebSocketHandler(requestUri);
+ if (!_webSocketHandler) {
+ LS_WARNING(_logger, "Couldn't find WebSocket end point for '" << requestUri << "'");
+ return send404();
+ }
+ verb = Request::Verb::WebSocket;
+ }
+
+ _request.reset(new PageRequest(_address, requestUri, verb, std::move(headers)));
+
+ 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);
+ }
+
+ if (_request->contentLength() > MaxBufferSize) {
+ return sendBadRequest("Content length too long");
+ }
+ if (_request->contentLength() == 0) {
+ return handlePageRequest();
+ }
+ _state = BUFFERING_POST_DATA;
+ return true;
+}
+
+bool Connection::handlePageRequest() {
+ std::shared_ptr<Response> response;
+ try {
+ response = _server.handle(*_request);
+ } catch (const std::exception& e) {
+ LS_ERROR(_logger, "page error: " << e.what());
+ return sendISE(e.what());
+ } catch (...) {
+ LS_ERROR(_logger, "page error: (unknown)");
+ return sendISE("(unknown)");
+ }
+ 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());
+ if (!_webSocketHandler) {
+ LS_WARNING(_logger, "Couldn't find WebSocket end point for '" << uri << "'");
+ return send404();
+ }
+ if (webSocketVersion == 0) {
+ // Hixie
+ _state = READING_WEBSOCKET_KEY3;
+ return true;
+ }
+ auto hybiKey = _request->getHeader("Sec-WebSocket-Key");
+ return handleHybiHandshake(webSocketVersion, hybiKey);
+ }
+ return sendResponse(response);
+}
+
+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();
+ }
+ return true;
+}
+
+bool Connection::handleHybiHandshake(
+ int webSocketVersion,
+ const std::string& webSocketKey) {
+ if (webSocketVersion != 8 && webSocketVersion != 13) {
+ return sendBadRequest("Invalid websocket version");
+ }
+ LS_DEBUG(_logger, "Got a hybi-8 websocket with key=" << webSocketKey);
+
+ LS_DEBUG(_logger, "Attempting websocket upgrade");
+
+ bufferResponseAndCommonHeaders(ResponseCode::WebSocketProtocolHandshake);
+ bufferLine("Upgrade: websocket");
+ bufferLine("Connection: Upgrade");
+ bufferLine("Sec-WebSocket-Accept: " + getAcceptKey(webSocketKey));
+ bufferLine("");
+ flush();
+
+ if (_webSocketHandler) {
+ _webSocketHandler->onConnect(this);
+ }
+ _state = HANDLING_HYBI_WEBSOCKET;
+ return true;
+}
+
+bool Connection::parseRange(const std::string& rangeStr, Range& range) const {
+ size_t minusPos = rangeStr.find('-');
+ if (minusPos == std::string::npos) {
+ LS_WARNING(_logger, "Bad range: '" << rangeStr << "'");
+ return false;
+ }
+ if (minusPos == 0) {
+ // A range like "-500" means 500 bytes from end of file to end.
+ range.start = atoi(rangeStr.c_str());
+ 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.end = std::numeric_limits<long>::max();
+ } else {
+ range.end = atoi(rangeStr.substr(minusPos + 1).c_str());
+ }
+ return true;
+ }
+ return false;
+}
+
+bool Connection::parseRanges(const std::string& range, std::list<Range>& ranges) const {
+ static const std::string expectedPrefix = "bytes=";
+ if (range.length() < expectedPrefix.length() || range.substr(0, expectedPrefix.length()) != expectedPrefix) {
+ LS_WARNING(_logger, "Bad range request prefix: '" << range << "'");
+ return false;
+ }
+ auto rangesText = split(range.substr(expectedPrefix.length()), ',');
+ for (auto it = rangesText.begin(); it != rangesText.end(); ++it) {
+ Range r;
+ if (!parseRange(*it, r)) {
+ return false;
+ }
+ ranges.push_back(r);
+ }
+ return !ranges.empty();
+}
+
+// Sends HTTP 200 or 206, content-length, and range info as needed. Returns the actual file ranges
+// needing sending.
+std::list<Connection::Range> Connection::processRangesForStaticData(const std::list<Range>& origRanges, long fileSize) {
+ if (origRanges.empty()) {
+ // Easy case: a non-range request.
+ bufferResponseAndCommonHeaders(ResponseCode::Ok);
+ bufferLine("Content-Length: " + toString(fileSize));
+ return { Range { 0, fileSize - 1 } };
+ }
+
+ // Partial content request.
+ bufferResponseAndCommonHeaders(ResponseCode::PartialContent);
+ int contentLength = 0;
+ std::ostringstream rangeLine;
+ rangeLine << "Content-Range: bytes ";
+ std::list<Range> sendRanges;
+ for (auto rangeIter = origRanges.cbegin(); rangeIter != origRanges.cend(); ++rangeIter) {
+ Range actualRange = *rangeIter;
+ if (actualRange.start < 0) {
+ actualRange.start += fileSize;
+ }
+ if (actualRange.start >= fileSize) {
+ actualRange.start = fileSize - 1;
+ }
+ if (actualRange.end >= fileSize) {
+ actualRange.end = fileSize - 1;
+ }
+ contentLength += actualRange.length();
+ sendRanges.push_back(actualRange);
+ rangeLine << actualRange.start << "-" << actualRange.end;
+ }
+ rangeLine << "/" << fileSize;
+ bufferLine(rangeLine.str());
+ bufferLine("Content-Length: " + toString(contentLength));
+ return sendRanges;
+}
+
+bool Connection::sendStaticData() {
+ // TODO: fold this into the handler way of doing things.
+ std::string path = _server.getStaticPath() + getRequestUri();
+ auto rangeHeader = getHeader("Range");
+ // Trim any trailing queries.
+ size_t queryPos = path.find('?');
+ if (queryPos != path.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) {
+ return send404();
+ }
+ std::list<Range> ranges;
+ if (!rangeHeader.empty() && !parseRanges(rangeHeader, ranges)) {
+ return sendBadRequest("Bad range header");
+ }
+ ranges = processRangesForStaticData(ranges, stat.st_size);
+ bufferLine("Content-Type: " + getContentType(path));
+ bufferLine("Connection: keep-alive");
+ bufferLine("Accept-Ranges: bytes");
+ bufferLine("Last-Modified: " + webtime(stat.st_mtime));
+ if (!isCacheable(path)) {
+ bufferLine("Cache-Control: no-store");
+ bufferLine("Pragma: no-cache");
+ bufferLine("Expires: " + now());
+ }
+ bufferLine("");
+ if (!flush()) {
+ return false;
+ }
+
+ for (auto rangeIter = ranges.cbegin(); rangeIter != ranges.cend(); ++rangeIter) {
+ if (::lseek(input, rangeIter->start, SEEK_SET) == -1) {
+ // We've (probably) already sent data.
+ return false;
+ }
+ auto bytesLeft = rangeIter->length();
+ while (bytesLeft) {
+ char buf[ReadWriteBufferSize];
+ auto bytesRead = ::read(input, buf, std::min(sizeof(buf), bytesLeft));
+ if (bytesRead <= 0) {
+ const static std::string unexpectedEof("Unexpected EOF");
+ LS_ERROR(_logger, "Error reading file: " << (bytesRead == 0 ? unexpectedEof : getLastError()));
+ // We can't send an error document as we've sent the header.
+ return false;
+ }
+ bytesLeft -= bytesRead;
+ if (!write(buf, bytesRead, true)) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+bool Connection::sendData(const std::string& type, const char* start, size_t size) {
+ bufferResponseAndCommonHeaders(ResponseCode::Ok);
+ bufferLine("Content-Type: " + type);
+ bufferLine("Content-Length: " + toString(size));
+ bufferLine("Connection: keep-alive");
+ bufferLine("");
+ bool result = write(start, size, true);
+ return result;
+}
+
+void Connection::bufferResponseAndCommonHeaders(ResponseCode code) {
+ auto responseCodeInt = static_cast<int>(code);
+ auto responseCodeName = ::name(code);
+ auto response = std::string("HTTP/1.1 " + toString(responseCodeInt) + " " + responseCodeName);
+ LS_ACCESS(_logger, "Response: " << response);
+ bufferLine(response);
+ bufferLine("Server: " SEASOCKS_VERSION_STRING);
+ bufferLine("Date: " + now());
+ bufferLine("Access-Control-Allow-Origin: *");
+}
+
+void Connection::setLinger() {
+ if (_fd == -1) {
+ return;
+ }
+ const int secondsToLinger = 1;
+ 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");
+ }
+}
+
+bool Connection::hasHeader(const std::string& header) const {
+ return _request ? _request->hasHeader(header) : false;
+}
+
+std::string Connection::getHeader(const std::string& header) const {
+ return _request ? _request->getHeader(header) : "";
+}
+
+const std::string& Connection::getRequestUri() const {
+ static const std::string empty;
+ return _request ? _request->getRequestUri() : empty;
+}
+
+} // seasocks
diff --git a/src/main/c/HybiAccept.cpp b/src/main/c/HybiAccept.cpp
new file mode 100644
index 0000000..8c2dbde
--- /dev/null
+++ b/src/main/c/HybiAccept.cpp
@@ -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.
+
+#include "internal/Base64.h"
+#include "internal/HybiAccept.h"
+
+#include "sha1/sha1.h"
+
+#include <arpa/inet.h>
+
+namespace seasocks {
+
+static const std::string magicString("258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
+
+std::string getAcceptKey(const std::string& challenge) {
+ auto fullString = challenge + magicString;
+ SHA1 hasher;
+ 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]);
+ }
+ return base64Encode(hash, sizeof(hash));
+}
+
+}
diff --git a/src/main/c/HybiPacketDecoder.cpp b/src/main/c/HybiPacketDecoder.cpp
new file mode 100644
index 0000000..f0fdefe
--- /dev/null
+++ b/src/main/c/HybiPacketDecoder.cpp
@@ -0,0 +1,102 @@
+// Copyright (c) 2013, Matt Godbolt
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+#include "internal/HybiPacketDecoder.h"
+#include "internal/LogStream.h"
+
+#include <arpa/inet.h>
+
+namespace seasocks {
+
+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) {
+ if (_messageStart + 1 >= _buffer.size()) {
+ return 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;
+ }
+ if ((_buffer[_messageStart] & (7<<4)) != 0) {
+ LS_WARNING(&_logger, "Received hybi frame with reserved bits set - error");
+ return Error;
+ }
+ auto opcode = _buffer[_messageStart] & 0xf;
+ size_t payloadLength = _buffer[_messageStart + 1] & 0x7f;
+ 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]));
+ ptr += 2;
+ } else if (payloadLength == 127) {
+ if (_buffer.size() < 10) { return NoMessage; }
+ payloadLength = __bswap_64(*reinterpret_cast<const uint64_t*>(&_buffer[ptr]));
+ 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]));
+ ptr += 4;
+ }
+ auto bytesLeftInBuffer = _buffer.size() - ptr;
+ if (payloadLength > bytesLeftInBuffer) { return 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));
+ }
+ _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;
+ }
+}
+
+size_t HybiPacketDecoder::numBytesDecoded() const {
+ return _messageStart;
+}
+
+}
diff --git a/src/main/c/Logger.cpp b/src/main/c/Logger.cpp
new file mode 100644
index 0000000..347ae30
--- /dev/null
+++ b/src/main/c/Logger.cpp
@@ -0,0 +1,76 @@
+// Copyright (c) 2013, Matt Godbolt
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+#include "internal/Debug.h"
+
+#include "seasocks/Logger.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+
+namespace seasocks {
+
+const int MAX_MESSAGE_LENGTH = 1024;
+
+#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);
+#endif
+}
+
+void Logger::access(const char* message, ...) {
+ PRINT_TO_MESSAGEBUF();
+ log(ACCESS, messageBuf);
+}
+
+void Logger::info(const char* message, ...) {
+ PRINT_TO_MESSAGEBUF();
+ log(INFO, messageBuf);
+}
+
+void Logger::warning(const char* message, ...) {
+ PRINT_TO_MESSAGEBUF();
+ log(WARNING, messageBuf);
+}
+
+void Logger::error(const char* message, ...) {
+ PRINT_TO_MESSAGEBUF();
+ log(ERROR, messageBuf);
+}
+
+void Logger::severe(const char* message, ...) {
+ PRINT_TO_MESSAGEBUF();
+ log(SEVERE, messageBuf);
+}
+
+} // namespace seasocks
diff --git a/src/main/c/PageRequest.cpp b/src/main/c/PageRequest.cpp
new file mode 100644
index 0000000..a2a5313
--- /dev/null
+++ b/src/main/c/PageRequest.cpp
@@ -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.
+
+#include "internal/PageRequest.h"
+
+#include <cstdlib>
+#include <cstring>
+
+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")) {
+}
+
+bool PageRequest::consumeContent(std::vector<uint8_t>& buffer) {
+ if (buffer.size() < _contentLength) return false;
+ if (buffer.size() == _contentLength) {
+ _content.swap(buffer);
+ } else {
+ _content.assign(buffer.begin(), buffer.begin() + _contentLength);
+ buffer.erase(buffer.begin(), buffer.begin() + _contentLength);
+ }
+ 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());
+}
+
+} // namespace seasocks
diff --git a/src/main/c/Response.cpp b/src/main/c/Response.cpp
new file mode 100644
index 0000000..2eddfe2
--- /dev/null
+++ b/src/main/c/Response.cpp
@@ -0,0 +1,63 @@
+// Copyright (c) 2013, Matt Godbolt
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+#include "internal/ConcreteResponse.h"
+
+#include "seasocks/Response.h"
+
+using namespace seasocks;
+
+namespace seasocks {
+
+std::shared_ptr<Response> Response::unhandled() {
+ static std::shared_ptr<Response> unhandled;
+ return unhandled;
+}
+
+std::shared_ptr<Response> Response::notFound() {
+ static std::shared_ptr<Response> notFound(new ConcreteResponse(ResponseCode::NotFound, "Not found", "text/plain", Response::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::textResponse(const std::string& response) {
+ return std::shared_ptr<Response>(
+ new ConcreteResponse(ResponseCode::Ok, response, "text/plain", Response::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));
+}
+
+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));
+}
+
+}
diff --git a/src/main/c/Server.cpp b/src/main/c/Server.cpp
new file mode 100644
index 0000000..5ad1c30
--- /dev/null
+++ b/src/main/c/Server.cpp
@@ -0,0 +1,568 @@
+// Copyright (c) 2013, Matt Godbolt
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+#include "internal/LogStream.h"
+
+#include "seasocks/Connection.h"
+#include "seasocks/Logger.h"
+#include "seasocks/Server.h"
+#include "seasocks/PageHandler.h"
+#include "seasocks/StringUtil.h"
+#include "seasocks/util/Json.h"
+
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+
+#include <sys/epoll.h>
+#include <sys/eventfd.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/syscall.h>
+
+#include <memory>
+#include <stdexcept>
+#include <string.h>
+#include <unistd.h>
+
+namespace {
+
+struct EventBits {
+ uint32_t bits;
+ explicit EventBits(uint32_t bits) : bits(bits) {}
+};
+
+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)
+ DO_BIT(EPOLLIN);
+ DO_BIT(EPOLLPRI);
+ DO_BIT(EPOLLOUT);
+ DO_BIT(EPOLLRDNORM);
+ DO_BIT(EPOLLRDBAND);
+ DO_BIT(EPOLLWRNORM);
+ DO_BIT(EPOLLWRBAND);
+ DO_BIT(EPOLLMSG);
+ DO_BIT(EPOLLERR);
+ DO_BIT(EPOLLHUP);
+#ifdef EPOLLRDHUP
+ DO_BIT(EPOLLRDHUP);
+#endif
+ DO_BIT(EPOLLONESHOT);
+ DO_BIT(EPOLLET);
+#undef DO_BIT
+ return o;
+}
+
+const int EpollTimeoutMillis = 500; // Twice a second is ample.
+const int DefaultLameConnectionTimeoutSeconds = 10;
+int gettid() {
+ return syscall(SYS_gettid);
+}
+
+}
+
+namespace seasocks {
+
+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) {
+
+ _epollFd = epoll_create(10);
+ if (_epollFd == -1) {
+ LS_ERROR(_logger, "Unable to create epoll: " << getLastError());
+ return;
+ }
+
+ _eventFd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
+ if (_eventFd == -1) {
+ LS_ERROR(_logger, "Unable to create event FD: " << getLastError());
+ return;
+ }
+
+ 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;
+ }
+}
+
+Server::~Server() {
+ LS_INFO(_logger, "Server destruction");
+ shutdown();
+ // Only shut the eventfd and epoll at the very end
+ if (_eventFd != -1) {
+ close(_eventFd);
+ }
+ if (_epollFd != -1) {
+ close(_epollFd);
+ }
+}
+
+void Server::shutdown() {
+ // Stop listening to any further incoming connections.
+ if (_listenSock != -1) {
+ close(_listenSock);
+ _listenSock = -1;
+ }
+ // Disconnect and close any current connections.
+ while (!_connections.empty()) {
+ // Deleting the connection closes it and removes it from 'this'.
+ Connection* toBeClosed = _connections.begin()->first;
+ toBeClosed->setLinger();
+ delete toBeClosed;
+ }
+}
+
+bool Server::makeNonBlocking(int fd) const {
+ int yesPlease = 1;
+ if (ioctl(fd, FIONBIO, &yesPlease) != 0) {
+ LS_ERROR(_logger, "Unable to make FD non-blocking: " << getLastError());
+ return false;
+ }
+ return true;
+}
+
+bool Server::configureSocket(int fd) const {
+ if (!makeNonBlocking(fd)) {
+ return false;
+ }
+ const int yesPlease = 1;
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yesPlease, sizeof(yesPlease)) == -1) {
+ LS_ERROR(_logger, "Unable to set reuse socket option: " << getLastError());
+ return false;
+ }
+ if (_maxKeepAliveDrops > 0) {
+ if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &yesPlease, sizeof(yesPlease)) == -1) {
+ LS_ERROR(_logger, "Unable to enable keepalive: " << getLastError());
+ return false;
+ }
+ const int oneSecond = 1;
+ if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &oneSecond, sizeof(oneSecond)) == -1) {
+ LS_ERROR(_logger, "Unable to set idle probe: " << getLastError());
+ return false;
+ }
+ if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &oneSecond, sizeof(oneSecond)) == -1) {
+ LS_ERROR(_logger, "Unable to set idle interval: " << getLastError());
+ return false;
+ }
+ if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &_maxKeepAliveDrops, sizeof(_maxKeepAliveDrops)) == -1) {
+ LS_ERROR(_logger, "Unable to set keep alive count: " << getLastError());
+ return false;
+ }
+ }
+ return true;
+}
+
+void Server::terminate() {
+ _expectedTerminate = true;
+ _terminate = true;
+ uint64_t one = 1;
+ if (_eventFd != -1 && ::write(_eventFd, &one, sizeof(one)) == -1) {
+ LS_ERROR(_logger, "Unable to post a wake event: " << getLastError());
+ }
+}
+
+bool Server::startListening(int port) {
+ return startListening(INADDR_ANY, port);
+}
+
+bool Server::startListening(uint32_t hostAddr, int port) {
+ if (_epollFd == -1 || _eventFd == -1) {
+ LS_ERROR(_logger, "Unable to serve, did not initialize properly.");
+ return false;
+ }
+
+ _listenSock = socket(AF_INET, SOCK_STREAM, 0);
+ if (_listenSock == -1) {
+ LS_ERROR(_logger, "Unable to create listen socket: " << getLastError());
+ return false;
+ }
+ if (!configureSocket(_listenSock)) {
+ return false;
+ }
+ sockaddr_in sock;
+ memset(&sock, 0, sizeof(sock));
+ sock.sin_port = htons(port);
+ sock.sin_addr.s_addr = htonl(hostAddr);
+ sock.sin_family = AF_INET;
+ if (bind(_listenSock, reinterpret_cast<const sockaddr*>(&sock), sizeof(sock)) == -1) {
+ LS_ERROR(_logger, "Unable to bind socket: " << getLastError());
+ return false;
+ }
+ if (listen(_listenSock, 5) == -1) {
+ LS_ERROR(_logger, "Unable to listen on 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 listen socket to epoll: " << getLastError());
+ return false;
+ }
+
+ char buf[1024];
+ ::gethostname(buf, sizeof(buf));
+ LS_INFO(_logger, "Listening on http://" << buf << ":" << port << "/");
+
+ return true;
+}
+
+void Server::handlePipe() {
+ uint64_t dummy;
+ while (::read(_eventFd, &dummy, sizeof(dummy)) != -1) {
+ // Spin, draining the pipe until it returns EWOULDBLOCK or similar.
+ }
+ if (errno != EAGAIN || errno != EWOULDBLOCK) {
+ LS_ERROR(_logger, "Error from wakeFd read: " << getLastError());
+ _terminate = true;
+ }
+ // It's a "wake up" event; this will just cause the epoll loop to wake up.
+}
+
+Server::NewState Server::handleConnectionEvents(Connection* connection, uint32_t events) {
+ if (events & ~(EPOLLIN|EPOLLOUT|EPOLLHUP|EPOLLERR)) {
+ LS_WARNING(_logger, "Got unhandled epoll event (" << EventBits(events) << ") on connection: "
+ << formatAddress(connection->getRemoteAddress()));
+ return Close;
+ } else if (events & EPOLLERR) {
+ LS_INFO(_logger, "Error on socket (" << EventBits(events) << "): "
+ << formatAddress(connection->getRemoteAddress()));
+ return Close;
+ } else if (events & EPOLLHUP) {
+ LS_DEBUG(_logger, "Graceful hang-up (" << EventBits(events) << ") of socket: "
+ << formatAddress(connection->getRemoteAddress()));
+ return Close;
+ } else {
+ if (events & EPOLLOUT) {
+ connection->handleDataReadyForWrite();
+ }
+ if (events & EPOLLIN) {
+ connection->handleDataReadyForRead();
+ }
+ }
+ return KeepOpen;
+}
+
+void Server::checkAndDispatchEpoll(int epollMillis) {
+ const int maxEvents = 256;
+ epoll_event events[maxEvents];
+
+ std::list<Connection*> toBeDeleted;
+ int numEvents = epoll_wait(_epollFd, events, maxEvents, epollMillis);
+ if (numEvents == -1) {
+ if (errno != EINTR) {
+ LS_ERROR(_logger, "Error from epoll_wait: " << getLastError());
+ }
+ return;
+ }
+ if (numEvents == maxEvents) {
+ static time_t lastWarnTime = 0;
+ time_t now = time(NULL);
+ if (now - lastWarnTime >= 60) {
+ LS_WARNING(_logger, "Full event queue; may start starving connections. "
+ "Will warn at most once a minute");
+ lastWarnTime = now;
+ }
+ }
+ for (int i = 0; i < numEvents; ++i) {
+ 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");
+ _terminate = true;
+ break;
+ }
+ handleAccept();
+ } 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");
+ _terminate = true;
+ break;
+ }
+ handlePipe();
+ } else {
+ auto connection = reinterpret_cast<Connection*>(events[i].data.ptr);
+ if (handleConnectionEvents(connection, events[i].events) == 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;
+ if (_connections.find(connection) == _connections.end()) {
+ LS_SEVERE(_logger, "Attempt to delete connection we didn't know about: " << (void*)connection
+ << formatAddress(connection->getRemoteAddress()));
+ _terminate = true;
+ break;
+ }
+ LS_DEBUG(_logger, "Deleting connection: " << formatAddress(connection->getRemoteAddress()));
+ delete connection;
+ }
+}
+
+void Server::setStaticPath(const char* staticPath) {
+ LS_INFO(_logger, "Serving content from " << staticPath);
+ _staticPath = staticPath;
+}
+
+bool Server::serve(const char* staticPath, int port) {
+ setStaticPath(staticPath);
+ if (!startListening(port)) {
+ return false;
+ }
+
+ return loop();
+}
+
+bool Server::loop() {
+ if (_listenSock == -1) {
+ LS_ERROR(_logger, "Server not initialised");
+ return false;
+ }
+
+ // Stash away "the" server thread id.
+ _threadId = gettid();
+
+ while (!_terminate) {
+ // Always process events first to catch start up events.
+ processEventQueue();
+ checkAndDispatchEpoll(EpollTimeoutMillis);
+ }
+ // Reasonable effort to ensure anything enqueued during terminate has a chance to run.
+ processEventQueue();
+ LS_INFO(_logger, "Server terminating");
+ shutdown();
+ return _expectedTerminate;
+}
+
+Server::PollResult Server::poll(int millis) {
+ // Grab the thread ID on the first poll.
+ if (_threadId == 0) _threadId = gettid();
+ if (_threadId != gettid()) {
+ LS_ERROR(_logger, "poll() called from the wrong thread");
+ return PollResult::Error;
+ }
+ if (_listenSock == -1) {
+ LS_ERROR(_logger, "Server not initialised");
+ return PollResult::Error;
+ }
+ processEventQueue();
+ checkAndDispatchEpoll(millis);
+ if (!_terminate) return PollResult::Continue;
+
+ // Reasonable effort to ensure anything enqueued during terminate has a chance to run.
+ processEventQueue();
+ LS_INFO(_logger, "Server terminating");
+ shutdown();
+
+ return _expectedTerminate ? PollResult::Terminated : PollResult::Error;
+}
+
+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;
+ }
+ }
+}
+
+void Server::handleAccept() {
+ sockaddr_in address;
+ socklen_t addrLen = sizeof(address);
+ int fd = ::accept(_listenSock,
+ reinterpret_cast<sockaddr*>(&address),
+ &addrLen);
+ if (fd == -1) {
+ LS_ERROR(_logger, "Unable to accept: " << getLastError());
+ return;
+ }
+ if (!configureSocket(fd)) {
+ ::close(fd);
+ return;
+ }
+ LS_INFO(_logger, formatAddress(address) << " : Accepted on descriptor " << fd);
+ Connection* newConnection = new Connection(_logger, *this, fd, address);
+ 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)));
+}
+
+void Server::remove(Connection* connection) {
+ checkThread();
+ 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());
+ }
+ _connections.erase(connection);
+}
+
+bool Server::subscribeToWriteEvents(Connection* 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;
+ }
+ return true;
+}
+
+bool Server::unsubscribeFromWriteEvents(Connection* 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;
+ }
+ return true;
+}
+
+void Server::addWebSocketHandler(const char* endpoint, std::shared_ptr<WebSocket::Handler> handler,
+ 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 {
+ auto splits = split(endpoint, '?');
+ auto iter = _webSocketHandlerMap.find(splits[0]);
+ if (iter == _webSocketHandlerMap.end()) {
+ return false;
+ }
+ return iter->second.allowCrossOrigin;
+}
+
+std::shared_ptr<WebSocket::Handler> Server::getWebSocketHandler(const char* endpoint) const {
+ auto splits = split(endpoint, '?');
+ auto iter = _webSocketHandlerMap.find(splits[0]);
+ if (iter == _webSocketHandlerMap.end()) {
+ return std::shared_ptr<WebSocket::Handler>();
+ }
+ return iter->second.handler;
+}
+
+void Server::execute(std::shared_ptr<Runnable> runnable) {
+ std::unique_lock<decltype(_pendingRunnableMutex)> lock(_pendingRunnableMutex);
+ _pendingRunnables.push_back(runnable);
+ lock.unlock();
+
+ uint64_t one = 1;
+ if (_eventFd != -1 && ::write(_eventFd, &one, sizeof(one)) == -1) {
+ if (errno != EAGAIN && errno != EWOULDBLOCK) {
+ LS_ERROR(_logger, "Unable to post a wake event: " << getLastError());
+ }
+ }
+}
+
+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 << "connection({";
+ auto connection = it->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;
+ }
+ return doc.str();
+}
+
+void Server::setLameConnectionTimeoutSeconds(int seconds) {
+ LS_INFO(_logger, "Setting lame connection timeout to " << seconds);
+ _lameConnectionTimeoutSeconds = seconds;
+}
+
+void Server::setMaxKeepAliveDrops(int maxKeepAliveDrops) {
+ LS_INFO(_logger, "Setting max keep alive drops to " << maxKeepAliveDrops);
+ _maxKeepAliveDrops = maxKeepAliveDrops;
+}
+
+void Server::checkThread() const {
+ auto thisTid = gettid();
+ if (thisTid != _threadId) {
+ std::ostringstream o;
+ o << "seasocks called on wrong thread : " << thisTid << " instead of " << _threadId;
+ LS_SEVERE(_logger, o.str());
+ throw std::runtime_error(o.str());
+ }
+}
+
+std::shared_ptr<Response> Server::handle(const Request &request) {
+ for (auto handler : _pageHandlers) {
+ auto result = handler->handle(request);
+ if (result != Response::unhandled()) return result;
+ }
+ return Response::unhandled();
+}
+
+} // namespace seasocks
diff --git a/src/main/c/StringUtil.cpp b/src/main/c/StringUtil.cpp
new file mode 100644
index 0000000..b7177ce
--- /dev/null
+++ b/src/main/c/StringUtil.cpp
@@ -0,0 +1,109 @@
+// 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/StringUtil.h"
+
+#include <cctype>
+#include <cerrno>
+#include <cstddef>
+#include <cstdio>
+#include <cstring>
+
+namespace seasocks {
+
+char* skipWhitespace(char* str) {
+ while (isspace(*str)) ++str;
+ return str;
+}
+
+char* skipNonWhitespace(char* str) {
+ while (*str && !isspace(*str)) {
+ ++str;
+ }
+ return str;
+}
+
+char* shift(char*& str) {
+ if (str == NULL) {
+ return NULL;
+ }
+ char* startOfWord = skipWhitespace(str);
+ if (*startOfWord == 0) {
+ str = startOfWord;
+ return NULL;
+ }
+ char* endOfWord = skipNonWhitespace(startOfWord);
+ if (*endOfWord != 0) {
+ *endOfWord++ = 0;
+ }
+ str = endOfWord;
+ return startOfWord;
+}
+
+std::string getLastError(){
+ char errbuf[1024];
+ return strerror_r(errno, errbuf, sizeof(errbuf));
+}
+
+std::string formatAddress(const sockaddr_in& address) {
+ char ipBuffer[24];
+ sprintf(ipBuffer,
+ "%d.%d.%d.%d:%d",
+ (address.sin_addr.s_addr >> 0) & 0xff,
+ (address.sin_addr.s_addr >> 8) & 0xff,
+ (address.sin_addr.s_addr >> 16) & 0xff,
+ (address.sin_addr.s_addr >> 24) & 0xff,
+ htons(address.sin_port));
+ return ipBuffer;
+}
+
+std::vector<std::string> split(const std::string& input, char splitChar) {
+ if (input.empty()) return std::vector<std::string>();
+ std::vector<std::string> result;
+ size_t pos = 0;
+ size_t newPos;
+ while ((newPos = input.find(splitChar, pos)) != std::string::npos) {
+ result.push_back(input.substr(pos, newPos - pos));
+ pos = newPos + 1;
+ }
+ result.push_back(input.substr(pos));
+ return result;
+}
+
+void replace(std::string& string, const std::string& find, const std::string& replace) {
+ size_t pos = 0;
+ const size_t findLen = find.length();
+ const size_t replaceLen = replace.length();
+ while ((pos = string.find(find, pos)) != std::string::npos) {
+ string = string.substr(0, pos) + replace + string.substr(pos + findLen);
+ pos += replaceLen;
+ }
+}
+
+bool caseInsensitiveSame(const std::string &lhs, const std::string &rhs) {
+ return strcasecmp(lhs.c_str(), rhs.c_str()) == 0;
+}
+
+}
diff --git a/src/main/c/internal/.gitignore b/src/main/c/internal/.gitignore
new file mode 100644
index 0000000..40ee485
--- /dev/null
+++ b/src/main/c/internal/.gitignore
@@ -0,0 +1 @@
+/Config.h
diff --git a/src/main/c/internal/Base64.cpp b/src/main/c/internal/Base64.cpp
new file mode 100644
index 0000000..3b1cf20
--- /dev/null
+++ b/src/main/c/internal/Base64.cpp
@@ -0,0 +1,50 @@
+// Copyright (c) 2013, Matt Godbolt
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+#include "internal/Base64.h"
+
+#include <cstdint>
+
+namespace seasocks {
+
+const char cb64[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+std::string base64Encode(const void* dataVoid, size_t length) {
+ std::string output;
+ auto data = reinterpret_cast<const uint8_t*>(dataVoid);
+ 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;
+ 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)] : '='));
+ output.push_back((bytesLeft > 2 ? cb64[b2 & 0x3f] : '='));
+ }
+ return output;
+}
+
+} // namespace seasocks
diff --git a/src/main/c/internal/Base64.h b/src/main/c/internal/Base64.h
new file mode 100644
index 0000000..b6dc014
--- /dev/null
+++ b/src/main/c/internal/Base64.h
@@ -0,0 +1,34 @@
+// 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 {
+
+extern std::string base64Encode(const void* data, size_t length);
+
+}
diff --git a/src/main/c/internal/ConcreteResponse.h b/src/main/c/internal/ConcreteResponse.h
new file mode 100644
index 0000000..d4a68b3
--- /dev/null
+++ b/src/main/c/internal/ConcreteResponse.h
@@ -0,0 +1,67 @@
+// 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"
+
+namespace seasocks {
+
+class ConcreteResponse : public Response {
+ 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 {
+ return _responseCode;
+ }
+
+ virtual const char* payload() const {
+ return _payload.c_str();
+ }
+
+ virtual size_t payloadSize() const {
+ return _payload.size();
+ }
+
+ virtual bool keepConnectionAlive() const {
+ return _keepAlive;
+ }
+
+ virtual std::string contentType() const {
+ return _contentType;
+ }
+
+ virtual Headers getAdditionalHeaders() const {
+ return _headers;
+ }
+};
+
+}
diff --git a/src/main/c/internal/Config.h.in b/src/main/c/internal/Config.h.in
new file mode 100644
index 0000000..48c5421
--- /dev/null
+++ b/src/main/c/internal/Config.h.in
@@ -0,0 +1,219 @@
+/* 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
new file mode 100644
index 0000000..304e141
--- /dev/null
+++ b/src/main/c/internal/Debug.h
@@ -0,0 +1,29 @@
+// 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
+
+// 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
new file mode 100644
index 0000000..5741c62
--- /dev/null
+++ b/src/main/c/internal/Embedded.h
@@ -0,0 +1,35 @@
+// 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>
+
+struct EmbeddedContent {
+ 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
new file mode 100644
index 0000000..ba5ed35
--- /dev/null
+++ b/src/main/c/internal/HeaderMap.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 <cctype>
+#include <cstring>
+#include <string>
+#include <unordered_map>
+
+namespace seasocks {
+
+struct CaseInsensitiveHash {
+ size_t operator()(const std::string &string) const {
+ size_t h = 0;
+ for (auto c: string) {
+ h = h * 13 + tolower(c);
+ }
+ return h;
+ }
+};
+
+struct CaseInsensitiveComparison {
+ 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>;
+
+}
diff --git a/src/main/c/internal/HybiAccept.h b/src/main/c/internal/HybiAccept.h
new file mode 100644
index 0000000..d02833b
--- /dev/null
+++ b/src/main/c/internal/HybiAccept.h
@@ -0,0 +1,34 @@
+// 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 {
+
+extern std::string getAcceptKey(const std::string& challenge);
+
+}
diff --git a/src/main/c/internal/HybiPacketDecoder.h b/src/main/c/internal/HybiPacketDecoder.h
new file mode 100644
index 0000000..528bbf9
--- /dev/null
+++ b/src/main/c/internal/HybiPacketDecoder.h
@@ -0,0 +1,65 @@
+// 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 <memory>
+#include <string>
+#include <vector>
+
+namespace seasocks {
+
+class HybiPacketDecoder {
+ 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 MessageState {
+ NoMessage,
+ TextMessage,
+ BinaryMessage,
+ Error,
+ Ping,
+ Close
+ };
+ MessageState decodeNextMessage(std::vector<uint8_t>& messageOut);
+
+ size_t numBytesDecoded() const;
+};
+
+}
diff --git a/src/main/c/internal/LogStream.h b/src/main/c/internal/LogStream.h
new file mode 100644
index 0000000..16612ba
--- /dev/null
+++ b/src/main/c/internal/LogStream.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 "internal/Debug.h"
+
+// 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_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
new file mode 100644
index 0000000..6fea7fb
--- /dev/null
+++ b/src/main/c/internal/PageRequest.h
@@ -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.
+
+#pragma once
+
+#include "internal/HeaderMap.h"
+#include "seasocks/Request.h"
+
+#include <unordered_map>
+#include <vector>
+
+namespace seasocks {
+
+class PageRequest : public Request {
+ std::shared_ptr<Credentials> _credentials;
+ const sockaddr_in _remoteAddress;
+ const std::string _requestUri;
+ const Verb _verb;
+ std::vector<uint8_t> _content;
+ HeaderMap _headers;
+ const size_t _contentLength;
+
+public:
+ PageRequest(
+ const sockaddr_in& remoteAddress,
+ const std::string& requestUri,
+ Verb verb,
+ HeaderMap&& headers);
+
+ virtual Verb verb() const {
+ return _verb;
+ }
+
+ virtual std::shared_ptr<Credentials> credentials() const {
+ return _credentials;
+ }
+
+ virtual const sockaddr_in& getRemoteAddress() const {
+ return _remoteAddress;
+ }
+
+ virtual const std::string& getRequestUri() const {
+ return _requestUri;
+ }
+
+ virtual size_t contentLength() const {
+ return _contentLength;
+ }
+
+ virtual const uint8_t* content() const {
+ return _contentLength > 0 ? &_content[0] : NULL;
+ }
+
+ virtual bool hasHeader(const std::string& name) const {
+ return _headers.find(name) != _headers.end();
+ }
+
+ virtual std::string getHeader(const std::string& name) const {
+ 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;
+};
+
+} // namespace seasocks
diff --git a/src/main/c/internal/Version.h b/src/main/c/internal/Version.h
new file mode 100644
index 0000000..366f8ea
--- /dev/null
+++ b/src/main/c/internal/Version.h
@@ -0,0 +1,31 @@
+// Copyright (c) 2013, Matt Godbolt
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+#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
diff --git a/src/main/c/md5/md5.cpp b/src/main/c/md5/md5.cpp
new file mode 100644
index 0000000..4ad3606
--- /dev/null
+++ b/src/main/c/md5/md5.cpp
@@ -0,0 +1,416 @@
+// 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.
+
+/*
+ Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved.
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ L. Peter Deutsch
+ ghost@aladdin.com
+
+ */
+/* $Id: md5.c,v 1.6 2002/04/13 19:20:28 lpd Exp $ */
+/*
+ Independent implementation of MD5 (RFC 1321).
+
+ This code implements the MD5 Algorithm defined in RFC 1321, whose
+ text is available at
+ http://www.ietf.org/rfc/rfc1321.txt
+ The code is derived from the text of the RFC, including the test suite
+ (section A.5) but excluding the rest of Appendix A. It does not include
+ any code or documentation that is identified in the RFC as being
+ copyrighted.
+
+ The original and principal author of md5.c is L. Peter Deutsch
+ <ghost@aladdin.com>. Other authors are noted in the change history
+ that follows (in reverse chronological order):
+
+ 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order
+ either statically or dynamically; added missing #include <string.h>
+ in library.
+ 2002-03-11 lpd Corrected argument list for main(), and added int return
+ type, in test program and T value program.
+ 2002-02-21 lpd Added missing #include <stdio.h> in test program.
+ 2000-07-03 lpd Patched to eliminate warnings about "constant is
+ unsigned in ANSI C, signed in traditional"; made test program
+ self-checking.
+ 1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+ 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5).
+ 1999-05-03 lpd Original version.
+ */
+
+#include "md5/md5.h"
+
+#include <string.h>
+
+#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)
+#else
+# define BYTE_ORDER 0
+#endif
+
+#define T_MASK ((md5_word_t)~0)
+#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87)
+#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9)
+#define T3 0x242070db
+#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111)
+#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050)
+#define T6 0x4787c62a
+#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec)
+#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe)
+#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 T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c)
+#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71)
+#define T16 0x49b40821
+#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d)
+#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf)
+#define T19 0x265e5a51
+#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855)
+#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2)
+#define T22 0x02441453
+#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e)
+#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437)
+#define T25 0x21e1cde6
+#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829)
+#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278)
+#define T28 0x455a14ed
+#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa)
+#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07)
+#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 T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3)
+#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb)
+#define T38 0x4bdecfa9
+#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f)
+#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f)
+#define T41 0x289b7ec6
+#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805)
+#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a)
+#define T44 0x04881d05
+#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6)
+#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a)
+#define T47 0x1fa27cf8
+#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a)
+#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb)
+#define T50 0x432aff97
+#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58)
+#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6)
+#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 T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f)
+#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb)
+#define T60 0x4e0811a1
+#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d)
+#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca)
+#define T63 0x2ad7d2bb
+#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e)
+
+
+static void
+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];
+ md5_word_t t;
+#if BYTE_ORDER > 0
+ /* Define storage only for big-endian CPUs. */
+ md5_word_t X[16];
+#else
+ /* Define storage for little-endian or both types of CPUs. */
+ md5_word_t xbuf[16];
+ 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;
+
+ if (*((const md5_byte_t *)&w)) /* dynamic little-endian */
+#endif
+#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;
+ }
+ }
+#endif
+#if BYTE_ORDER == 0
+ else /* dynamic big-endian */
+#endif
+#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;
+
+# 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
+ }
+
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
+
+ /* Round 1. */
+ /* 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
+ /* 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(c, d, a, b, 10, 17, T11);
+ SET(b, c, d, a, 11, 22, T12);
+ 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
+ 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);
+ 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(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, 12, 20, T32);
+#undef SET
+
+ /* 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);
+ 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(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(d, a, b, c, 12, 11, T46);
+ SET(c, d, a, b, 15, 16, T47);
+ SET(b, c, d, a, 2, 23, T48);
+#undef SET
+
+ /* 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);
+ 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(c, d, a, b, 10, 15, T55);
+ 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(b, c, d, a, 13, 21, T60);
+ 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);
+#undef SET
+
+ /* 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;
+ pms->abcd[1] += b;
+ pms->abcd[2] += c;
+ pms->abcd[3] += d;
+}
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+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;
+ pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301;
+ pms->abcd[3] = 0x10325476;
+}
+
+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;
+
+ /* Update the message length. */
+ pms->count[1] += nbytes >> 29;
+ pms->count[0] += nbits;
+ if (pms->count[0] < nbits)
+ pms->count[1]++;
+
+ /* Process an initial partial block. */
+ if (offset) {
+ 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);
+ }
+
+ /* Process full blocks. */
+ for (; left >= 64; p += 64, left -= 64)
+ md5_process(pms, p);
+
+ /* Process a final partial block. */
+ if (left)
+ memcpy(pms->buf, p, left);
+}
+
+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
+ };
+ 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));
+ /* 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));
+}
+
+#ifdef __cplusplus
+} /* end extern "C" */
+#endif
diff --git a/src/main/c/md5/md5.h b/src/main/c/md5/md5.h
new file mode 100644
index 0000000..3b88587
--- /dev/null
+++ b/src/main/c/md5/md5.h
@@ -0,0 +1,116 @@
+// 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.
+
+/*
+ Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved.
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ L. Peter Deutsch
+ ghost@aladdin.com
+
+ */
+/* $Id: md5.h,v 1.4 2002/04/13 19:20:28 lpd Exp $ */
+/*
+ Independent implementation of MD5 (RFC 1321).
+
+ This code implements the MD5 Algorithm defined in RFC 1321, whose
+ text is available at
+ http://www.ietf.org/rfc/rfc1321.txt
+ The code is derived from the text of the RFC, including the test suite
+ (section A.5) but excluding the rest of Appendix A. It does not include
+ any code or documentation that is identified in the RFC as being
+ copyrighted.
+
+ The original and principal author of md5.h is L. Peter Deutsch
+ <ghost@aladdin.com>. Other authors are noted in the change history
+ that follows (in reverse chronological order):
+
+ 2002-04-13 lpd Removed support for non-ANSI compilers; removed
+ references to Ghostscript; clarified derivation from RFC 1321;
+ now handles byte order either statically or dynamically.
+ 1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+ 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5);
+ added conditionalization for C++ compilation from Martin
+ Purschke <purschke@bnl.gov>.
+ 1999-05-03 lpd Original version.
+ */
+
+#ifndef md5_INCLUDED
+# define md5_INCLUDED
+
+/*
+ * This package supports both compile-time and run-time determination of CPU
+ * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be
+ * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is
+ * defined as non-zero, the code will be compiled to run only on big-endian
+ * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to
+ * run on either big- or little-endian CPUs, but will run slightly less
+ * 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 */
+
+/* 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_state_t;
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/* Initialize the algorithm. */
+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);
+
+/* Finish the message and return the digest. */
+void md5_finish(md5_state_t *pms, md5_byte_t digest[16]);
+
+#ifdef __cplusplus
+} /* end extern "C" */
+#endif
+
+#endif /* md5_INCLUDED */
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;
+ }
+};
+
+}
diff --git a/src/main/c/sha1/Makefile b/src/main/c/sha1/Makefile
new file mode 100644
index 0000000..66aaf2b
--- /dev/null
+++ b/src/main/c/sha1/Makefile
@@ -0,0 +1,41 @@
+#
+# Makefile
+#
+# Copyright (C) 1998, 2009
+# Paul E. Jones <paulej@packetizer.com>
+# All Rights Reserved.
+#
+#############################################################################
+# $Id: Makefile 12 2009-06-22 19:34:25Z paulej $
+#############################################################################
+#
+# Description:
+# This is a makefile for UNIX to build the programs sha, shacmp, and
+# shatest
+#
+#
+
+CC = g++
+
+CFLAGS = -c -O2 -Wall -D_FILE_OFFSET_BITS=64
+
+LIBS =
+
+OBJS = sha1.o
+
+all: sha shacmp shatest
+
+sha: sha.o $(OBJS)
+ $(CC) -o $@ sha.o $(OBJS) $(LIBS)
+
+shacmp: shacmp.o $(OBJS)
+ $(CC) -o $@ shacmp.o $(OBJS) $(LIBS)
+
+shatest: shatest.o $(OBJS)
+ $(CC) -o $@ shatest.o $(OBJS) $(LIBS)
+
+%.o: %.cpp
+ $(CC) $(CFLAGS) -o $@ $<
+
+clean:
+ $(RM) *.o sha shacmp shatest
diff --git a/src/main/c/sha1/Makefile.nt b/src/main/c/sha1/Makefile.nt
new file mode 100644
index 0000000..9bacf64
--- /dev/null
+++ b/src/main/c/sha1/Makefile.nt
@@ -0,0 +1,48 @@
+#
+# Makefile.nt
+#
+# Copyright (C) 1998, 2009
+# Paul E. Jones <paulej@packetizer.com>
+# All Rights Reserved.
+#
+#############################################################################
+# $Id: Makefile.nt 13 2009-06-22 20:20:32Z paulej $
+#############################################################################
+#
+# Description:
+# This is a makefile for Win32 to build the programs sha, shacmp, and
+# shatest
+#
+# Portability Issues:
+# Designed to work with Visual C++
+#
+#
+
+.silent:
+
+!include <win32.mak>
+
+RM = del /q
+
+LIBS = $(conlibs) setargv.obj
+
+CFLAGS = -D _CRT_SECURE_NO_WARNINGS /EHsc /O2 /W3
+
+OBJS = sha1.obj
+
+all: sha.exe shacmp.exe shatest.exe
+
+sha.exe: sha.obj $(OBJS)
+ $(link) $(conflags) -out:$@ sha.obj $(OBJS) $(LIBS)
+
+shacmp.exe: shacmp.obj $(OBJS)
+ $(link) $(conflags) -out:$@ shacmp.obj $(OBJS) $(LIBS)
+
+shatest.exe: shatest.obj $(OBJS)
+ $(link) $(conflags) -out:$@ shatest.obj $(OBJS) $(LIBS)
+
+.cpp.obj:
+ $(cc) $(CFLAGS) $(cflags) $(cvars) $<
+
+clean:
+ $(RM) *.obj sha.exe shacmp.exe shatest.exe
diff --git a/src/main/c/sha1/license.txt b/src/main/c/sha1/license.txt
new file mode 100644
index 0000000..8d7f394
--- /dev/null
+++ b/src/main/c/sha1/license.txt
@@ -0,0 +1,14 @@
+Copyright (C) 1998, 2009
+Paul E. Jones <paulej@packetizer.com>
+
+Freeware Public License (FPL)
+
+This software is licensed as "freeware." Permission to distribute
+this software in source and binary forms, including incorporation
+into other products, is hereby granted without a fee. THIS SOFTWARE
+IS PROVIDED 'AS IS' AND WITHOUT ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHOR SHALL NOT BE HELD
+LIABLE FOR ANY DAMAGES RESULTING FROM THE USE OF THIS SOFTWARE, EITHER
+DIRECTLY OR INDIRECTLY, INCLUDING, BUT NOT LIMITED TO, LOSS OF DATA
+OR DATA BEING RENDERED INACCURATE.
diff --git a/src/main/c/sha1/sha1.cpp b/src/main/c/sha1/sha1.cpp
new file mode 100644
index 0000000..9a42766
--- /dev/null
+++ b/src/main/c/sha1/sha1.cpp
@@ -0,0 +1,615 @@
+// 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.
+
+// DO NOT REFORMAT <- for tidy
+/*
+ * sha1.cpp
+ *
+ * Copyright (C) 1998, 2009
+ * Paul E. Jones <paulej@packetizer.com>
+ * All Rights Reserved.
+ *
+ *****************************************************************************
+ * $Id: sha1.cpp 12 2009-06-22 19:34:25Z paulej $
+ *****************************************************************************
+ *
+ * Description:
+ * This class implements the Secure Hashing Standard as defined
+ * in FIPS PUB 180-1 published April 17, 1995.
+ *
+ * The Secure Hashing Standard, which uses the Secure Hashing
+ * Algorithm (SHA), produces a 160-bit message digest for a
+ * given data stream. In theory, it is highly improbable that
+ * two messages will produce the same message digest. Therefore,
+ * this algorithm can serve as a means of providing a "fingerprint"
+ * for a message.
+ *
+ * Portability Issues:
+ * SHA-1 is defined in terms of 32-bit "words". This code was
+ * written with the expectation that the processor has at least
+ * a 32-bit machine word size. If the machine word size is larger,
+ * the code should still function properly. One caveat to that
+ * is that the input functions taking characters and character arrays
+ * assume that only 8 bits of information are stored in each character.
+ *
+ * Caveats:
+ * SHA-1 is designed to work with messages less than 2^64 bits long.
+ * Although SHA-1 allows a message digest to be generated for
+ * messages of any number of bits less than 2^64, this implementation
+ * only works with messages with a length that is a multiple of 8
+ * bits.
+ *
+ */
+
+
+#include "sha1.h"
+
+/*
+ * SHA1
+ *
+ * Description:
+ * This is the constructor for the sha1 class.
+ *
+ * Parameters:
+ * None.
+ *
+ * Returns:
+ * Nothing.
+ *
+ * Comments:
+ *
+ */
+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:
+ * This function will initialize the sha1 class member variables
+ * in preparation for computing a new message digest.
+ *
+ * Parameters:
+ * None.
+ *
+ * Returns:
+ * Nothing.
+ *
+ * Comments:
+ *
+ */
+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;
+
+ Computed = false;
+ Corrupted = false;
+}
+
+/*
+ * Result
+ *
+ * Description:
+ * This function will return the 160-bit message digest into the
+ * array provided.
+ *
+ * Parameters:
+ * message_digest_array: [out]
+ * This is an array of five unsigned integers which will be filled
+ * with the message digest that has been computed.
+ *
+ * Returns:
+ * True if successful, false if it failed.
+ *
+ * Comments:
+ *
+ */
+bool SHA1::Result(unsigned *message_digest_array)
+{
+ int i; // Counter
+
+ if (Corrupted)
+ {
+ return false;
+ }
+
+ if (!Computed)
+ {
+ PadMessage();
+ Computed = true;
+ }
+
+ for(i = 0; i < 5; i++)
+ {
+ message_digest_array[i] = H[i];
+ }
+
+ return true;
+}
+
+/*
+ * Input
+ *
+ * Description:
+ * This function accepts an array of octets as the next portion of
+ * the message.
+ *
+ * Parameters:
+ * message_array: [in]
+ * An array of characters representing the next portion of the
+ * message.
+ *
+ * Returns:
+ * Nothing.
+ *
+ * Comments:
+ *
+ */
+void SHA1::Input( const unsigned char *message_array,
+ unsigned length)
+{
+ if (!length)
+ {
+ return;
+ }
+
+ if (Computed || Corrupted)
+ {
+ Corrupted = true;
+ return;
+ }
+
+ 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_High++;
+ Length_High &= 0xFFFFFFFF; // Force it to 32 bits
+ if (Length_High == 0)
+ {
+ Corrupted = true; // Message is too long
+ }
+ }
+
+ if (Message_Block_Index == 64)
+ {
+ ProcessMessageBlock();
+ }
+
+ message_array++;
+ }
+}
+
+/*
+ * Input
+ *
+ * Description:
+ * This function accepts an array of octets as the next portion of
+ * the message.
+ *
+ * Parameters:
+ * message_array: [in]
+ * An array of characters representing the next portion of the
+ * message.
+ * length: [in]
+ * The length of the message_array
+ *
+ * Returns:
+ * Nothing.
+ *
+ * Comments:
+ *
+ */
+void SHA1::Input( const char *message_array,
+ unsigned length)
+{
+ Input((unsigned char *) message_array, length);
+}
+
+/*
+ * Input
+ *
+ * Description:
+ * This function accepts a single octets as the next message element.
+ *
+ * Parameters:
+ * message_element: [in]
+ * The next octet in the message.
+ *
+ * Returns:
+ * Nothing.
+ *
+ * Comments:
+ *
+ */
+void SHA1::Input(unsigned char message_element)
+{
+ Input(&message_element, 1);
+}
+
+/*
+ * Input
+ *
+ * Description:
+ * This function accepts a single octet as the next message element.
+ *
+ * Parameters:
+ * message_element: [in]
+ * The next octet in the message.
+ *
+ * Returns:
+ * Nothing.
+ *
+ * Comments:
+ *
+ */
+void SHA1::Input(char message_element)
+{
+ Input((unsigned char *) &message_element, 1);
+}
+
+/*
+ * operator<<
+ *
+ * Description:
+ * This operator makes it convenient to provide character strings to
+ * the SHA1 object for processing.
+ *
+ * Parameters:
+ * message_array: [in]
+ * The character array to take as input.
+ *
+ * Returns:
+ * A reference to the SHA1 object.
+ *
+ * Comments:
+ * Each character is assumed to hold 8 bits of information.
+ *
+ */
+SHA1& SHA1::operator<<(const char *message_array)
+{
+ const char *p = message_array;
+
+ while(*p)
+ {
+ Input(*p);
+ p++;
+ }
+
+ return *this;
+}
+
+/*
+ * operator<<
+ *
+ * Description:
+ * This operator makes it convenient to provide character strings to
+ * the SHA1 object for processing.
+ *
+ * Parameters:
+ * message_array: [in]
+ * The character array to take as input.
+ *
+ * Returns:
+ * A reference to the SHA1 object.
+ *
+ * Comments:
+ * Each character is assumed to hold 8 bits of information.
+ *
+ */
+SHA1& SHA1::operator<<(const unsigned char *message_array)
+{
+ const unsigned char *p = message_array;
+
+ while(*p)
+ {
+ Input(*p);
+ p++;
+ }
+
+ return *this;
+}
+
+/*
+ * operator<<
+ *
+ * Description:
+ * This function provides the next octet in the message.
+ *
+ * Parameters:
+ * message_element: [in]
+ * The next octet in the message
+ *
+ * Returns:
+ * A reference to the SHA1 object.
+ *
+ * Comments:
+ * The character is assumed to hold 8 bits of information.
+ *
+ */
+SHA1& SHA1::operator<<(const char message_element)
+{
+ Input((unsigned char *) &message_element, 1);
+
+ return *this;
+}
+
+/*
+ * operator<<
+ *
+ * Description:
+ * This function provides the next octet in the message.
+ *
+ * Parameters:
+ * message_element: [in]
+ * The next octet in the message
+ *
+ * Returns:
+ * A reference to the SHA1 object.
+ *
+ * Comments:
+ * The character is assumed to hold 8 bits of information.
+ *
+ */
+SHA1& SHA1::operator<<(const unsigned char message_element)
+{
+ Input(&message_element, 1);
+
+ return *this;
+}
+
+/*
+ * ProcessMessageBlock
+ *
+ * Description:
+ * This function will process the next 512 bits of the message
+ * stored in the Message_Block array.
+ *
+ * Parameters:
+ * None.
+ *
+ * Returns:
+ * Nothing.
+ *
+ * Comments:
+ * Many of the variable names in this function, especially the single
+ * character names, were used because those were the names used
+ * 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
+
+ /*
+ * Initialize the first 16 words in the array W
+ */
+ 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]);
+ }
+
+ A = H[0];
+ B = H[1];
+ C = H[2];
+ 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];
+ temp &= 0xFFFFFFFF;
+ E = D;
+ D = C;
+ 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];
+ temp &= 0xFFFFFFFF;
+ E = D;
+ D = C;
+ C = CircularShift(30,B);
+ B = A;
+ A = temp;
+ }
+
+ 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);
+ B = A;
+ A = temp;
+ }
+
+ 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);
+ B = A;
+ A = temp;
+ }
+
+ H[0] = (H[0] + A) & 0xFFFFFFFF;
+ H[1] = (H[1] + B) & 0xFFFFFFFF;
+ H[2] = (H[2] + C) & 0xFFFFFFFF;
+ H[3] = (H[3] + D) & 0xFFFFFFFF;
+ H[4] = (H[4] + E) & 0xFFFFFFFF;
+
+ Message_Block_Index = 0;
+}
+
+/*
+ * PadMessage
+ *
+ * Description:
+ * According to the standard, the message must be padded to an even
+ * 512 bits. The first padding bit must be a '1'. The last 64 bits
+ * represent the length of the original message. All bits in between
+ * should be 0. This function will pad the message according to those
+ * rules by filling the message_block array accordingly. It will also
+ * call ProcessMessageBlock() appropriately. When it returns, it
+ * can be assumed that the message digest has been computed.
+ *
+ * Parameters:
+ * None.
+ *
+ * Returns:
+ * Nothing.
+ *
+ * Comments:
+ *
+ */
+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)
+ {
+ Message_Block[Message_Block_Index++] = 0x80;
+ while(Message_Block_Index < 64)
+ {
+ Message_Block[Message_Block_Index++] = 0;
+ }
+
+ ProcessMessageBlock();
+
+ while(Message_Block_Index < 56)
+ {
+ Message_Block[Message_Block_Index++] = 0;
+ }
+ }
+ else
+ {
+ Message_Block[Message_Block_Index++] = 0x80;
+ while(Message_Block_Index < 56)
+ {
+ Message_Block[Message_Block_Index++] = 0;
+ }
+
+ }
+
+ /*
+ * Store the message length as the last 8 octets
+ */
+ 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[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;
+
+ ProcessMessageBlock();
+}
+
+
+/*
+ * CircularShift
+ *
+ * Description:
+ * This member function will perform a circular shifting operation.
+ *
+ * Parameters:
+ * bits: [in]
+ * The number of bits to shift (1-31)
+ * word: [in]
+ * The value to shift (assumes a 32-bit integer)
+ *
+ * Returns:
+ * The shifted value.
+ *
+ * Comments:
+ *
+ */
+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
new file mode 100644
index 0000000..3c7d896
--- /dev/null
+++ b/src/main/c/sha1/sha1.h
@@ -0,0 +1,114 @@
+// 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.
+
+// DO NOT REFORMAT <- for tidy
+/*
+ * sha1.h
+ *
+ * Copyright (C) 1998, 2009
+ * Paul E. Jones <paulej@packetizer.com>
+ * All Rights Reserved.
+ *
+ *****************************************************************************
+ * $Id: sha1.h 12 2009-06-22 19:34:25Z paulej $
+ *****************************************************************************
+ *
+ * Description:
+ * This class implements the Secure Hashing Standard as defined
+ * in FIPS PUB 180-1 published April 17, 1995.
+ *
+ * Many of the variable names in this class, especially the single
+ * character names, were used because those were the names used
+ * in the publication.
+ *
+ * Please read the file sha1.cpp for more information.
+ *
+ */
+
+#ifndef _SHA1_H_
+#define _SHA1_H_
+
+class SHA1
+{
+
+ public:
+
+ SHA1();
+ virtual ~SHA1();
+
+ /*
+ * Re-initialize the class
+ */
+ void Reset();
+
+ /*
+ * Returns the message digest
+ */
+ 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);
+
+ private:
+
+ /*
+ * Process the next 512 bits of the message
+ */
+ void ProcessMessageBlock();
+
+ /*
+ * Pads the current message block to 512 bits
+ */
+ void PadMessage();
+
+ /*
+ * Performs a circular left shift operation
+ */
+ inline unsigned CircularShift(int bits, unsigned word);
+
+ unsigned H[5]; // Message digest buffers
+
+ 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
+
+ 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
new file mode 100644
index 0000000..1da757f
--- /dev/null
+++ b/src/main/c/util/CrackedUri.cpp
@@ -0,0 +1,129 @@
+// 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/StringUtil.h"
+#include "seasocks/util/CrackedUri.h"
+
+#include <algorithm>
+#include <sstream>
+#include <stdexcept>
+
+#define THROW(stuff) \
+ do {\
+ std::ostringstream err; \
+ err << stuff; \
+ throw std::runtime_error(err.str()); \
+ } while (0);
+
+namespace {
+
+char fromHex(char c) {
+ c = tolower(c);
+ return c >= '0' && c <= '9' ? c - '0' : c - 'a' + 10;
+}
+
+std::string unescape(std::string uri) {
+ seasocks::replace(uri, "+", " ");
+ size_t pos = 0;
+ while (pos < uri.size()) {
+ pos = uri.find('%', pos);
+ if (pos == uri.npos) break;
+ if (pos + 2 > uri.size()) {
+ THROW("Truncated uri: '" << uri << "'");
+ }
+ if (!isxdigit(uri[pos + 1]) || !isxdigit(uri[pos + 2])) {
+ THROW("Bad digit in uri: '" << uri << "'");
+ }
+ auto hex = (fromHex(uri[pos + 1]) << 4) | fromHex(uri[pos + 2]);
+ uri = uri.substr(0, pos) + std::string(1, hex) + uri.substr(pos + 3);
+ ++pos;
+ }
+ return uri;
+}
+
+}
+
+namespace seasocks {
+
+CrackedUri::CrackedUri(const std::string& uri) {
+ if (uri.empty() || uri[0] != '/') {
+ THROW("Malformed URI: '" << uri << "'");
+ }
+ auto endOfPath = uri.find('?');
+ std::string path;
+ std::string remainder;
+ if (endOfPath == uri.npos) {
+ path = uri.substr(1);
+ } else {
+ path = uri.substr(1, endOfPath - 1);
+ remainder = uri.substr(endOfPath + 1);
+ }
+
+ _path = split(path, '/');
+ 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, '=');
+ std::transform(split.begin(), split.end(), split.begin(), unescape);
+ if (split.size() == 1) {
+ _queryParams.insert(std::make_pair(split[0], std::string()));
+ } else if (split.size() == 2) {
+ _queryParams.insert(std::make_pair(split[0], split[1]));
+ } else {
+ THROW("Malformed URI, two many = in query: '" << uri << "'");
+ }
+ }
+}
+
+bool CrackedUri::hasParam(const std::string& param) const {
+ return _queryParams.find(param) != _queryParams.end();
+}
+
+std::string CrackedUri::queryParam(const std::string& param, const std::string& def) const {
+ auto found = _queryParams.find(param);
+ return found == _queryParams.end() ? def : found->second;
+}
+
+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)
+ params.push_back(iter->second);
+ return params;
+}
+
+CrackedUri CrackedUri::shift() const {
+ CrackedUri shifted(*this);
+ if (_path.size() > 1) {
+ shifted._path = std::vector<std::string>(_path.begin() + 1, _path.end());
+ } else {
+ shifted._path = {""};
+ }
+
+ return shifted;
+}
+
+}
diff --git a/src/main/c/util/Json.cpp b/src/main/c/util/Json.cpp
new file mode 100644
index 0000000..48c74d7
--- /dev/null
+++ b/src/main/c/util/Json.cpp
@@ -0,0 +1,66 @@
+// Copyright (c) 2013, Matt Godbolt
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+#include "seasocks/util/Json.h"
+
+#include <iomanip>
+
+namespace seasocks {
+
+void jsonToStream(std::ostream& str, const char* t) {
+ str << '"';
+ for (; *t; ++t) {
+ switch (*t) {
+ default:
+ if (*t >= 32) {
+ 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;
+ }
+ }
+ str << '"';
+}
+
+void jsonToStream(std::ostream& str, bool b) {
+ str << (b ? "true" : "false");
+}
+
+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
new file mode 100644
index 0000000..4f9b60f
--- /dev/null
+++ b/src/main/c/util/PathHandler.cpp
@@ -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/PathHandler.h"
+
+#include "seasocks/Request.h"
+#include "seasocks/Response.h"
+#include "seasocks/util/CrackedUri.h"
+
+using namespace seasocks;
+
+CrackedUriPageHandler::Ptr PathHandler::add(const CrackedUriPageHandler::Ptr& handler) {
+ _handlers.emplace_back(handler);
+ return _handlers.back();
+}
+
+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();
+
+ auto shiftedUri = uri.shift();
+
+ for (const auto &it : _handlers) {
+ auto response = it->handle(shiftedUri, request);
+ 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
new file mode 100644
index 0000000..f6b4f52
--- /dev/null
+++ b/src/main/c/util/RootPageHandler.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/util/RootPageHandler.h"
+
+#include "seasocks/Request.h"
+#include "seasocks/Response.h"
+#include "seasocks/util/CrackedUri.h"
+
+using namespace seasocks;
+
+RootPageHandler::RootPageHandler() {
+}
+
+RootPageHandler::~RootPageHandler() {
+}
+
+CrackedUriPageHandler::Ptr RootPageHandler::add(const CrackedUriPageHandler::Ptr& handler) {
+ _handlers.emplace_back(handler);
+ return _handlers.back();
+}
+
+std::shared_ptr<Response> RootPageHandler::handle(const Request& request) {
+ CrackedUri uri(request.getRequestUri());
+ for (const auto &it : _handlers) {
+ auto response = it->handle(uri, request);
+ if (response != Response::unhandled()) return response;
+ }
+ return Response::unhandled();
+}