diff --git a/examples/Http3Server.cpp b/examples/Http3Server.cpp index 8a1ecec..cbbff86 100644 --- a/examples/Http3Server.cpp +++ b/examples/Http3Server.cpp @@ -6,18 +6,20 @@ /* Tentative example of simple Http3 server */ int main() { - uWS::QuicApp({ + uWS::H3App({ .key_file_name = "../misc/key.pem", .cert_file_name = "../misc/cert.pem", .passphrase = "1234" }).get("/*", [](auto *res, auto *req) { - std::cout << req->getHeader(":path") << std::endl; + /* Printing these should obviously be disabled if doing benchmarking */ + //std::cout << req->getHeader(":path") << std::endl; + //std::cout << req->getHeader(":method") << std::endl; - res->end("Hello quic!"); - }).listen(3000, [](auto *listen_socket) { + res->end("Hello H3 from uWS!"); + }).listen(9004, [](auto *listen_socket) { if (listen_socket) { - std::cout << "Listening on port " << 3000 << std::endl; + std::cout << "Listening on port " << 9004 << std::endl; } }).run(); diff --git a/src/Http3App.h b/src/Http3App.h index b57c30a..23c3c2a 100644 --- a/src/Http3App.h +++ b/src/Http3App.h @@ -6,28 +6,103 @@ namespace uWS { - struct QuicApp { + struct H3App { Http3Context *http3Context; - QuicApp(SocketContextOptions options = {}) { + H3App(SocketContextOptions options = {}) { /* Create the http3 context */ http3Context = Http3Context::create((us_loop_t *)Loop::get(), {}); + + http3Context->init(); } - QuicApp &listen(int port, std::function cb) { - return *this; + /* Disallow copying, only move */ + H3App(const H3App &other) = delete; + + H3App(H3App &&other) { + /* Move HttpContext */ + http3Context = other.http3Context; + other.http3Context = nullptr; } - QuicApp &get(std::string path, std::function cb) { - // http3Context->onHttp, internally using httprouter + H3App &&listen(int port, std::function cb) { + http3Context->listen(); + return std::move(*this); + } - return *this; + H3App &&get(std::string pattern, MoveOnlyFunction &&handler) { + if (http3Context) { + http3Context->onHttp("GET", pattern, std::move(handler)); + } + return std::move(*this); + } + + H3App &&post(std::string pattern, MoveOnlyFunction &&handler) { + if (http3Context) { + http3Context->onHttp("POST", pattern, std::move(handler)); + } + return std::move(*this); + } + + H3App &&options(std::string pattern, MoveOnlyFunction &&handler) { + if (http3Context) { + http3Context->onHttp("OPTIONS", pattern, std::move(handler)); + } + return std::move(*this); + } + + H3App &&del(std::string pattern, MoveOnlyFunction &&handler) { + if (http3Context) { + http3Context->onHttp("DELETE", pattern, std::move(handler)); + } + return std::move(*this); + } + + H3App &&patch(std::string pattern, MoveOnlyFunction &&handler) { + if (http3Context) { + http3Context->onHttp("PATCH", pattern, std::move(handler)); + } + return std::move(*this); + } + + H3App &&put(std::string pattern, MoveOnlyFunction &&handler) { + if (http3Context) { + http3Context->onHttp("PUT", pattern, std::move(handler)); + } + return std::move(*this); + } + + H3App &&head(std::string pattern, MoveOnlyFunction &&handler) { + if (http3Context) { + http3Context->onHttp("HEAD", pattern, std::move(handler)); + } + return std::move(*this); + } + + H3App &&connect(std::string pattern, MoveOnlyFunction &&handler) { + if (http3Context) { + http3Context->onHttp("CONNECT", pattern, std::move(handler)); + } + return std::move(*this); + } + + H3App &&trace(std::string pattern, MoveOnlyFunction &&handler) { + if (http3Context) { + http3Context->onHttp("TRACE", pattern, std::move(handler)); + } + return std::move(*this); + } + + /* This one catches any method */ + H3App &&any(std::string pattern, MoveOnlyFunction &&handler) { + if (http3Context) { + http3Context->onHttp("*", pattern, std::move(handler)); + } + return std::move(*this); } void run() { uWS::Loop::get()->run(); } }; - - } \ No newline at end of file diff --git a/src/Http3Context.h b/src/Http3Context.h index db572f8..7a2f4d5 100644 --- a/src/Http3Context.h +++ b/src/Http3Context.h @@ -6,59 +6,6 @@ extern "C" { #include "Http3ContextData.h" #include "Http3ResponseData.h" -/* Let's just have this one here for now */ -us_quic_socket_context_t *context; - -/* This would be a request */ -void on_stream_headers(us_quic_stream_t *s) { - - Http3ContextData *contextData = (Http3ContextData *) us_quic_socket_context_ext(us_quic_socket_context(us_quic_stream_socket(s))); - - contextData->router.route(); - - -} - -/* And this would be the body of the request */ -void on_stream_data(us_quic_stream_t *s, char *data, int length) { - - // Http3ResponseData *responseData = us_quic_stream_ext(s); - // responseData->onData(data, length); - - printf("Body length is: %d\n", length); -} - -void on_stream_writable(us_quic_stream_t *s) { - // Http3ResponseData *responseData = us_quic_stream_ext(s); - // responseData->onWritable(); -} - -void on_stream_close(us_quic_stream_t *s) { - - // Http3ResponseData *responseData = us_quic_stream_ext(s); - // responseData->onAborted(); - - //printf("Stream closed\n"); - - // inplace destruct Http3ResponseData here -} - -/* On new connection */ -void on_open(us_quic_socket_t *s, int is_client) { - printf("Connection established!\n"); -} - -/* On new stream */ -void on_stream_open(us_quic_stream_t *s, int is_client) { - //printf("Stream opened!\n"); - - // inplace initialize Http3ResponseData here -} - -void on_close(us_quic_socket_t *s) { - printf("Disconnected!\n"); -} - namespace uWS { struct Http3Context { static Http3Context *create(us_loop_t *loop, us_quic_socket_context_options_t options) { @@ -67,32 +14,103 @@ namespace uWS { printf("Creating context now\n"); /* Create quic socket context (assumes h3 for now) */ - context = us_create_quic_socket_context(loop, options); // sizeof(Http3ContextData) + auto *context = us_create_quic_socket_context(loop, options, sizeof(Http3ContextData)); // sizeof(Http3ContextData) /* Specify application callbacks */ - us_quic_socket_context_on_stream_data(context, on_stream_data); - us_quic_socket_context_on_stream_open(context, on_stream_open); - us_quic_socket_context_on_stream_close(context, on_stream_close); - us_quic_socket_context_on_stream_writable(context, on_stream_writable); - us_quic_socket_context_on_stream_headers(context, on_stream_headers); - us_quic_socket_context_on_open(context, on_open); - us_quic_socket_context_on_close(context, on_close); + us_quic_socket_context_on_stream_data(context, [](us_quic_stream_t *s, char *data, int length) { - /* The listening socket is the actual UDP socket used */ - us_quic_listen_socket_t *listen_socket = us_quic_socket_context_listen(context, "::1", 9004); // sizeof(Http3ResponseData) + // Http3ResponseData *responseData = us_quic_stream_ext(s); + // responseData->onData(data, length); + + printf("Body length is: %d\n", length); + }); + us_quic_socket_context_on_stream_open(context, [](us_quic_stream_t *s, int is_client) { + //printf("Stream opened!\n"); + + // inplace initialize Http3ResponseData here + }); + us_quic_socket_context_on_close(context, [](us_quic_socket_t *s) { + printf("Disconnected!\n"); + }); + us_quic_socket_context_on_stream_writable(context, [](us_quic_stream_t *s) { + // Http3ResponseData *responseData = us_quic_stream_ext(s); + // responseData->onWritable(); + }); + us_quic_socket_context_on_stream_headers(context, [](us_quic_stream_t *s) { + + Http3ContextData *contextData = (Http3ContextData *) us_quic_socket_context_ext(us_quic_socket_context(us_quic_stream_socket(s))); + + Http3Request *req = nullptr; + + std::string_view upperCasedMethod = req->getHeader(":method"); + //std::transform(lowerCasedMethod.begin(), lowerCasedMethod.end(), lowerCasedMethod.begin(), ::tolower); + + contextData->router.getUserData() = {(Http3Response *) s, (Http3Request *) nullptr}; + contextData->router.route(upperCasedMethod, "/"); - return nullptr; + }); + us_quic_socket_context_on_open(context, [](us_quic_socket_t *s, int is_client) { + printf("Connection established!\n"); + }); + us_quic_socket_context_on_stream_close(context, [](us_quic_stream_t *s) { + + // Http3ResponseData *responseData = us_quic_stream_ext(s); + // responseData->onAborted(); + + //printf("Stream closed\n"); + + // inplace destruct Http3ResponseData here + }); + + return (Http3Context *) context; // call init here after setting the ext to Http3ContextData } - void init() { - // set all callbacks here + us_quic_listen_socket_t *listen() { + /* The listening socket is the actual UDP socket used */ + us_quic_listen_socket_t *listen_socket = us_quic_socket_context_listen((us_quic_socket_context_t *) this, "::1", 9004, sizeof(Http3ResponseData)); // sizeof(Http3ResponseData) + + return listen_socket; } - void onHttp() { + void init() { + // set all callbacks here + + + + Http3ContextData *contextData = (Http3ContextData *) us_quic_socket_context_ext((us_quic_socket_context_t *) this); + + printf("init: %p\n", contextData); + + new (contextData) Http3ContextData(); + + } + + // generic for get, post, any, etc + void onHttp(std::string method, std::string path, MoveOnlyFunction &&cb) { // modifies the router we own as part of Http3ContextData, used in callbacks set in init + + Http3ContextData *contextData = (Http3ContextData *) us_quic_socket_context_ext((us_quic_socket_context_t *) this); + + /* Todo: This is ugly, fix */ + std::vector methods; + if (method == "*") { + methods = contextData->router.upperCasedMethods; //bug! needs to be upper cased! + // router.upperCasedMethods; + } else { + methods = {method}; + } + + contextData->router.add(methods, path, [handler = std::move(cb)](HttpRouter *router) mutable { + + Http3ContextData::RouterData &routerData = router->getUserData(); + + handler(routerData.res, routerData.req); + + return true; + }); } }; } \ No newline at end of file diff --git a/src/Http3ContextData.h b/src/Http3ContextData.h index 20ad5f0..8d93aaf 100644 --- a/src/Http3ContextData.h +++ b/src/Http3ContextData.h @@ -1,9 +1,21 @@ #include "HttpRouter.h" +struct Http3Response; +struct Http3Request; + namespace uWS { struct Http3ContextData { - HttpRouter router; + struct RouterData { + Http3Response *res; + Http3Request *req; + }; + + HttpRouter router; + + Http3ContextData() { + printf("Constructing http3contextdata: %p\n", this); + } }; } \ No newline at end of file diff --git a/src/Http3Request.h b/src/Http3Request.h index 299d888..0cae22d 100644 --- a/src/Http3Request.h +++ b/src/Http3Request.h @@ -15,8 +15,8 @@ namespace uWS { return {value, value_length}; } } - return {nullptr, 0}; } + return {nullptr, 0}; } }; } \ No newline at end of file diff --git a/src/Http3Response.h b/src/Http3Response.h index 50422cb..9d4e7bc 100644 --- a/src/Http3Response.h +++ b/src/Http3Response.h @@ -1,3 +1,7 @@ +extern "C" { +#include "quic.h" +} + namespace uWS { /* Is a quic stream */ @@ -14,16 +18,16 @@ namespace uWS { // if not already written status then write status /* Write headers */ - us_quic_socket_context_set_header(context, 0, ":status", 7, "200", 3); + us_quic_socket_context_set_header(nullptr, 0, (char *) ":status", 7, "200", 3); //us_quic_socket_context_set_header(context, 1, "content-length", 14, "11", 2); //us_quic_socket_context_set_header(context, 2, "content-type", 12, "text/html", 9); - us_quic_socket_context_send_headers(context, this, 1, 1); + us_quic_socket_context_send_headers(nullptr, (us_quic_stream_t *) this, 1, 1); /* Write body and shutdown (unknown if content-length must be present?) */ - us_quic_stream_write(this, "Hello quic!", 11); + us_quic_stream_write((us_quic_stream_t *) this, (char *) data.data(), data.length()); /* Every request has its own stream, so we conceptually serve requests like in HTTP 1.0 */ - us_quic_stream_shutdown(this); + us_quic_stream_shutdown((us_quic_stream_t *) this); } }; diff --git a/src/Http3ResponseData.h b/src/Http3ResponseData.h index e69de29..743da1d 100644 --- a/src/Http3ResponseData.h +++ b/src/Http3ResponseData.h @@ -0,0 +1,5 @@ +namespace uWS { + struct Http3ResponseData { + + }; +} \ No newline at end of file diff --git a/src/HttpRouter.h b/src/HttpRouter.h index d9b0dff..ee611c8 100644 --- a/src/HttpRouter.h +++ b/src/HttpRouter.h @@ -35,6 +35,7 @@ template struct HttpRouter { /* These are public for now */ std::vector methods = {"get", "post", "head", "put", "delete", "connect", "options", "trace", "patch"}; + std::vector upperCasedMethods = {"GET", "POST", "HEAD", "PUT", "DELETE", "CONNECT", "OPTIONS", "TRACE", "PATCH"}; static const uint32_t HIGH_PRIORITY = 0xd0000000, MEDIUM_PRIORITY = 0xe0000000, LOW_PRIORITY = 0xf0000000; private: diff --git a/uSockets b/uSockets index e66bf99..4528c2f 160000 --- a/uSockets +++ b/uSockets @@ -1 +1 @@ -Subproject commit e66bf99d5afd3b86fb085a71a946dad4efb177ea +Subproject commit 4528c2f9dd686a27aa644911434d1da3b6312236