Hook up h3 router and callbacks

This commit is contained in:
Alex Hultman
2022-05-15 14:39:34 +02:00
parent 7da868a57d
commit 73c9262f34
9 changed files with 205 additions and 88 deletions
+7 -5
View File
@@ -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();
+84 -9
View File
@@ -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<void(void *)> 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<void(Http3Response *, Http3Request *)> cb) {
// http3Context->onHttp, internally using httprouter
H3App &&listen(int port, std::function<void(void *)> cb) {
http3Context->listen();
return std::move(*this);
}
return *this;
H3App &&get(std::string pattern, MoveOnlyFunction<void(Http3Response *, Http3Request *)> &&handler) {
if (http3Context) {
http3Context->onHttp("GET", pattern, std::move(handler));
}
return std::move(*this);
}
H3App &&post(std::string pattern, MoveOnlyFunction<void(Http3Response *, Http3Request *)> &&handler) {
if (http3Context) {
http3Context->onHttp("POST", pattern, std::move(handler));
}
return std::move(*this);
}
H3App &&options(std::string pattern, MoveOnlyFunction<void(Http3Response *, Http3Request *)> &&handler) {
if (http3Context) {
http3Context->onHttp("OPTIONS", pattern, std::move(handler));
}
return std::move(*this);
}
H3App &&del(std::string pattern, MoveOnlyFunction<void(Http3Response *, Http3Request *)> &&handler) {
if (http3Context) {
http3Context->onHttp("DELETE", pattern, std::move(handler));
}
return std::move(*this);
}
H3App &&patch(std::string pattern, MoveOnlyFunction<void(Http3Response *, Http3Request *)> &&handler) {
if (http3Context) {
http3Context->onHttp("PATCH", pattern, std::move(handler));
}
return std::move(*this);
}
H3App &&put(std::string pattern, MoveOnlyFunction<void(Http3Response *, Http3Request *)> &&handler) {
if (http3Context) {
http3Context->onHttp("PUT", pattern, std::move(handler));
}
return std::move(*this);
}
H3App &&head(std::string pattern, MoveOnlyFunction<void(Http3Response *, Http3Request *)> &&handler) {
if (http3Context) {
http3Context->onHttp("HEAD", pattern, std::move(handler));
}
return std::move(*this);
}
H3App &&connect(std::string pattern, MoveOnlyFunction<void(Http3Response *, Http3Request *)> &&handler) {
if (http3Context) {
http3Context->onHttp("CONNECT", pattern, std::move(handler));
}
return std::move(*this);
}
H3App &&trace(std::string pattern, MoveOnlyFunction<void(Http3Response *, Http3Request *)> &&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<void(Http3Response *, Http3Request *)> &&handler) {
if (http3Context) {
http3Context->onHttp("*", pattern, std::move(handler));
}
return std::move(*this);
}
void run() {
uWS::Loop::get()->run();
}
};
}
+85 -67
View File
@@ -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<void(Http3Response *, Http3Request *)> &&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<std::string> 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<Http3ContextData::RouterData> *router) mutable {
Http3ContextData::RouterData &routerData = router->getUserData();
handler(routerData.res, routerData.req);
return true;
});
}
};
}
+13 -1
View File
@@ -1,9 +1,21 @@
#include "HttpRouter.h"
struct Http3Response;
struct Http3Request;
namespace uWS {
struct Http3ContextData {
HttpRouter<int> router;
struct RouterData {
Http3Response *res;
Http3Request *req;
};
HttpRouter<RouterData> router;
Http3ContextData() {
printf("Constructing http3contextdata: %p\n", this);
}
};
}
+1 -1
View File
@@ -15,8 +15,8 @@ namespace uWS {
return {value, value_length};
}
}
return {nullptr, 0};
}
return {nullptr, 0};
}
};
}
+8 -4
View File
@@ -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);
}
};
+5
View File
@@ -0,0 +1,5 @@
namespace uWS {
struct Http3ResponseData {
};
}
+1
View File
@@ -35,6 +35,7 @@ template <class USERDATA>
struct HttpRouter {
/* These are public for now */
std::vector<std::string> methods = {"get", "post", "head", "put", "delete", "connect", "options", "trace", "patch"};
std::vector<std::string> upperCasedMethods = {"GET", "POST", "HEAD", "PUT", "DELETE", "CONNECT", "OPTIONS", "TRACE", "PATCH"};
static const uint32_t HIGH_PRIORITY = 0xd0000000, MEDIUM_PRIORITY = 0xe0000000, LOW_PRIORITY = 0xf0000000;
private: