From 9513c1d1d9a723828eefe863f0b0fbdd08921904 Mon Sep 17 00:00:00 2001 From: Marko Vejnovic Date: Mon, 17 Nov 2025 13:36:03 -0800 Subject: [PATCH] chore: HttpContext.h cleanup (#24730) --- packages/bun-uws/src/HttpContext.h | 146 +++++++++++++---------------- packages/bun-uws/src/HttpRouter.h | 26 ++--- 2 files changed, 76 insertions(+), 96 deletions(-) diff --git a/packages/bun-uws/src/HttpContext.h b/packages/bun-uws/src/HttpContext.h index 2d1e7b79b9..5ac6695383 100644 --- a/packages/bun-uws/src/HttpContext.h +++ b/packages/bun-uws/src/HttpContext.h @@ -38,6 +38,56 @@ namespace uWS { + +namespace detail { + +template +[[nodiscard]] constexpr auto makeArray(T&& el0, Args&&... values) noexcept { + return std::array, 1 + sizeof...(Args)>{ + std::forward(el0), std::forward(values)... + }; +} + +static constexpr auto supportedHttpMethods = makeArray( + "ACL", + "BIND", + "CHECKOUT", + "CONNECT", + "COPY", + "DELETE", + "GET", + "HEAD", + "LINK", + "LOCK", + "M-SEARCH", + "MERGE", + "MKACTIVITY", + "MKCALENDAR", + "MKCOL", + "MOVE", + "NOTIFY", + "OPTIONS", + "PATCH", + "POST", + "PROPFIND", + "PROPPATCH", + "PURGE", + "PUT", + "QUERY", + "REBIND", + "REPORT", + "SEARCH", + "SOURCE", + "SUBSCRIBE", + "TRACE", + "UNBIND", + "UNLINK", + "UNLOCK", + "UNSUBSCRIBE" +); + +} // namespace detail + template struct HttpResponse; template @@ -53,78 +103,6 @@ private: /* Minimum allowed receive throughput per second (clients uploading less than 16kB/sec get dropped) */ static constexpr int HTTP_RECEIVE_THROUGHPUT_BYTES = 16 * 1024; - -#define FOR_EACH_HTTP_METHOD(MACRO) \ - MACRO("ACL") \ - MACRO("BIND") \ - MACRO("CHECKOUT") \ - MACRO("CONNECT") \ - MACRO("COPY") \ - MACRO("DELETE") \ - MACRO("GET") \ - MACRO("HEAD") \ - MACRO("LINK") \ - MACRO("LOCK") \ - MACRO("M-SEARCH") \ - MACRO("MERGE") \ - MACRO("MKACTIVITY") \ - MACRO("MKCALENDAR") \ - MACRO("MKCOL") \ - MACRO("MOVE") \ - MACRO("NOTIFY") \ - MACRO("OPTIONS") \ - MACRO("PATCH") \ - MACRO("POST") \ - MACRO("PROPFIND") \ - MACRO("PROPPATCH") \ - MACRO("PURGE") \ - MACRO("PUT") \ - MACRO("QUERY") \ - MACRO("REBIND") \ - MACRO("REPORT") \ - MACRO("SEARCH") \ - MACRO("SOURCE") \ - MACRO("SUBSCRIBE") \ - MACRO("TRACE") \ - MACRO("UNBIND") \ - MACRO("UNLINK") \ - MACRO("UNLOCK") \ - MACRO("UNSUBSCRIBE") \ - - -#ifndef _WIN32 - static constexpr std::array HTTP_METHODS = { - #define MACRO(name) std::string {name}, - FOR_EACH_HTTP_METHOD(MACRO) - #undef MACRO - }; - static std::span getAllHttpMethods() { - return {HTTP_METHODS.data(), HTTP_METHODS.size()}; - } -#else - // Windows, and older C++ can't do constexpr std::array - static constexpr std::array HTTP_METHODS = { - #define MACRO(name) name, - FOR_EACH_HTTP_METHOD(MACRO) - #undef MACRO - }; - - static std::span getAllHttpMethods() { - static std::once_flag flag; - static std::array methods; - std::call_once(flag, []() { - methods = { - #define MACRO(name) std::string {name}, - FOR_EACH_HTTP_METHOD(MACRO) - #undef MACRO - }; - }); - return {methods.data(), methods.size()}; - } -#endif -#undef FOR_EACH_HTTP_METHOD - - us_socket_context_t *getSocketContext() { return (us_socket_context_t *) this; } @@ -195,7 +173,7 @@ private: /* Call filter */ HttpContextData *httpContextData = getSocketContextDataS(s); - + if(httpResponseData && httpResponseData->isConnectRequest) { if (httpResponseData->socketData && httpContextData->onSocketData) { httpContextData->onSocketData(httpResponseData->socketData, SSL, s, "", 0, true); @@ -203,7 +181,7 @@ private: if(httpResponseData->inStream) { httpResponseData->inStream(reinterpret_cast *>(s), "", 0, true, httpResponseData->userData); httpResponseData->inStream = nullptr; - } + } } @@ -253,7 +231,7 @@ private: /* Mark that we are inside the parser now */ httpContextData->flags.isParsingHttp = true; httpResponseData->isIdle = false; - + // clients need to know the cursor after http parse, not servers! // how far did we read then? we need to know to continue with websocket parsing data? or? @@ -266,7 +244,7 @@ private: auto result = httpResponseData->consumePostPadded(httpContextData->maxHeaderSize, httpResponseData->isConnectRequest, httpContextData->flags.requireHostHeader,httpContextData->flags.useStrictMethodValidation, data, (unsigned int) length, s, proxyParser, [httpContextData](void *s, HttpRequest *httpRequest) -> void * { - + /* For every request we reset the timeout and hang until user makes action */ /* Warning: if we are in shutdown state, resetting the timer is a security issue! */ us_socket_timeout(SSL, (us_socket_t *) s, 0); @@ -465,7 +443,7 @@ private: us_socket_context_on_writable(SSL, getSocketContext(), [](us_socket_t *s) { auto *asyncSocket = reinterpret_cast *>(s); auto *httpResponseData = reinterpret_cast *>(asyncSocket->getAsyncSocketData()); - + /* Attempt to drain the socket buffer before triggering onWritable callback */ size_t bufferedAmount = asyncSocket->getBufferedAmount(); if (bufferedAmount > 0) { @@ -536,7 +514,7 @@ private: us_socket_context_on_end(SSL, getSocketContext(), [](us_socket_t *s) { auto *asyncSocket = reinterpret_cast *>(s); asyncSocket->uncorkWithoutSending(); - + /* We do not care for half closed sockets */ return asyncSocket->close(); }); @@ -602,14 +580,16 @@ public: void onHttp(std::string_view method, std::string_view pattern, MoveOnlyFunction *, HttpRequest *)> &&handler, bool upgrade = false) { HttpContextData *httpContextData = getSocketContextData(); - std::span methods; - std::array methods_buffer; + std::span methods; + std::string method_buffer; + std::string_view method_sv_buffer; // When it's NOT node:http, allow the uWS default precedence ordering. if (method == "*" && !httpContextData->flags.useStrictMethodValidation) { - methods = getAllHttpMethods(); + methods = detail::supportedHttpMethods; } else { - methods_buffer[0] = std::string(method); - methods = {methods_buffer.data(), 1}; + method_buffer = std::string(method); + method_sv_buffer = std::string_view(method_buffer); + methods = {&method_sv_buffer, 1}; } uint32_t priority = method == "*" ? httpContextData->currentRouter->LOW_PRIORITY : (upgrade ? httpContextData->currentRouter->HIGH_PRIORITY : httpContextData->currentRouter->MEDIUM_PRIORITY); diff --git a/packages/bun-uws/src/HttpRouter.h b/packages/bun-uws/src/HttpRouter.h index fffd02364e..d90311899e 100644 --- a/packages/bun-uws/src/HttpRouter.h +++ b/packages/bun-uws/src/HttpRouter.h @@ -33,13 +33,13 @@ namespace uWS { -template +template struct HttpRouter { static constexpr std::string_view ANY_METHOD_TOKEN = "*"; static constexpr uint32_t HIGH_PRIORITY = 0xd0000000, MEDIUM_PRIORITY = 0xe0000000, LOW_PRIORITY = 0xf0000000; private: - USERDATA userData; + UserDataType userData; static const unsigned int MAX_URL_SEGMENTS = 100; /* Handler ids are 32-bit */ @@ -60,8 +60,8 @@ private: std::vector handlers = {}; bool isHighPriority = false; - Node(std::string name) : name(std::move(name)) {} - } root = {"rootNode"}; + explicit constexpr Node(std::string name) noexcept : name(std::move(name)) {} + } root {"rootNode"}; /* Sort wildcards after alphanum */ int lexicalOrder(std::string_view name) { @@ -78,7 +78,7 @@ private: } /* Advance from parent to child, adding child if necessary */ - Node *getNode(Node *parent, std::string child, bool isHighPriority) { + Node *getNode(Node *parent, std::string_view child, bool isHighPriority) { for (const std::unique_ptr &node : parent->children) { if (node->name == child && node->isHighPriority == isHighPriority) { return node.get(); @@ -86,7 +86,7 @@ private: } /* Insert sorted, but keep order if parent is root (we sort methods by priority elsewhere) */ - std::unique_ptr newNode(new Node(child)); + auto newNode = std::make_unique(std::string(child)); newNode->isHighPriority = isHighPriority; auto iter = std::upper_bound(parent->children.begin(), parent->children.end(), newNode, [parent, this](auto &a, auto &b) { if (a->isHighPriority != b->isHighPriority) { @@ -120,7 +120,7 @@ private: } routeParameters; /* Set URL for router. Will reset any URL cache */ - inline void setUrl(std::string_view url) { + void setUrl(std::string_view url) { /* Todo: URL may also start with "http://domain/" or "*", not only "/" */ @@ -130,7 +130,7 @@ private: } /* Lazily parse or read from cache */ - inline std::pair getUrlSegment(int urlSegment) { + std::pair getUrlSegment(int urlSegment) { if (urlSegment > urlSegmentTop) { /* Signal as STOP when we have no more URL or stack space */ if (!currentUrl.length() || urlSegment > int(MAX_URL_SEGMENTS - 1)) { @@ -164,7 +164,7 @@ private: } /* Executes as many handlers it can */ - bool executeHandlers(Node *parent, int urlSegment, USERDATA &userData) { + bool executeHandlers(Node *parent, int urlSegment, UserDataType &userData) { auto [segment, isStop] = getUrlSegment(urlSegment); @@ -248,7 +248,7 @@ public: return {routeParameters.paramsTop, routeParameters.params}; } - USERDATA &getUserData() { + UserDataType &getUserData() { return userData; } @@ -278,11 +278,11 @@ public: } /* Adds the corresponding entires in matching tree and handler list */ - void add(const std::span &methods, std::string_view pattern, MoveOnlyFunction &&handler, uint32_t priority = MEDIUM_PRIORITY) { + void add(std::span methods, std::string_view pattern, MoveOnlyFunction &&handler, uint32_t priority = MEDIUM_PRIORITY) { /* First remove existing handler */ remove(methods[0], pattern, priority); - for (const std::string &method : methods) { + for (const std::string_view method : methods) { /* Lookup method */ Node *node = getNode(&root, method, false); /* Iterate over all segments */ @@ -381,4 +381,4 @@ public: } -#endif // UWS_HTTPROUTER_HPP \ No newline at end of file +#endif // UWS_HTTPROUTER_HPP