From f7407789975ac62926948d822555340341412dae Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Mon, 29 Aug 2022 02:19:24 +0200 Subject: [PATCH] Http3 POST/GET example working --- examples/Http3Server.cpp | 41 ++++++++++++++++++++++++++++++++++++---- src/Http3Context.h | 10 +++++++--- src/Http3Response.h | 20 ++++++++++++++++++-- src/Http3ResponseData.h | 8 +++++--- 4 files changed, 67 insertions(+), 12 deletions(-) diff --git a/examples/Http3Server.cpp b/examples/Http3Server.cpp index 329bb9a..5079d59 100644 --- a/examples/Http3Server.cpp +++ b/examples/Http3Server.cpp @@ -4,19 +4,52 @@ #include "Http3App.h" #include -/* Tentative example of simple Http3 server */ +/* Example of simple Http3 server handling both GET with headers and POST with body. + * You might be surprised to find out you can replace uWS::H3App with uWS::SSLApp and + * serve TCP/TLS-based HTTP instead of QUIC-based HTTP, using the same very code ;) */ int main() { - uWS::H3App({ + uWS::SSLApp({ .key_file_name = "misc/key.pem", .cert_file_name = "misc/cert.pem", .passphrase = "1234" }).get("/*", [](auto *res, auto *req) { /* Printing these should obviously be disabled if doing benchmarking */ - //std::cout << req->getHeader(":path") << std::endl; - //std::cout << req->getHeader(":method") << std::endl; + std::cout << req->getHeader(":path") << std::endl; + std::cout << req->getHeader(":method") << std::endl; res->end("Hello H3 from uWS!"); + }).post("/*", [](auto *res, auto */*req*/) { + + /* You also need to set onAborted if receiving data */ + res->onData([res, bodyBuffer = (std::string *)nullptr](std::string_view chunk, bool isLast) mutable { + if (isLast) { + if (bodyBuffer) { + /* Send back the (chunked) body we got, as response */ + bodyBuffer->append(chunk); + res->end(*bodyBuffer); + delete bodyBuffer; + } else { + /* Send back the body we got, as response (fast path) */ + res->end(chunk); + } + } else { + /* Slow path */ + if (!bodyBuffer) { + bodyBuffer = new std::string; + } + /* If we got the body in a chunk, buffer it up until whole */ + bodyBuffer->append(chunk); + } + + }); + + /* If you have pending, asynch work, you should abort such work in this callback */ + res->onAborted([]() { + /* Again, just printing is not enough, you need to abort any pending work here + * so that nothing will call res->end, since the request was aborted and deleted */ + printf("Stream was aborted!\n"); + }); }).listen(9004, [](auto *listen_socket) { if (listen_socket) { std::cout << "Listening on port " << 9004 << std::endl; diff --git a/src/Http3Context.h b/src/Http3Context.h index 7a2f4d5..f6d5664 100644 --- a/src/Http3Context.h +++ b/src/Http3Context.h @@ -19,10 +19,14 @@ namespace uWS { /* Specify application callbacks */ us_quic_socket_context_on_stream_data(context, [](us_quic_stream_t *s, char *data, int length) { - // Http3ResponseData *responseData = us_quic_stream_ext(s); - // responseData->onData(data, length); + // we don't have a way to know EOF? + Http3ResponseData *responseData = (Http3ResponseData *) us_quic_stream_ext(s); + + if (responseData) { + responseData->onData({data, length}, true); + } - printf("Body length is: %d\n", 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"); diff --git a/src/Http3Response.h b/src/Http3Response.h index 894c04d..4d5f245 100644 --- a/src/Http3Response.h +++ b/src/Http3Response.h @@ -29,8 +29,8 @@ namespace uWS { Http3ResponseData *responseData = (Http3ResponseData *) us_quic_stream_ext((us_quic_stream_t *) this); - printf("Wrapper responseData is %p\n", responseData); - printf("%s\n", responseData); + //printf("Wrapper responseData is %p\n", responseData); + //printf("%s\n", responseData); // if not already written status then write status @@ -46,6 +46,22 @@ namespace uWS { /* Every request has its own stream, so we conceptually serve requests like in HTTP 1.0 */ us_quic_stream_shutdown((us_quic_stream_t *) this); } + + + /* Attach handler for aborted HTTP request */ + Http3Response *onAborted(MoveOnlyFunction &&handler) { + Http3ResponseData *responseData = (Http3ResponseData *) us_quic_stream_ext((us_quic_stream_t *) this); + + responseData->onAborted = std::move(handler); + return this; + } + + /* Attach a read handler for data sent. Will be called with FIN set true if last segment. */ + void onData(MoveOnlyFunction &&handler) { + Http3ResponseData *responseData = (Http3ResponseData *) us_quic_stream_ext((us_quic_stream_t *) this); + + responseData->onData = std::move(handler); + } }; } \ No newline at end of file diff --git a/src/Http3ResponseData.h b/src/Http3ResponseData.h index 87ccdc1..a1c0201 100644 --- a/src/Http3ResponseData.h +++ b/src/Http3ResponseData.h @@ -1,12 +1,14 @@ #ifndef UWS_H3RESPONSEDATA_H #define UWS_H3RESPONSEDATA_H +#include "MoveOnlyFunction.h" +#include + namespace uWS { struct Http3ResponseData { - - // will need onWritable - // onData + MoveOnlyFunction onAborted; + MoveOnlyFunction onData; // hasWrittenStatus