Fix HTTP spec issues by upgrading uWS version (#14853)

This commit is contained in:
Jarred Sumner
2024-10-27 12:34:45 -07:00
committed by GitHub
parent e93c5ad993
commit f005e8c057
16 changed files with 1295 additions and 712 deletions

View File

@@ -16,8 +16,7 @@
* limitations under the License.
*/
// clang-format off
#ifndef UWS_APP_H
#define UWS_APP_H
#include <string>
#include <charconv>
@@ -619,4 +618,3 @@ typedef TemplatedApp<true> SSLApp;
}
#endif // UWS_APP_H

View File

@@ -16,8 +16,7 @@
* limitations under the License.
*/
#ifndef UWS_HTTPCONTEXT_H
#define UWS_HTTPCONTEXT_H
#pragma once
/* This class defines the main behavior of HTTP and emits various events */
@@ -27,6 +26,8 @@
#include "AsyncSocket.h"
#include "WebSocketData.h"
#include <string>
#include <map>
#include <string_view>
#include <iostream>
#include "MoveOnlyFunction.h"
@@ -171,7 +172,7 @@ private:
#endif
/* The return value is entirely up to us to interpret. The HttpParser only care for whether the returned value is DIFFERENT or not from passed user */
void *returnedSocket = httpResponseData->consumePostPadded(data, (unsigned int) length, s, proxyParser, [httpContextData](void *s, HttpRequest *httpRequest) -> void * {
auto [err, returnedSocket] = httpResponseData->consumePostPadded(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);
@@ -180,7 +181,9 @@ private:
HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) us_socket_ext(SSL, (us_socket_t *) s);
httpResponseData->offset = 0;
/* Are we not ready for another request yet? Terminate the connection. */
/* Are we not ready for another request yet? Terminate the connection.
* Important for denying async pipelining until, if ever, we want to suppot it.
* Otherwise requests can get mixed up on the same connection. We still support sync pipelining. */
if (httpResponseData->state & HttpResponseData<SSL>::HTTP_RESPONSE_PENDING) {
us_socket_close(SSL, (us_socket_t *) s, 0, nullptr);
return nullptr;
@@ -280,10 +283,6 @@ private:
}
}
return user;
}, [](void *user) {
/* Close any socket on HTTP errors */
us_socket_close(SSL, (us_socket_t *) user, 0, nullptr);
return nullptr;
});
/* Mark that we are no longer parsing Http */
@@ -291,6 +290,9 @@ private:
/* If we got fullptr that means the parser wants us to close the socket from error (same as calling the errorHandler) */
if (returnedSocket == FULLPTR) {
/* For errors, we only deliver them "at most once". We don't care if they get halfways delivered or not. */
us_socket_write(SSL, s, httpErrorResponses[err].data(), (int) httpErrorResponses[err].length(), false);
us_socket_shutdown(SSL, s);
/* Close any socket on HTTP errors */
us_socket_close(SSL, s, 0, nullptr);
/* This just makes the following code act as if the socket was closed from error inside the parser. */
@@ -299,9 +301,8 @@ private:
/* We need to uncork in all cases, except for nullptr (closed socket, or upgraded socket) */
if (returnedSocket != nullptr) {
us_socket_t* returnedSocketPtr = (us_socket_t*) returnedSocket;
/* We don't want open sockets to keep the event loop alive between HTTP requests */
us_socket_unref(returnedSocketPtr);
us_socket_unref((us_socket_t *) returnedSocket);
/* Timeout on uncork failure */
auto [written, failed] = ((AsyncSocket<SSL> *) returnedSocket)->uncork();
@@ -321,7 +322,7 @@ private:
}
}
}
return returnedSocketPtr;
return (us_socket_t *) returnedSocket;
}
/* If we upgraded, check here (differ between nullptr close and nullptr upgrade) */
@@ -483,10 +484,27 @@ public:
return;
}
httpContextData->currentRouter->add(methods, pattern, [handler = std::move(handler)](auto *r) mutable {
/* Record this route's parameter offsets */
std::map<std::string, unsigned short, std::less<>> parameterOffsets;
unsigned short offset = 0;
for (unsigned int i = 0; i < pattern.length(); i++) {
if (pattern[i] == ':') {
i++;
unsigned int start = i;
while (i < pattern.length() && pattern[i] != '/') {
i++;
}
parameterOffsets[std::string(pattern.data() + start, i - start)] = offset;
//std::cout << "<" << std::string(pattern.data() + start, i - start) << "> is offset " << offset;
offset++;
}
}
httpContextData->currentRouter->add(methods, pattern, [handler = std::move(handler), parameterOffsets = std::move(parameterOffsets)](auto *r) mutable {
auto user = r->getUserData();
user.httpRequest->setYield(false);
user.httpRequest->setParameters(r->getParameters());
user.httpRequest->setParameterOffsets(&parameterOffsets);
/* Middleware? Automatically respond to expectations */
std::string_view expect = user.httpRequest->getHeader("expect");
@@ -528,4 +546,4 @@ public:
}
#endif // UWS_HTTPCONTEXT_H

View File

@@ -0,0 +1,53 @@
/*
* Authored by Alex Hultman, 2018-2023.
* Intellectual property of third-party.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef UWS_HTTP_ERRORS
#define UWS_HTTP_ERRORS
#include <string_view>
namespace uWS {
/* Possible errors from http parsing */
enum HttpError {
HTTP_ERROR_505_HTTP_VERSION_NOT_SUPPORTED = 1,
HTTP_ERROR_431_REQUEST_HEADER_FIELDS_TOO_LARGE = 2,
HTTP_ERROR_400_BAD_REQUEST = 3
};
#ifndef UWS_HTTPRESPONSE_NO_WRITEMARK
/* Returned parser errors match this LUT. */
static const std::string_view httpErrorResponses[] = {
"", /* Zeroth place is no error so don't use it */
"HTTP/1.1 505 HTTP Version Not Supported\r\nConnection: close\r\n\r\n<h1>HTTP Version Not Supported</h1><p>This server does not support HTTP/1.0.</p><hr><i>uWebSockets/20 Server</i>",
"HTTP/1.1 431 Request Header Fields Too Large\r\nConnection: close\r\n\r\n<h1>Request Header Fields Too Large</h1><hr><i>uWebSockets/20 Server</i>",
"HTTP/1.1 400 Bad Request\r\nConnection: close\r\n\r\n<h1>Bad Request</h1><hr><i>uWebSockets/20 Server</i>",
};
#else
/* Anonymized pages */
static const std::string_view httpErrorResponses[] = {
"", /* Zeroth place is no error so don't use it */
"HTTP/1.1 505 HTTP Version Not Supported\r\nConnection: close\r\n\r\n",
"HTTP/1.1 431 Request Header Fields Too Large\r\nConnection: close\r\n\r\n",
"HTTP/1.1 400 Bad Request\r\nConnection: close\r\n\r\n"
};
#endif
}
#endif

View File

@@ -0,0 +1,42 @@
/*
* Authored by Alex Hultman, 2018-2023.
* Intellectual property of third-party.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <string_view>
namespace uWS {
/* Possible errors from http parsing */
enum HttpError {
HTTP_ERROR_505_HTTP_VERSION_NOT_SUPPORTED = 1,
HTTP_ERROR_431_REQUEST_HEADER_FIELDS_TOO_LARGE = 2,
HTTP_ERROR_400_BAD_REQUEST = 3
};
/* Anonymized pages */
static const std::string_view httpErrorResponses[] = {
"", /* Zeroth place is no error so don't use it */
"HTTP/1.1 505 HTTP Version Not Supported\r\nConnection: close\r\n\r\n",
"HTTP/1.1 431 Request Header Fields Too Large\r\nConnection: close\r\n\r\n",
"HTTP/1.1 400 Bad Request\r\nConnection: close\r\n\r\n"
};
}

File diff suppressed because it is too large Load Diff

View File

@@ -15,8 +15,7 @@
* limitations under the License.
*/
// clang-format off
#ifndef UWS_HTTPRESPONSEDATA_H
#define UWS_HTTPRESPONSEDATA_H
#pragma once
/* This data belongs to the HttpResponse */
@@ -106,4 +105,4 @@ struct HttpResponseData : AsyncSocketData<SSL>, HttpParser {
}
#endif // UWS_HTTPRESPONSEDATA_H

View File

@@ -15,9 +15,7 @@
* limitations under the License.
*/
#ifndef UWS_TOPICTREE_H
#define UWS_TOPICTREE_H
#pragma once
#include <map>
#include <list>
#include <iostream>
@@ -366,4 +364,4 @@ public:
}
#endif