From 73aa9cae6bba77efc4ec41a33b16767ad2a6d069 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Wed, 20 Jun 2018 22:26:40 +0200 Subject: [PATCH] Seamless SSL/non-SSL swapping, new Context-centric interface --- main.cpp | 19 ++++-- src/Context.h | 175 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/Http.h | 12 +++- src/Hub.h | 132 ++++++------------------------------- uSockets | 2 +- 5 files changed, 217 insertions(+), 123 deletions(-) diff --git a/main.cpp b/main.cpp index b3a3356..12ec3fd 100644 --- a/main.cpp +++ b/main.cpp @@ -1,23 +1,28 @@ // this is roughly the interfaces I plan for (with changes and additional helpers added with time) // much speaks for a header-only or header-mostly implementation now that uSockets is properly isolating its internal headers -#include "uWS.h" +#include "Context.h" int main() { - std::cout << "HttpSocket size: " << sizeof(HttpSocket::Data) << std::endl; + std::cout << "HttpSocket size: " << sizeof(HttpSocket::Data) << std::endl; char *buffer = new char[512]; - // either use this, or use onHttpRoute but not both - uWS::defaultHub.onHttpRequest([buffer](HttpSocket *s, HttpRequest *req) { + // SSL options are given via uSockets structure + us_ssl_socket_context_options options = {}; + options.key_file_name = "/home/alexhultman/uWebSockets/misc/ssl/key.pem"; + options.cert_file_name = "/home/alexhultman/uWebSockets/misc/ssl/cert.pem"; + options.passphrase = "1234"; + + uWS::SSLContext(options).onHttpRequest([buffer](auto *s, HttpRequest *req) { if (req->getUrl() == "/") { - s->writeStatus(200)->writeHeader("Server", "µWebSockets v0.15")->end(buffer, 512); + s->writeStatus(200)->writeHeader("Hello", "World")->end(buffer, 512); } else { std::cout << "Got HTTP request at URL: " << req->getUrl() << std::endl; } - }).listen("localhost", 3000, 0).run(); + }).listen("localhost", 3000, 0); - // todo: important swapping to SSL should be .secureListen(same interfaces) and work out of the box! + uWS::defaultHub.run(); } diff --git a/src/Context.h b/src/Context.h index ddd9dd4..18f7535 100644 --- a/src/Context.h +++ b/src/Context.h @@ -1,6 +1,181 @@ #ifndef CONTEXT_H #define CONTEXT_H +#include "Hub.h" +#include "libusockets.h" +#include + +// represents a mix of two us_socket contexts either in ssl or not (one for http, one for websocket) + +// if you want to share the same openssl context between the two us socket ssl contexts? how? + +#include "Http.h" + +//#define SWAP_F [](auto a, auto b){ if constexpr(SSL) return a; else return b; } + +namespace uWS { + +// holds 4 protocols: server http, client http, server websocket, client websocket (for either SSL or not) +template +class ContextBase { + +protected: + + template + static typename std::conditional::type *static_dispatch(A *a, B *b) { + if constexpr(SSL) { + return a; + } else { + return b; + } + } + + typedef typename std::conditional::type SOCKET_CONTEXT_TYPE; + + struct Data { + Data() { + + + } + + std::function *)> onHttpConnection; + std::function *)> onHttpDisconnection; + std::function *, HttpRequest *)> onHttpRequest; + } *data; + + // server protocols + SOCKET_CONTEXT_TYPE *httpServerContext; + SOCKET_CONTEXT_TYPE *webSocketServerContext; + + + + // client protocols + +public: + + // the shared constructor + void init(us_loop *loop) { + + new (data = (Data *) static_dispatch(us_ssl_socket_context_ext, us_socket_context_ext)(httpServerContext)) Data(); + + // register shims + static_dispatch(us_ssl_socket_context_on_open, us_socket_context_on_open)(httpServerContext, [](auto *s) { + Data *data = (Data *) static_dispatch(us_ssl_socket_context_ext, us_socket_context_ext)(static_dispatch(us_ssl_socket_get_context, us_socket_get_context)(s)); + + + // here we need to construct a HTTP socket on the ext! + + + + // we always give pointers to us_socket? + // same mistake as before? + + if (!data->onHttpConnection) { + return; + } + + // note: this is VERY tricky to keep bug-free! + // we could give the ext here, and skip all bugs? + data->onHttpConnection((HttpSocket *) s); + }); + + static_dispatch(us_ssl_socket_context_on_close, us_socket_context_on_close)(httpServerContext, [](auto *s) { + Data *data = (Data *) static_dispatch(us_ssl_socket_context_ext, us_socket_context_ext)(static_dispatch(us_ssl_socket_get_context, us_socket_get_context)(s)); + + + // we always give pointers to us_socket? + // same mistake as before? + + if (!data->onHttpDisconnection) { + return; + } + + // note: this is VERY tricky to keep bug-free! + // we could give the ext here, and skip all bugs? + data->onHttpDisconnection((HttpSocket *) s); + }); + + static_dispatch(us_ssl_socket_context_on_data, us_socket_context_on_data)(httpServerContext, [](auto *s, char *data, int length) { + + Data *contextData = (Data *) static_dispatch(us_ssl_socket_context_ext, us_socket_context_ext)(static_dispatch(us_ssl_socket_get_context, us_socket_get_context)(s)); + + + + + + // a HttpSocket is basically the Http state and everything needed for it, we use it to parse the data and it knows about its context + + // + + HttpRequest req(data, length); + if (req.isComplete()) { + contextData->onHttpRequest((HttpSocket *) s, &req); + } else { + std::cout << "Got chunked HTTP headers!" << std::endl; + } + + // here we run the HTTP parser on this data + + + + // we always give pointers to us_socket? + // same mistake as before? + + // note: this is VERY tricky to keep bug-free! + // we could give the ext here, and skip all bugs? + //data->((HttpSocket *) s); + }); + } + + // for server + void listen(const char *host, int port, int options) { + static_dispatch(us_ssl_socket_context_listen, us_socket_context_listen)(httpServerContext, host, port, options, sizeof(HttpSocket)); + } + + // for server + ContextBase &onHttpRequest(decltype(Data::onHttpRequest) handler) { + data->onHttpRequest = handler; + + return *this; + } + + // for client and server + ContextBase &onWebSocketConnection() { + return *this; + } + + ~ContextBase() { + } +}; + +class Context : public ContextBase { + +public: + Context() { + + // we create the context ourselves here + httpServerContext = us_create_socket_context(defaultHub.loop, sizeof(Data)); + + // init from base + init(defaultHub.loop); + } +}; + +class SSLContext : public ContextBase { + +public: + // denna finns i barnet SSLContext! + SSLContext(us_ssl_socket_context_options options) { + // we create the context ourselves here + httpServerContext = us_create_ssl_socket_context(defaultHub.loop, sizeof(Data), options); + + // init from base + init(defaultHub.loop); + } + +}; + +} #endif // CONTEXT_H diff --git a/src/Http.h b/src/Http.h index 8fc48e1..4c2dece 100644 --- a/src/Http.h +++ b/src/Http.h @@ -71,6 +71,7 @@ struct HttpRequest { // HttpSocket is an alias for us_socket +template struct HttpSocket { // chunked response will be tricky with this buffering scheme @@ -115,14 +116,21 @@ struct HttpSocket { return this; } + // this depends on SSL! void end(char *data, int length) { - us_socket *s = (us_socket *) this; + //us_ssl_socket *s = (us_ssl_socket *) this; char *corkBuffer = getCorkBuffer(); memcpy(corkBuffer + corkOffset, data, length); corkOffset += length; - us_socket_write(s, corkBuffer, corkOffset, 0); + + if constexpr(SSL) { + us_ssl_socket_write((us_ssl_socket *) this, corkBuffer, corkOffset); + } else { + us_socket_write((us_socket *) this, corkBuffer, corkOffset, 0); + } + corkOffset = 0; } diff --git a/src/Hub.h b/src/Hub.h index 754da67..1f9096b 100644 --- a/src/Hub.h +++ b/src/Hub.h @@ -16,130 +16,38 @@ int corkOffset = 0; struct Hub; -// maybe a Context is both TCP and SSL in one? -template -struct Context { +// Hub::listen has to create the socket context (with SSL options) via first time init +// what if you want to listen to another port with another SSL cert? +// every listen has to correspond to a listen socket and a socket context, created for each call to listen +// same with connect? - Hub &hub; +// just remove Context altogether and just let Hub have all of this stuff +// it really only needs to hold the listen socket and the socket context wrapped as ListenToken instead +// ListenToken.close() - struct Data { - Data() { +// ListenContext? - } +// instead of Context, have some kind of token for Listening and allow it to be stopped but not cloesed (you stop listening but keep sockets alive) +// what if you then want to listen again? you don't want to create a new context that time? - std::function onHttpConnection; - std::function onHttpDisconnection; - std::function onHttpRequest; - } *data; +// imagine listening, then stopping, then continuing - maybe support PAUSING a listen socket without closing it? that way you listen once, pause listening, etc +// this way we can solve the accept issue with listen-pause and listen timers? - us_socket_context *httpContext; - - Context(Hub &hub) : hub(hub) { - - } - - // one for each? ssl and non-ssl? - void init(us_loop *loop) { - httpContext = us_create_socket_context(loop, sizeof(Data)); - - new (data = (Data *) us_socket_context_ext(httpContext)) Data(); - - // register shims - us_socket_context_on_open(httpContext, [](us_socket *s) { - Data *data = (Data *) us_socket_context_ext(us_socket_get_context(s)); +// ListenToken.pause(), .resume(), .close() - // here we need to construct a HTTP socket on the ext! +// eller så bara ändrar man gränssnittet så att man explicit skapar ett context? +// createContext(ssl options eller inga options avgör om SSL eller inte) +// eller bara sslContext(options).onHttplalala.listen().Hub().run(); - // we always give pointers to us_socket? - // same mistake as before? +// hubben är underförstådd och du agerar egentligen alltid på ett context som antingen är ssl eller inte - if (!data->onHttpConnection) { - return; - } - - // note: this is VERY tricky to keep bug-free! - // we could give the ext here, and skip all bugs? - data->onHttpConnection((HttpSocket *) s); - }); - - us_socket_context_on_close(httpContext, [](us_socket *s) { - Data *data = (Data *) us_socket_context_ext(us_socket_get_context(s)); - - // we always give pointers to us_socket? - // same mistake as before? - - if (!data->onHttpDisconnection) { - return; - } - - // note: this is VERY tricky to keep bug-free! - // we could give the ext here, and skip all bugs? - data->onHttpDisconnection((HttpSocket *) s); - }); - - us_socket_context_on_data(httpContext, [](us_socket *s, char *data, int length) { - Data *contextData = (Data *) us_socket_context_ext(us_socket_get_context(s)); - - - - - // a HttpSocket is basically the Http state and everything needed for it, we use it to parse the data and it knows about its context - - // - - HttpRequest req(data, length); - if (req.isComplete()) { - contextData->onHttpRequest((HttpSocket *) s, &req); - } else { - std::cout << "Got chunked HTTP headers!" << std::endl; - } - - // here we run the HTTP parser on this data - - - - // we always give pointers to us_socket? - // same mistake as before? - - // note: this is VERY tricky to keep bug-free! - // we could give the ext here, and skip all bugs? - //data->((HttpSocket *) s); - }); - } - - void onHttpConnection(decltype(Data::onHttpConnection) handler) { - data->onHttpConnection = handler; - } - void onHttpDisconnection(decltype(Data::onHttpDisconnection) handler) { - data->onHttpDisconnection = handler; - } - Hub &onHttpRequest(decltype(Data::onHttpRequest) handler) { - data->onHttpRequest = handler; - - return hub; - } - - // this should only be enabled if we are server context! - - // should return Loop, not hub? or simply make it so that ALL contexts stem from some Hub? Hub is essentually the Loop abstraction? could work - Hub &listen(const char *host, int port, int options) { - us_socket_context_listen(httpContext, host, port, options, sizeof(HttpSocket)); - - return hub; - } -}; - -// are we SSL or regular hub? -struct Hub : Context { +struct Hub { us_loop *loop; - using Context::onHttpConnection; - using Context::listen; - struct Data { Data() { @@ -160,10 +68,8 @@ struct Hub : Context { } - Hub() : loop(us_create_loop(wakeupCb, preCb, postCb, sizeof(Data))), Context(*this) { + Hub() : loop(us_create_loop(wakeupCb, preCb, postCb, sizeof(Data))) { new (data = (Data *) us_loop_ext(loop)) Data(); - - Context::init(loop); } void run() { diff --git a/uSockets b/uSockets index ad3ee4c..88ff2b1 160000 --- a/uSockets +++ b/uSockets @@ -1 +1 @@ -Subproject commit ad3ee4c3b51ec95ddecc97a7e1f09c658e899e35 +Subproject commit 88ff2b1f35af8e8633cc410c2413dc631bf38ca6