Add per-SNI HttpRouter support (initial)

This commit is contained in:
Alex Hultman
2022-09-26 00:22:30 +02:00
parent c77cae4635
commit b1b931edfc
5 changed files with 49 additions and 37 deletions
+10 -30
View File
@@ -2,46 +2,26 @@
/* Note that uWS::SSLApp({options}) is the same as uWS::App() when compiled without SSL support */
struct us_listen_socket_t *globalListenSocket;
int main() {
/* Overly simple hello world app (SNI) */
/* The SSL context given in SSLApp constructor is the default / catch-all context */
uWS::SSLApp app = uWS::SSLApp({
.key_file_name = "misc/key.pem",
.cert_file_name = "misc/cert.pem",
.passphrase = "1234"
}).missingServerName([&app](const char *hostname) {
printf("We are missing server name: <%s>\n", hostname);
/* Assume it is localhost, so add it */
app.addServerName("localhost", {
.key_file_name = "../misc/key.pem",
.cert_file_name = "../misc/cert.pem",
.passphrase = "1234"
});
}).get("/*", [](auto *res, auto */*req*/) {
res->end("Hello world!");
}).get("/exit", [](auto *res, auto */*req*/) {
res->end("Shutting down!");
/* We use this to check graceful closedown */
us_listen_socket_close(1, globalListenSocket);
res->end("Hello from catch-all context!");
}).addServerName("*.google.*", {
/* Following is the context for *.google.* domain */
.key_file_name = "misc/key.pem",
.cert_file_name = "misc/cert.pem",
.passphrase = "1234"
}).domain("*.google.*").get("/*", [](auto *res, auto */*req*/) {
res->end("Hello from *.google.* context!");
}).listen(3000, [](auto *listenSocket) {
if (listenSocket) {
std::cout << "Listening on port " << 3000 << std::endl;
globalListenSocket = listenSocket;
} else {
std::cout << "Failed to listen on port 3000" << std::endl;
}
});
/* Let's add a wildcard SNI to begin with */
app.addServerName("*.google.*", {
.key_file_name = "../misc/key.pem",
.cert_file_name = "../misc/cert.pem",
.passphrase = "1234"
});
app.run();
}).run();
}
+20 -1
View File
@@ -104,7 +104,10 @@ public:
/* Server name */
TemplatedApp &&addServerName(std::string hostname_pattern, SocketContextOptions options = {}) {
us_socket_context_add_server_name(SSL, (struct us_socket_context_t *) httpContext, hostname_pattern.c_str(), options);
/* First we create a new router for this domain */
auto *domainRouter = new HttpRouter<typename HttpContextData<SSL>::RouterData>();
us_socket_context_add_server_name(SSL, (struct us_socket_context_t *) httpContext, hostname_pattern.c_str(), options, domainRouter);
return std::move(*this);
}
@@ -407,6 +410,22 @@ public:
return std::move(*this);
}
/* Browse to a server name, changing the router to this domain */
TemplatedApp &&domain(std::string serverName) {
HttpContextData<SSL> *httpContextData = httpContext->getSocketContextData();
void *domainRouter = us_socket_context_find_server_name_userdata(SSL, (struct us_socket_context_t *) httpContext, serverName.c_str());
if (domainRouter) {
std::cout << "Browsed to SNI: " << serverName << std::endl;
httpContextData->currentRouter = (decltype(httpContextData->currentRouter)) domainRouter;
} else {
std::cout << "Cannot browse to SNI: " << serverName << std::endl;
httpContextData->currentRouter = &httpContextData->router;
}
return std::move(*this);
}
TemplatedApp &&get(std::string pattern, MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler) {
if (httpContext) {
httpContext->onHttp("get", pattern, std::move(handler));
+14 -5
View File
@@ -159,9 +159,18 @@ private:
httpResponseData->state |= HttpResponseData<SSL>::HTTP_CONNECTION_CLOSE;
}
/* Select the router based on SNI (only possible for SSL) */
auto *selectedRouter = &httpContextData->router;
if constexpr (SSL) {
void *domainRouter = us_socket_server_name_userdata(SSL, (struct us_socket_t *) s);
if (domainRouter) {
selectedRouter = (decltype(selectedRouter)) domainRouter;
}
}
/* Route the method and URL */
httpContextData->router.getUserData() = {(HttpResponse<SSL> *) s, httpRequest};
if (!httpContextData->router.route(httpRequest->getMethod(), httpRequest->getUrl())) {
selectedRouter->getUserData() = {(HttpResponse<SSL> *) s, httpRequest};
if (!selectedRouter->route(httpRequest->getMethod(), httpRequest->getUrl())) {
/* We have to force close this socket as we have no handler for it */
us_socket_close(SSL, (us_socket_t *) s, 0, nullptr);
return nullptr;
@@ -406,12 +415,12 @@ public:
/* Todo: This is ugly, fix */
std::vector<std::string> methods;
if (method == "*") {
methods = httpContextData->router.methods;
methods = httpContextData->currentRouter->methods;
} else {
methods = {method};
}
httpContextData->router.add(methods, pattern, [handler = std::move(handler)](auto *r) mutable {
httpContextData->currentRouter->add(methods, pattern, [handler = std::move(handler)](auto *r) mutable {
auto user = r->getUserData();
user.httpRequest->setYield(false);
user.httpRequest->setParameters(r->getParameters());
@@ -429,7 +438,7 @@ public:
return false;
}
return true;
}, method == "*" ? httpContextData->router.LOW_PRIORITY : (upgrade ? httpContextData->router.HIGH_PRIORITY : httpContextData->router.MEDIUM_PRIORITY));
}, method == "*" ? httpContextData->currentRouter->LOW_PRIORITY : (upgrade ? httpContextData->currentRouter->HIGH_PRIORITY : httpContextData->currentRouter->MEDIUM_PRIORITY));
}
/* Listen to port using this HttpContext */
+4
View File
@@ -42,6 +42,10 @@ private:
HttpRequest *httpRequest;
};
/* This is the currently browsed-to router when using SNI */
HttpRouter<RouterData> *currentRouter = &router;
/* This is the default router for default SNI or non-SSL */
HttpRouter<RouterData> router;
void *upgradedWebSocket = nullptr;
bool isParsingHttp = false;