chore: HttpContext.h cleanup (#24730)

This commit is contained in:
Marko Vejnovic
2025-11-17 13:36:03 -08:00
committed by GitHub
parent 509a97a435
commit 9513c1d1d9
2 changed files with 76 additions and 96 deletions

View File

@@ -38,6 +38,56 @@
namespace uWS {
namespace detail {
template <typename T, typename... Args>
[[nodiscard]] constexpr auto makeArray(T&& el0, Args&&... values) noexcept {
return std::array<std::decay_t<T>, 1 + sizeof...(Args)>{
std::forward<T>(el0), std::forward<Args>(values)...
};
}
static constexpr auto supportedHttpMethods = makeArray<std::string_view>(
"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<bool> struct HttpResponse;
template <bool SSL>
@@ -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<const std::string, 35> HTTP_METHODS = {
#define MACRO(name) std::string {name},
FOR_EACH_HTTP_METHOD(MACRO)
#undef MACRO
};
static std::span<const std::string> getAllHttpMethods() {
return {HTTP_METHODS.data(), HTTP_METHODS.size()};
}
#else
// Windows, and older C++ can't do constexpr std::array<const std::string, 35>
static constexpr std::array<const char*, 35> HTTP_METHODS = {
#define MACRO(name) name,
FOR_EACH_HTTP_METHOD(MACRO)
#undef MACRO
};
static std::span<const std::string> getAllHttpMethods() {
static std::once_flag flag;
static std::array<std::string, 35> 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<SSL> *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<HttpResponse<SSL> *>(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<AsyncSocket<SSL> *>(s);
auto *httpResponseData = reinterpret_cast<HttpResponseData<SSL> *>(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<AsyncSocket<SSL> *>(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<void(HttpResponse<SSL> *, HttpRequest *)> &&handler, bool upgrade = false) {
HttpContextData<SSL> *httpContextData = getSocketContextData();
std::span<const std::string> methods;
std::array<std::string, 1> methods_buffer;
std::span<const std::string_view> 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);

View File

@@ -33,13 +33,13 @@
namespace uWS {
template <class USERDATA>
template <typename UserDataType>
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<uint32_t> 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> &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<Node> newNode(new Node(child));
auto newNode = std::make_unique<Node>(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<std::string_view, bool> getUrlSegment(int urlSegment) {
std::pair<std::string_view, bool> 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<const std::string> &methods, std::string_view pattern, MoveOnlyFunction<bool(HttpRouter *)> &&handler, uint32_t priority = MEDIUM_PRIORITY) {
void add(std::span<const std::string_view> methods, std::string_view pattern, MoveOnlyFunction<bool(HttpRouter *)> &&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
#endif // UWS_HTTPROUTER_HPP