Improve uWS route performance (#17884)

This commit is contained in:
Kai Tamkun
2025-03-03 18:24:35 -08:00
committed by GitHub
parent 9141337c7d
commit 1803f73b15
10 changed files with 100 additions and 108 deletions

View File

@@ -68,11 +68,11 @@ namespace uWS {
int ssl_prefer_low_memory_usage = 0;
const char **key = nullptr;
unsigned int key_count = 0;
unsigned int key_count = 0;
const char **cert = nullptr;
unsigned int cert_count = 0;
unsigned int cert_count = 0;
const char **ca = nullptr;
unsigned int ca_count = 0;
unsigned int ca_count = 0;
unsigned int secure_options = 0;
int reject_unauthorized = 0;
int request_cert = 0;
@@ -105,7 +105,7 @@ public:
/* Server name */
TemplatedApp &&addServerName(std::string hostname_pattern, SocketContextOptions options = {}, bool *success = nullptr) {
TemplatedApp &&addServerName(const std::string &hostname_pattern, SocketContextOptions options = {}, bool *success = nullptr) {
/* Do nothing if not even on SSL */
if constexpr (SSL) {
@@ -121,8 +121,8 @@ public:
return std::move(*this);
}
TemplatedApp &&removeServerName(std::string hostname_pattern) {
TemplatedApp &&removeServerName(const std::string &hostname_pattern) {
/* This will do for now, would be better if us_socket_context_remove_server_name returned the user data */
auto *domainRouter = us_socket_context_find_server_name_userdata(SSL, (struct us_socket_context_t *) httpContext, hostname_pattern.c_str());
if (domainRouter) {
@@ -133,7 +133,7 @@ public:
return std::move(*this);
}
TemplatedApp &&missingServerName(MoveOnlyFunction<void(const char *hostname)> handler) {
TemplatedApp &&missingServerName(MoveOnlyFunction<void(const char *hostname)> &&handler) {
if (!constructorFailed()) {
httpContext->getSocketContextData()->missingServerNameHandler = std::move(handler);
@@ -294,7 +294,7 @@ public:
}
template <typename UserData>
TemplatedApp &&ws(std::string pattern, WebSocketBehavior<UserData> &&behavior) {
TemplatedApp &&ws(std::string_view pattern, WebSocketBehavior<UserData> &&behavior) {
/* Don't compile if alignment rules cannot be satisfied */
static_assert(alignof(UserData) <= LIBUS_EXT_ALIGNMENT,
"µWebSockets cannot satisfy UserData alignment requirements. You need to recompile µSockets with LIBUS_EXT_ALIGNMENT adjusted accordingly.");
@@ -473,7 +473,7 @@ public:
}
/* Browse to a server name, changing the router to this domain */
TemplatedApp &&domain(std::string serverName) {
TemplatedApp &&domain(const 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());
@@ -482,46 +482,46 @@ public:
} else {
httpContextData->currentRouter = &httpContextData->router;
}
return std::move(*this);
}
TemplatedApp &&get(std::string pattern, MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler) {
TemplatedApp &&get(std::string_view pattern, MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler) {
if (httpContext) {
httpContext->onHttp("GET", pattern, std::move(handler));
}
return std::move(*this);
}
TemplatedApp &&post(std::string pattern, MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler) {
TemplatedApp &&post(std::string_view pattern, MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler) {
if (httpContext) {
httpContext->onHttp("POST", pattern, std::move(handler));
}
return std::move(*this);
}
TemplatedApp &&options(std::string pattern, MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler) {
TemplatedApp &&options(std::string_view pattern, MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler) {
if (httpContext) {
httpContext->onHttp("OPTIONS", pattern, std::move(handler));
}
return std::move(*this);
}
TemplatedApp &&del(std::string pattern, MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler) {
TemplatedApp &&del(std::string_view pattern, MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler) {
if (httpContext) {
httpContext->onHttp("DELETE", pattern, std::move(handler));
}
return std::move(*this);
}
TemplatedApp &&patch(std::string pattern, MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler) {
TemplatedApp &&patch(std::string_view pattern, MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler) {
if (httpContext) {
httpContext->onHttp("PATCH", pattern, std::move(handler));
}
return std::move(*this);
}
TemplatedApp &&put(std::string pattern, MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler) {
TemplatedApp &&put(std::string_view pattern, MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler) {
if (httpContext) {
httpContext->onHttp("PUT", pattern, std::move(handler));
}
@@ -535,21 +535,21 @@ public:
}
TemplatedApp &&head(std::string pattern, MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler) {
TemplatedApp &&head(std::string_view pattern, MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler) {
if (httpContext) {
httpContext->onHttp("HEAD", pattern, std::move(handler));
}
return std::move(*this);
}
TemplatedApp &&connect(std::string pattern, MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler) {
TemplatedApp &&connect(std::string_view pattern, MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler) {
if (httpContext) {
httpContext->onHttp("CONNECT", pattern, std::move(handler));
}
return std::move(*this);
}
TemplatedApp &&trace(std::string pattern, MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler) {
TemplatedApp &&trace(std::string_view pattern, MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler) {
if (httpContext) {
httpContext->onHttp("TRACE", pattern, std::move(handler));
}
@@ -557,7 +557,7 @@ public:
}
/* This one catches any method */
TemplatedApp &&any(std::string pattern, MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler) {
TemplatedApp &&any(std::string_view pattern, MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler) {
if (httpContext) {
httpContext->onHttp("*", pattern, std::move(handler));
}
@@ -565,8 +565,8 @@ public:
}
/* Host, port, callback */
TemplatedApp &&listen(std::string host, int port, MoveOnlyFunction<void(us_listen_socket_t *)> &&handler) {
if (!host.length()) {
TemplatedApp &&listen(const std::string &host, int port, MoveOnlyFunction<void(us_listen_socket_t *)> &&handler) {
if (host.empty()) {
return listen(port, std::move(handler));
}
handler(httpContext ? httpContext->listen(host.c_str(), port, 0) : nullptr);
@@ -574,8 +574,8 @@ public:
}
/* Host, port, options, callback */
TemplatedApp &&listen(std::string host, int port, int options, MoveOnlyFunction<void(us_listen_socket_t *)> &&handler) {
if (!host.length()) {
TemplatedApp &&listen(const std::string &host, int port, int options, MoveOnlyFunction<void(us_listen_socket_t *)> &&handler) {
if (host.empty()) {
return listen(port, options, std::move(handler));
}
handler(httpContext ? httpContext->listen(host.c_str(), port, options) : nullptr);
@@ -595,13 +595,13 @@ public:
}
/* options, callback, path to unix domain socket */
TemplatedApp &&listen(int options, MoveOnlyFunction<void(us_listen_socket_t *)> &&handler, std::string path) {
TemplatedApp &&listen(int options, MoveOnlyFunction<void(us_listen_socket_t *)> &&handler, std::string_view path) {
handler(httpContext ? httpContext->listen_unix(path.data(), path.length(), options) : nullptr);
return std::move(*this);
}
/* callback, path to unix domain socket */
TemplatedApp &&listen(MoveOnlyFunction<void(us_listen_socket_t *)> &&handler, std::string path, int options) {
TemplatedApp &&listen(MoveOnlyFunction<void(us_listen_socket_t *)> &&handler, std::string_view path, int options) {
handler(httpContext ? httpContext->listen_unix(path.data(), path.length(), options) : nullptr);
return std::move(*this);
}

View File

@@ -39,6 +39,7 @@ struct BackPressure {
/* Always erase a minimum of 1/32th the current backpressure */
if (pendingRemoval > (buffer.length() >> 5)) {
buffer.erase(0, pendingRemoval);
buffer.shrink_to_fit();
pendingRemoval = 0;
}
}

View File

@@ -42,9 +42,9 @@ namespace uWS {
/* Reads hex number until CR or out of data to consume. Updates state. Returns bytes consumed. */
inline void consumeHexNumber(std::string_view &data, uint64_t &state) {
/* Consume everything higher than 32 */
while (data.length() && data.data()[0] > 32) {
while (data.length() && data[0] > 32) {
unsigned char digit = (unsigned char)data.data()[0];
unsigned char digit = (unsigned char)data[0];
if (digit >= 'a') {
digit = (unsigned char) (digit - ('a' - ':'));
} else if (digit >= 'A') {
@@ -67,7 +67,7 @@ namespace uWS {
data.remove_prefix(1);
}
/* Consume everything not /n */
while (data.length() && data.data()[0] != '\n') {
while (data.length() && data[0] != '\n') {
data.remove_prefix(1);
}
/* Now we stand on \n so consume it and enable size */

View File

@@ -22,7 +22,7 @@ namespace uWS {
//webSocketContext = WebSocketContext<0, false, int>::create();
}
ClientApp &&connect(std::string url, std::string protocol = "") {
ClientApp &&connect(std::string_view url, std::string_view protocol = "") {
return std::move(*this);
}

View File

@@ -33,8 +33,8 @@ namespace uWS {
}
/* Host, port, callback */
H3App &&listen(std::string host, int port, MoveOnlyFunction<void(us_listen_socket_t *)> &&handler) {
if (!host.length()) {
H3App &&listen(const std::string &host, int port, MoveOnlyFunction<void(us_listen_socket_t *)> &&handler) {
if (host.empty()) {
return listen(port, std::move(handler));
}
handler(http3Context ? (us_listen_socket_t *) http3Context->listen(host.c_str(), port) : nullptr);
@@ -42,8 +42,8 @@ namespace uWS {
}
/* Host, port, options, callback */
H3App &&listen(std::string host, int port, int options, MoveOnlyFunction<void(us_listen_socket_t *)> &&handler) {
if (!host.length()) {
H3App &&listen(const std::string &host, int port, int options, MoveOnlyFunction<void(us_listen_socket_t *)> &&handler) {
if (host.empty()) {
return listen(port, options, std::move(handler));
}
handler(http3Context ? (us_listen_socket_t *) http3Context->listen(host.c_str(), port) : nullptr);
@@ -62,63 +62,63 @@ namespace uWS {
return std::move(*this);
}
H3App &&get(std::string pattern, MoveOnlyFunction<void(Http3Response *, Http3Request *)> &&handler) {
H3App &&get(std::string_view 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) {
H3App &&post(std::string_view 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) {
H3App &&options(std::string_view 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) {
H3App &&del(std::string_view 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) {
H3App &&patch(std::string_view 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) {
H3App &&put(std::string_view 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) {
H3App &&head(std::string_view 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) {
H3App &&connect(std::string_view 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) {
H3App &&trace(std::string_view pattern, MoveOnlyFunction<void(Http3Response *, Http3Request *)> &&handler) {
if (http3Context) {
http3Context->onHttp("TRACE", pattern, std::move(handler));
}
@@ -126,7 +126,7 @@ namespace uWS {
}
/* This one catches any method */
H3App &&any(std::string pattern, MoveOnlyFunction<void(Http3Response *, Http3Request *)> &&handler) {
H3App &&any(std::string_view pattern, MoveOnlyFunction<void(Http3Response *, Http3Request *)> &&handler) {
if (http3Context) {
http3Context->onHttp("*", pattern, std::move(handler));
}

View File

@@ -17,7 +17,7 @@ namespace uWS {
us_quic_socket_context_on_stream_data(context, [](us_quic_stream_t *s, char *data, int length) {
Http3ResponseData *responseData = (Http3ResponseData *) us_quic_stream_ext(s);
/* We never emit FIN here */
if (responseData->onData) {
responseData->onData({data, (size_t) length}, false);
@@ -26,7 +26,7 @@ namespace uWS {
us_quic_socket_context_on_stream_end(context, [](us_quic_stream_t *s) {
Http3ResponseData *responseData = (Http3ResponseData *) us_quic_stream_ext(s);
/* Emit FIN to app */
if (responseData->onData) {
responseData->onData({nullptr, 0}, true);
@@ -77,7 +77,7 @@ namespace uWS {
std::string_view upperCasedMethod = req->getHeader(":method");
std::string_view path = req->getHeader(":path");
contextData->router.getUserData() = {(Http3Response *) s, (Http3Request *) nullptr};
contextData->router.route(upperCasedMethod, path);
@@ -92,7 +92,7 @@ namespace uWS {
//lsquic_stream_has_unacked_data
Http3ResponseData *responseData = (Http3ResponseData *) us_quic_stream_ext(s);
if (responseData->onAborted) {
responseData->onAborted();
}
@@ -130,9 +130,9 @@ namespace uWS {
}
// generic for get, post, any, etc
void onHttp(std::string method, std::string path, MoveOnlyFunction<void(Http3Response *, Http3Request *)> &&cb) {
void onHttp(std::string_view method, std::string_view 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 */
@@ -141,7 +141,7 @@ namespace uWS {
methods = contextData->router.upperCasedMethods; //bug! needs to be upper cased!
// router.upperCasedMethods;
} else {
methods = {method};
methods = {std::string(method)};
}
contextData->router.add(methods, path, [handler = std::move(cb)](HttpRouter<Http3ContextData::RouterData> *router) mutable {

View File

@@ -66,14 +66,14 @@ private:
/* Init the HttpContext by registering libusockets event handlers */
HttpContext<SSL> *init() {
if(SSL) {
// if we are SSL we need to handle the handshake properly
us_socket_context_on_handshake(SSL, getSocketContext(), [](us_socket_t *s, int success, struct us_bun_verify_error_t verify_error, void* custom_data) {
// if we are closing or already closed, we don't need to do anything
if (!us_socket_is_closed(SSL, s) && !us_socket_is_shut_down(SSL, s)) {
if (!us_socket_is_closed(SSL, s) && !us_socket_is_shut_down(SSL, s)) {
HttpContextData<SSL> *httpContextData = getSocketContextDataS(s);
if(httpContextData->rejectUnauthorized) {
if(!success || verify_error.error != 0) {
// we failed to handshake, close the socket
@@ -92,7 +92,7 @@ private:
}
}, nullptr);
}
/* Handle socket connections */
us_socket_context_on_open(SSL, getSocketContext(), [](us_socket_t *s, int /*is_client*/, char */*ip*/, int /*ip_length*/) {
/* Init socket ext */
@@ -115,7 +115,7 @@ private:
us_socket_context_on_close(SSL, getSocketContext(), [](us_socket_t *s, int /*code*/, void */*reason*/) {
((AsyncSocket<SSL> *)s)->uncorkWithoutSending();
/* Get socket ext */
HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) us_socket_ext(SSL, s);
@@ -129,7 +129,7 @@ private:
if (httpResponseData->onAborted) {
httpResponseData->onAborted((HttpResponse<SSL> *)s, httpResponseData->userData);
}
/* Destruct socket ext */
httpResponseData->~HttpResponseData<SSL>();
@@ -221,7 +221,7 @@ private:
}
/* Was the socket closed? */
if (us_socket_is_closed(SSL, (struct us_socket_t *) s)) {
if (us_socket_is_closed(SSL, (us_socket_t *) s)) {
return nullptr;
}
@@ -377,7 +377,7 @@ private:
return s;
}
/* We need to drain any remaining buffered data if success == true*/
/* We need to drain any remaining buffered data if success == true*/
}
/* Drain any socket buffer, this might empty our backpressure and thus finish the request */
@@ -441,7 +441,7 @@ public:
return nullptr;
}
// for servers this is only valid when request cert is enabled
/* Init socket context data */
auto* httpContextData = new ((HttpContextData<SSL> *) us_socket_context_ext(SSL, (us_socket_context_t *) httpContext)) HttpContextData<SSL>();
if(options.request_cert && options.reject_unauthorized) {
@@ -465,16 +465,10 @@ public:
}
/* Register an HTTP route handler acording to URL pattern */
void onHttp(std::string method, std::string pattern, MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler, bool upgrade = false) {
HttpContextData<SSL> *httpContextData = getSocketContextData();
void onHttp(std::string_view method, std::string_view pattern, MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler, bool upgrade = false) {
HttpContextData<SSL> *httpContextData = getSocketContextData();
/* Todo: This is ugly, fix */
std::vector<std::string> methods;
if (method == "*") {
methods = {"*"};
} else {
methods = {method};
}
std::vector<std::string> methods{std::string(method)};
uint32_t priority = method == "*" ? httpContextData->currentRouter->LOW_PRIORITY : (upgrade ? httpContextData->currentRouter->HIGH_PRIORITY : httpContextData->currentRouter->MEDIUM_PRIORITY);
@@ -495,7 +489,6 @@ public:
i++;
}
parameterOffsets[std::string(pattern.data() + start, i - start)] = offset;
//std::cout << "<" << std::string(pattern.data() + start, i - start) << "> is offset " << offset;
offset++;
}
}
@@ -529,7 +522,7 @@ public:
// we dont depend on libuv ref for keeping it alive
if (socket) {
us_socket_unref(&socket->s);
}
}
return socket;
}

View File

@@ -141,13 +141,13 @@ namespace uWS
std::string_view getFullUrl()
{
return std::string_view(headers->value.data(), headers->value.length());
return headers->value;
}
/* Hack: this should be getMethod */
std::string_view getCaseSensitiveMethod()
{
return std::string_view(headers->key.data(), headers->key.length());
return headers->key;
}
std::string_view getMethod()
@@ -158,7 +158,7 @@ namespace uWS
((char *)headers->key.data())[i] |= 32;
}
return std::string_view(headers->key.data(), headers->key.length());
return headers->key;
}
/* Returns the raw querystring as a whole, still encoded */
@@ -167,7 +167,7 @@ namespace uWS
if (querySeparator < headers->value.length())
{
/* Strip the initial ? */
return std::string_view(headers->value.data() + querySeparator + 1, headers->value.length() - querySeparator - 1);
return headers->value.substr(querySeparator + 1);
}
else
{
@@ -179,9 +179,7 @@ namespace uWS
std::string_view getQuery(std::string_view key)
{
/* Raw querystring including initial '?' sign */
std::string_view queryString = std::string_view(headers->value.data() + querySeparator, headers->value.length() - querySeparator);
return getDecodedQueryValue(key, queryString);
return getDecodedQueryValue(key, headers->value.substr(querySeparator));
}
void setParameters(std::pair<int, std::string_view *> parameters)
@@ -569,7 +567,7 @@ namespace uWS
* From here we return either [consumed, user] for "keep going",
* or [consumed, nullptr] for "break; I am closed or upgraded to websocket"
* or [whatever, fullptr] for "break and close me, I am a parser error!" */
template <int CONSUME_MINIMALLY>
template <bool ConsumeMinimally>
std::pair<unsigned int, void *> fenceAndConsumePostPadded(char *data, unsigned int length, void *user, void *reserved, HttpRequest *req, MoveOnlyFunction<void *(void *, HttpRequest *)> &requestHandler, MoveOnlyFunction<void *(void *, std::string_view, bool)> &dataHandler) {
/* How much data we CONSUMED (to throw away) */
@@ -669,7 +667,7 @@ namespace uWS
remainingStreamingBytes = STATE_IS_CHUNKED;
/* If consume minimally, we do not want to consume anything but we want to mark this as being chunked */
if (!CONSUME_MINIMALLY) {
if constexpr (!ConsumeMinimally) {
/* Go ahead and parse it (todo: better heuristics for emitting FIN to the app level) */
std::string_view dataToConsume(data, length);
for (auto chunk : uWS::ChunkIterator(&dataToConsume, &remainingStreamingBytes)) {
@@ -686,7 +684,7 @@ namespace uWS
}
} else if (contentLengthStringLen) {
if (!CONSUME_MINIMALLY) {
if constexpr (!ConsumeMinimally) {
unsigned int emittable = (unsigned int) std::min<uint64_t>(remainingStreamingBytes, length);
dataHandler(user, std::string_view(data, emittable), emittable == remainingStreamingBytes);
remainingStreamingBytes -= emittable;
@@ -701,7 +699,7 @@ namespace uWS
}
/* Consume minimally should break as easrly as possible */
if (CONSUME_MINIMALLY) {
if constexpr (ConsumeMinimally) {
break;
}
}

View File

@@ -460,7 +460,7 @@ public:
writeStatus(HTTP_200_OK);
/* Do not allow sending 0 chunks, they mark end of response */
if (!data.length()) {
if (data.empty()) {
/* If you called us, then according to you it was fine to call us so it's fine to still call us */
return true;
}

View File

@@ -36,7 +36,7 @@ namespace uWS {
template <class USERDATA>
struct HttpRouter {
static constexpr std::string_view ANY_METHOD_TOKEN = "*";
static const uint32_t HIGH_PRIORITY = 0xd0000000, MEDIUM_PRIORITY = 0xe0000000, LOW_PRIORITY = 0xf0000000;
static constexpr uint32_t HIGH_PRIORITY = 0xd0000000, MEDIUM_PRIORITY = 0xe0000000, LOW_PRIORITY = 0xf0000000;
private:
USERDATA userData;
@@ -60,12 +60,12 @@ private:
std::vector<uint32_t> handlers = {};
bool isHighPriority = false;
Node(std::string name) : name(name) {}
Node(std::string name) : name(std::move(name)) {}
} root = {"rootNode"};
/* Sort wildcards after alphanum */
int lexicalOrder(std::string &name) {
if (!name.length()) {
int lexicalOrder(std::string_view name) {
if (name.empty()) {
return 2;
}
if (name[0] == ':') {
@@ -79,7 +79,7 @@ private:
/* Advance from parent to child, adding child if necessary */
Node *getNode(Node *parent, std::string child, bool isHighPriority) {
for (std::unique_ptr<Node> &node : parent->children) {
for (const std::unique_ptr<Node> &node : parent->children) {
if (node->name == child && node->isHighPriority == isHighPriority) {
return node.get();
}
@@ -88,14 +88,13 @@ private:
/* Insert sorted, but keep order if parent is root (we sort methods by priority elsewhere) */
std::unique_ptr<Node> newNode(new Node(child));
newNode->isHighPriority = isHighPriority;
return parent->children.emplace(std::upper_bound(parent->children.begin(), parent->children.end(), newNode, [parent, this](auto &a, auto &b) {
auto iter = std::upper_bound(parent->children.begin(), parent->children.end(), newNode, [parent, this](auto &a, auto &b) {
if (a->isHighPriority != b->isHighPriority) {
return a->isHighPriority;
}
return b->name.length() && (parent != &root) && (lexicalOrder(b->name) < lexicalOrder(a->name));
}), std::move(newNode))->get();
return !b->name.empty() && (parent != &root) && (lexicalOrder(b->name) < lexicalOrder(a->name));
});
return parent->children.emplace(iter, std::move(newNode))->get();
}
/* Basically a pre-allocated stack */
@@ -182,14 +181,14 @@ private:
}
for (auto &p : parent->children) {
if (p->name.length() && p->name[0] == '*') {
if (p->name.starts_with('*')) {
/* Wildcard match (can be seen as a shortcut) */
for (uint32_t handler : p->handlers) {
if (handlers[handler & HANDLER_MASK](this)) {
return true;
}
}
} else if (p->name.length() && p->name[0] == ':' && segment.length()) {
} else if (p->name.starts_with(':') && !segment.empty()) {
/* Parameter match */
routeParameters.push(segment);
if (executeHandlers(p.get(), urlSegment + 1, userData)) {
@@ -207,17 +206,17 @@ private:
}
/* Scans for one matching handler, returning the handler and its priority or UINT32_MAX for not found */
uint32_t findHandler(std::string method, std::string pattern, uint32_t priority) {
for (std::unique_ptr<Node> &node : root.children) {
uint32_t findHandler(std::string_view method, std::string_view pattern, uint32_t priority) {
for (const std::unique_ptr<Node> &node : root.children) {
if (method == node->name) {
setUrl(pattern);
Node *n = node.get();
for (int i = 0; !getUrlSegment(i).second; i++) {
/* Go to next segment or quit */
std::string segment = std::string(getUrlSegment(i).first);
std::string segment(getUrlSegment(i).first);
Node *next = nullptr;
for (std::unique_ptr<Node> &child : n->children) {
if (((segment.length() && child->name.length() && segment[0] == ':' && child->name[0] == ':') || child->name == segment) && child->isHighPriority == (priority == HIGH_PRIORITY)) {
for (const std::unique_ptr<Node> &child : n->children) {
if (((segment.starts_with(':') && child->name.starts_with(':')) || child->name == segment) && child->isHighPriority == (priority == HIGH_PRIORITY)) {
next = child.get();
break;
}
@@ -242,7 +241,7 @@ private:
public:
HttpRouter() {
/* Always have ANY route */
getNode(&root, std::string(ANY_METHOD_TOKEN.data(), ANY_METHOD_TOKEN.length()), false);
getNode(&root, std::string(ANY_METHOD_TOKEN), false);
}
std::pair<int, std::string_view *> getParameters() {
@@ -279,25 +278,26 @@ public:
}
/* Adds the corresponding entires in matching tree and handler list */
void add(std::vector<std::string> methods, std::string pattern, MoveOnlyFunction<bool(HttpRouter *)> &&handler, uint32_t priority = MEDIUM_PRIORITY) {
void add(const std::vector<std::string> &methods, std::string_view pattern, MoveOnlyFunction<bool(HttpRouter *)> &&handler, uint32_t priority = MEDIUM_PRIORITY) {
/* First remove existing handler */
remove(methods[0], pattern, priority);
for (std::string method : methods) {
for (const std::string &method : methods) {
/* Lookup method */
Node *node = getNode(&root, method, false);
/* Iterate over all segments */
setUrl(pattern);
for (int i = 0; !getUrlSegment(i).second; i++) {
std::string strippedSegment(getUrlSegment(i).first);
if (strippedSegment.length() && strippedSegment[0] == ':') {
if (strippedSegment.length() > 1 && strippedSegment[0] == ':') {
/* Parameter routes must be named only : */
strippedSegment = ":";
strippedSegment.resize(1);
}
node = getNode(node, strippedSegment, priority == HIGH_PRIORITY);
}
/* Insert handler in order sorted by priority (most significant 1 byte) */
node->handlers.insert(std::upper_bound(node->handlers.begin(), node->handlers.end(), (uint32_t) (priority | handlers.size())), (uint32_t) (priority | handlers.size()));
uint32_t new_priority(priority | handlers.size());
node->handlers.insert(std::upper_bound(node->handlers.begin(), node->handlers.end(), new_priority), new_priority);
}
/* Alloate this handler */
@@ -359,7 +359,7 @@ public:
/* Removes ALL routes with the same handler as can be found with the given parameters.
* Removing a wildcard is done by removing ONE OF the methods the wildcard would match with.
* Example: If wildcard includes POST, GET, PUT, you can remove ALL THREE by removing GET. */
bool remove(std::string method, std::string pattern, uint32_t priority) {
bool remove(std::string_view method, std::string_view pattern, uint32_t priority) {
uint32_t handler = findHandler(method, pattern, priority);
if (handler == UINT32_MAX) {
/* Not found or already removed, do nothing */