From eb7a73651ffff0c7c684601f6c46ebf9fe36f333 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Sun, 5 Aug 2018 01:03:50 +0200 Subject: [PATCH] Shutdown idle HttpSockets, serve a complete website, etc --- main.cpp | 59 +++++++++++++++++++++++++++++++++++++++++++++--- src/App.h | 20 ++++++++++++++-- src/HttpSocket.h | 12 +++++++--- src/Loop.h | 2 +- uSockets | 2 +- 5 files changed, 85 insertions(+), 10 deletions(-) diff --git a/main.cpp b/main.cpp index 0b8fb8e..dbe4e75 100644 --- a/main.cpp +++ b/main.cpp @@ -1,5 +1,8 @@ #include "uWS.h" +#include +#include + std::string buffer; int connections = 0; @@ -12,6 +15,34 @@ void respond(T *s) { #define USE_SSL +#include + +std::string_view getFile(std::string_view file) { + static std::map cache; + + auto it = cache.find(file); + + if (it == cache.end()) { + std::cout << "Did not have file: " << file << std::endl; + + std::ifstream fin("rocket_files/" + std::string(file), std::ios::binary); + std::ostringstream oss; + oss << fin.rdbuf(); + + char *cachedFile = (char *) malloc(oss.str().size()); + memcpy(cachedFile, oss.str().data(), oss.str().size()); + + char *key = (char *) malloc(file.length()); + memcpy(key, file.data(), file.length()); + + cache[std::string_view(key, file.length())] = std::string_view(cachedFile, oss.str().size()); + + return getFile(file); + } else { + return it->second; + } +} + int main(int argc, char **argv) { // 50 mb for huge @@ -31,9 +62,31 @@ int main(int argc, char **argv) { // todo: add timeouts and socket shutdown! - app.onGet("/", [](auto *s, auto *req, auto *args) { - s->writeStatus("200 OK")->writeHeader("Content-type", "text/html; charset=utf-8")->end("

Welcome to µWebSockets v0.15!

"); - }).onGet("/tiny", [](auto *s, auto *req, auto *args) { + auto serve = [](auto *s, auto *req, auto *args) { + + //std::cout << "URL: " << req->getUrl() << std::endl; + + s->writeStatus("200 OK"); + std::string_view file; + if (args->size() != 2) { + file = getFile("rocket.html"); + } else { + file = getFile((*args)[1]); + + std::string_view name = (*args)[1]; + + if (name.length() > 4 && name.substr(name.length() - 4) == ".svg") { + s->writeHeader("Content-type", "image/svg+xml"); + } + } + + /*writeHeader("Content-type", "text/html; charset=utf-8")->*/s->write([file](int offset) { + return std::string_view(file.data() + offset, file.size() - offset); + }, file.size()); + + }; + + app.onGet("/", serve).onGet("/:folder/:file", serve).onGet("/tiny", [](auto *s, auto *req, auto *args) { respond<512>(s); }).onGet("/small", [](auto *s, auto *req, auto *args) { respond<4096>(s); diff --git a/src/App.h b/src/App.h index 5bd9087..d529d7a 100644 --- a/src/App.h +++ b/src/App.h @@ -90,6 +90,8 @@ protected: static_dispatch(us_ssl_socket_context_on_data, us_socket_context_on_data)(httpServerContext, [](auto *s, char *data, int length) { Data *appData = (Data *) static_dispatch(us_ssl_socket_context_ext, us_socket_context_ext)(static_dispatch(us_ssl_socket_get_context, us_socket_get_context)(s)); + // warning: should NOT reset timer on any data, ONLY reset data on full HTTP requests! + // warning: if we are in shutdown state, resetting the timer is a security issue! static_dispatch(us_ssl_socket_timeout, us_socket_timeout)(s, HTTP_IDLE_TIMEOUT_S); // onHttpRequest should probably be hard-coded to HttpRouter @@ -101,16 +103,30 @@ protected: }); static_dispatch(us_ssl_socket_context_on_writable, us_socket_context_on_writable)(httpServerContext, [](auto *s) { + + // what if the client + + // I think it's fair to never mind this one -> if we keep writing data after shutting down then that's an issue for us static_dispatch(us_ssl_socket_timeout, us_socket_timeout)(s, HTTP_IDLE_TIMEOUT_S); ((HttpSocket *) s)->onWritable(); }); + static_dispatch(us_ssl_socket_context_on_end, us_socket_context_on_end)(httpServerContext, [](auto *s) { + std::cout << "Socket was half-closed!" << std::endl; + }); + static_dispatch(us_ssl_socket_context_on_timeout, us_socket_context_on_timeout)(httpServerContext, [](auto *s) { - // basically, when any socket times out we want to close it + if (static_dispatch(us_ssl_socket_is_shut_down, us_socket_is_shut_down)(s)) { + std::cout << "Forcefully closing socket since shutdown was not answered in time" << std::endl; + static_dispatch(us_ssl_socket_close, us_socket_close)(s); + } else { + std::cout << "Shutting down socket now" << std::endl; + static_dispatch(us_ssl_socket_timeout, us_socket_timeout)(s, HTTP_IDLE_TIMEOUT_S); + static_dispatch(us_ssl_socket_shutdown, us_socket_shutdown)(s); + } - std::cout << "The server would now like to close a socket!" << std::endl; }); } diff --git a/src/HttpSocket.h b/src/HttpSocket.h index 9ded908..00ce6d0 100644 --- a/src/HttpSocket.h +++ b/src/HttpSocket.h @@ -113,20 +113,26 @@ struct HttpSocket { std::string_view chunk = cb(0); + // kopiera upp till (SSL eller icke-ssl) max copy distance + + // om mer än detta, fortsätt skicka + + + // this strategy can be simplified to one, we can even have MAX_COPY_DISTANCE_SSL and MAX_COPY_DISTANCE if (length < uWS::Loop::MAX_COPY_DISTANCE) { // what if the streamer cannot return any data? // then it should return something to pause write, and then start it again // basically we need throttling writeToCorkBufferAndReset(chunk.data(), chunk.length(), length, false); } else { - // basically finish off the header section and send it as separate syscall (we do not copy anthing in this strategy) - writeToCorkBufferAndReset(nullptr, 0, length, true); + // copying some data with the headers is a good idea for SSL but probably not for non-SSL + writeToCorkBufferAndReset(chunk.data(), uWS::Loop::MAX_COPY_DISTANCE, length, true); // just assume this went fine Data *httpData = (Data *) static_dispatch(us_ssl_socket_ext, us_socket_ext)((SOCKET_TYPE *) this); // write that off! - static_dispatch(us_ssl_socket_write, us_socket_write)((SOCKET_TYPE *) this, chunk.data(), chunk.length(), 0); + static_dispatch(us_ssl_socket_write, us_socket_write)((SOCKET_TYPE *) this, chunk.data() + uWS::Loop::MAX_COPY_DISTANCE, chunk.length() - uWS::Loop::MAX_COPY_DISTANCE, 0); // if offset is at the end, we are done if (httpData->offset < length) { diff --git a/src/Loop.h b/src/Loop.h index a5da773..3901871 100644 --- a/src/Loop.h +++ b/src/Loop.h @@ -38,7 +38,7 @@ struct Loop { } - Loop() : loop(us_create_loop(wakeupCb, preCb, postCb, sizeof(Data))) { + Loop() : loop(us_create_loop(1, wakeupCb, preCb, postCb, sizeof(Data))) { new (data = (Data *) us_loop_ext(loop)) Data(); } diff --git a/uSockets b/uSockets index 2760d77..1b19930 160000 --- a/uSockets +++ b/uSockets @@ -1 +1 @@ -Subproject commit 2760d77be39efbb79ba92e34ed6989921e523fec +Subproject commit 1b1993045b548250c0ed1cb6a52eb2c74489ed67