diff --git a/15.pro b/15.pro index f4e5cdc..5154b82 100644 --- a/15.pro +++ b/15.pro @@ -4,32 +4,20 @@ CONFIG -= app_bundle CONFIG -= qt SOURCES += \ - main.cpp \ - uSockets/src/eventing/epoll.c \ - uSockets/src/context.c \ - uSockets/src/socket.c \ - uSockets/src/eventing/libuv.c \ - uSockets/src/ssl.c \ - uSockets/src/loop.c + main.cpp \ + uSockets/src/eventing/epoll.c \ + uSockets/src/context.c \ + uSockets/src/socket.c \ + uSockets/src/eventing/libuv.c \ + uSockets/src/ssl.c \ + uSockets/src/loop.c HEADERS += \ - src/Hub.h \ - src/Http.h \ - src/Context.h \ - src/uWS.h \ - src/HttpRouter.h - #uSockets/libusockets.h \ - #uSockets/internal/eventing/epoll.h \ - #uSockets/internal/networking/bsd.h \ - #uSockets/internal/eventing/libuv.h \ - #uSockets/internal/common.h \ - #uSockets/interfaces/timer.h \ - #uSockets/interfaces/socket.h \ - #uSockets/interfaces/poll.h \ - #uSockets/interfaces/context.h \ - #uSockets/interfaces/loop.h \ - #uSockets/interfaces/ssl.h \ - #uSockets/internal/loop.h + src/Http.h \ + src/uWS.h \ + src/HttpRouter.h \ + src/Loop.h \ + src/App.h INCLUDEPATH += uSockets/src src #QMAKE_CXXFLAGS += -fsanitize=address diff --git a/main.cpp b/main.cpp index 15f6b9d..32e0b96 100644 --- a/main.cpp +++ b/main.cpp @@ -1,7 +1,7 @@ // 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 "Context.h" +#include "App.h" #include "HttpRouter.h" int main() { @@ -9,24 +9,23 @@ int main() { std::cout << "HttpSocket size: " << sizeof(HttpSocket::Data) << std::endl; char *buffer = new char[512]; - // 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::SSLApp c(uWS::SSLOptions() + .keyFileName("/home/alexhultman/uWebSockets/misc/ssl/key.pem") + .certFileName("/home/alexhultman/uWebSockets/misc/ssl/cert.pem") + .passphrase("1234")); + //uWS::App c; - //uWS::SSLContext c(options); - uWS::Context c; - - c.route("GET", "/", [buffer](auto *s, HttpRequest *req, auto *args) { + c.onGet("/", [buffer](auto *s, HttpRequest *req, auto *args) { s->writeStatus(200)->writeHeader("Hello", "World")->end(buffer, 512); - }).route("GET", "/wrong", [buffer](auto *s, HttpRequest *req, auto *args) { + }).onGet("/wrong", [buffer](auto *s, HttpRequest *req, auto *args) { std::cout << "Wrong way!" << std::endl; + }).onWebSocket([]() { + }).listen("localhost", 3000, 0); - uWS::defaultHub.run(); + uWS::run(); } diff --git a/src/App.h b/src/App.h new file mode 100644 index 0000000..7bfbbd3 --- /dev/null +++ b/src/App.h @@ -0,0 +1,181 @@ +#ifndef CONTEXT_H +#define CONTEXT_H + +#include "libusockets.h" +#include +#include "Loop.h" +#include "Http.h" +#include "HttpRouter.h" + +namespace uWS { + +template +class AppBase { + +protected: + + template + static constexpr 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() { + + // default http handler is a router + onHttpRequest = [this](auto *s, HttpRequest *req) { + UserData user = {s, req}; + r.route("GET", 3, req->getUrl().data(), req->getUrl().length(), &user); + }; + + } + + struct UserData { + HttpSocket *httpSocket; + HttpRequest *httpRequest; + }; + + HttpRouter r; + + std::function *)> onHttpConnection; + std::function *)> onHttpDisconnection; + std::function *, HttpRequest *)> onHttpRequest; + } *data; + + // server protocols + SOCKET_CONTEXT_TYPE *httpServerContext; + SOCKET_CONTEXT_TYPE *webSocketServerContext; + + // a us_socket_context can be constructed from another us_socket_context and will then act purely as a behavior (shared SSL_CTX) + // only socket contexts that come from construction from the start can be listened or connected to + + // us_create_derived_socket_context(us_socket_context) + + // client protocols + + void init(us_loop *loop) { + new (data = (Data *) static_dispatch(us_ssl_socket_context_ext, us_socket_context_ext)(httpServerContext)) Data(); + + 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! + + if (!data->onHttpConnection) { + return; + } + + 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)); + + if (!data->onHttpDisconnection) { + return; + } + + 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; + } + }); + } + +public: + + // 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)); + } + + AppBase &onPost(std::string pattern, std::function *, HttpRequest *, std::vector *)> handler) { + data->r.add("POST", pattern.c_str(), [handler](typename Data::UserData *user, auto *args) { + handler(user->httpSocket, user->httpRequest, args); + }); + + return *this; + } + + AppBase &onGet(std::string pattern, std::function *, HttpRequest *, std::vector *)> handler) { + data->r.add("GET", pattern.c_str(), [handler](typename Data::UserData *user, auto *args) { + handler(user->httpSocket, user->httpRequest, args); + }); + + return *this; + } + + // for client and server + AppBase &onWebSocket(std::function handler) { + return *this; + } + + ~AppBase() { + } +}; + +class App : public AppBase { + +public: + App() { + httpServerContext = us_create_socket_context(defaultLoop.loop, sizeof(Data)); + init(defaultLoop.loop); + } +}; + +class SSLOptions { +public: + us_ssl_socket_context_options options = {}; + + SSLOptions() { + + } + + SSLOptions &keyFileName(std::string fileName) { + options.key_file_name = fileName.c_str(); + return *this; + } + + SSLOptions &certFileName(std::string fileName) { + options.cert_file_name = fileName.c_str(); + return *this; + } + + SSLOptions &passphrase(std::string passphrase) { + options.passphrase = passphrase.c_str(); + return *this; + } +}; + +class SSLApp : public AppBase { + +public: + SSLApp(SSLOptions &sslOptions) { + httpServerContext = us_create_ssl_socket_context(defaultLoop.loop, sizeof(Data), sslOptions.options); + init(defaultLoop.loop); + } + +}; + +} + +#endif // CONTEXT_H diff --git a/src/Context.h b/src/Context.h deleted file mode 100644 index eba3d83..0000000 --- a/src/Context.h +++ /dev/null @@ -1,204 +0,0 @@ -#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" - -#include "HttpRouter.h" - -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 - - // we have a router too - struct UserData { - // pass whatever you need as user data - HttpSocket *httpSocket; - HttpRequest *httpRequest; - }; - - HttpRouter r; - -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; - } - - ContextBase &route(std::string method, std::string pattern, std::function *, HttpRequest *, std::vector *)> handler) { - // calling this function overrides any other onHttpRequest! - onHttpRequest([this](auto *s, HttpRequest *req) { - UserData user = {s, req}; - r.route("GET", 3, req->getUrl().data(), req->getUrl().length(), &user); - }); - - r.add(method.c_str(), pattern.c_str(), [handler](UserData *user, auto *args) { - handler(user->httpSocket, user->httpRequest, args); - }); - - 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/Hub.h b/src/Hub.h deleted file mode 100644 index 1f9096b..0000000 --- a/src/Hub.h +++ /dev/null @@ -1,88 +0,0 @@ -// should lie in hub and be connected with pre/post callbacks -char *corkBuffer = new char[1024]; -int corkOffset = 0; - -#ifndef HUB_H -#define HUB_H - -#include "libusockets.h" -#include -#include -#include -#include - -#include "Http.h" -#include "Context.h" - -struct Hub; - -// 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? - -// 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() - -// 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? - -// 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? - -// ListenToken.pause(), .resume(), .close() - - -// 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(); - -// hubben är underförstådd och du agerar egentligen alltid på ett context som antingen är ssl eller inte - -struct Hub { - us_loop *loop; - - struct Data { - Data() { - - - } - - } *data; - - static void wakeupCb(us_loop *loop) { - - } - - static void preCb(us_loop *loop) { - - } - - static void postCb(us_loop *loop) { - - } - - Hub() : loop(us_create_loop(wakeupCb, preCb, postCb, sizeof(Data))) { - new (data = (Data *) us_loop_ext(loop)) Data(); - } - - void run() { - us_loop_run(loop); - } - - ~Hub() { - - } -}; - -namespace uWS { -thread_local Hub defaultHub; -} - -#endif // HUB_H diff --git a/src/Loop.h b/src/Loop.h new file mode 100644 index 0000000..eb3ee98 --- /dev/null +++ b/src/Loop.h @@ -0,0 +1,59 @@ +// should lie in hub and be connected with pre/post callbacks +char *corkBuffer = new char[1024]; +int corkOffset = 0; + +#ifndef HUB_H +#define HUB_H + +#include "libusockets.h" +#include +#include +#include +#include + +namespace uWS { +struct Loop { + us_loop *loop; + + struct Data { + Data() { + + + } + + } *data; + + static void wakeupCb(us_loop *loop) { + + } + + static void preCb(us_loop *loop) { + + } + + static void postCb(us_loop *loop) { + + } + + Loop() : loop(us_create_loop(wakeupCb, preCb, postCb, sizeof(Data))) { + new (data = (Data *) us_loop_ext(loop)) Data(); + } + + void run() { + us_loop_run(loop); + } + + ~Loop() { + + } +}; + +// dessa måste ligga i en sourcefil +thread_local Loop defaultLoop; + +static void run() { + defaultLoop.run(); +} +} + +#endif // HUB_H diff --git a/src/uWS.h b/src/uWS.h index 279c597..ce67783 100644 --- a/src/uWS.h +++ b/src/uWS.h @@ -1,6 +1,6 @@ #ifndef UWS_H #define UWS_H -#include "Hub.h" +#include "Loop.h" #endif // UWS_H