Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions fuzzing/Http.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
/* Return ok */
return user;

}, [](uWS::HttpRequest */*req*/, unsigned int /*errorCode*/) {
});

if (!returnedUser) {
Expand Down
7 changes: 7 additions & 0 deletions src/App.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,13 @@ struct TemplatedApp {
return std::move(static_cast<TemplatedApp &&>(*this));
}

/* Attaches a log handler for HTTP parsing errors */
TemplatedApp &&log(MoveOnlyFunction<void(HttpRequest *, int, std::string_view)> &&logHandler) {
httpContext->log(std::move(logHandler));

return std::move(static_cast<TemplatedApp &&>(*this));
}

/* Same as publish, but takes a prepared message */
bool publishPrepared(std::string_view topic, PreparedMessage &preparedMessage) {
/* It is assumed by heuristics that a prepared message ought to be big,
Expand Down
12 changes: 12 additions & 0 deletions src/HttpContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include "HttpResponseData.h"
#include "AsyncSocket.h"
#include "WebSocketData.h"
#include "HttpErrors.h"

#include <string_view>
#include <iostream>
Expand Down Expand Up @@ -251,6 +252,12 @@ struct HttpContext {
}
}
return user;
}, [httpContextData](/*void *s, */HttpRequest *httpRequest, unsigned int errorCode) -> void {
/* Call the high-level error handler if one is registered */
if (httpContextData->httpParsingErrorHandler) {
/* Map internal error codes to HTTP status codes and response bodies */
httpContextData->httpParsingErrorHandler(httpRequest, httpErrorStatusCodes[errorCode], httpErrorResponses[errorCode]);
}
});

/* Mark that we are no longer parsing Http */
Expand Down Expand Up @@ -421,6 +428,11 @@ struct HttpContext {
getSocketContextData()->filterHandlers.emplace_back(std::move(filterHandler));
}

/* Register an HTTP parsing error handler */
void log(MoveOnlyFunction<void(HttpRequest *, int, std::string_view)> &&logHandler) {
getSocketContextData()->httpParsingErrorHandler = std::move(logHandler);
}

/* 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();
Expand Down
3 changes: 3 additions & 0 deletions src/HttpContextData.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ struct alignas(16) HttpContextData {
std::vector<MoveOnlyFunction<void(HttpResponse<SSL> *, int)>> filterHandlers;

MoveOnlyFunction<void(const char *hostname)> missingServerNameHandler;

/* HTTP parsing error handler */
MoveOnlyFunction<void(HttpRequest *, int, std::string_view)> httpParsingErrorHandler;

struct RouterData {
HttpResponse<SSL> *httpResponse;
Expand Down
7 changes: 7 additions & 0 deletions src/HttpErrors.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,13 @@ enum HttpError {

#ifndef UWS_HTTPRESPONSE_NO_WRITEMARK

static const int httpErrorStatusCodes[] = {
400, /* Zeroth place (unused) */
505, /* HTTP_ERROR_505_HTTP_VERSION_NOT_SUPPORTED */
431, /* HTTP_ERROR_431_REQUEST_HEADER_FIELDS_TOO_LARGE */
400 /* HTTP_ERROR_400_BAD_REQUEST */
};

/* Returned parser errors match this LUT. */
static const std::string_view httpErrorResponses[] = {
"", /* Zeroth place is no error so don't use it */
Expand Down
15 changes: 10 additions & 5 deletions src/HttpParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,9 @@ struct HttpParser {
data += consumed;
length -= consumed;
consumedTotal += consumed;
/* Parse query */
const char *querySeparatorPtr = (const char *) memchr(req->headers->value.data(), '?', req->headers->value.length());
req->querySeparator = (unsigned int) ((querySeparatorPtr ? querySeparatorPtr : req->headers->value.data() + req->headers->value.length()) - req->headers->value.data());
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I moved this code, because HttpRequest::getUrl() requires querySeparator to be set, the below return on error will call the new callback handler and this allows to get the URL of the failed request for these cases


/* Even if we could parse it, check for length here as well */
if (consumed > MAX_FALLBACK_SIZE) {
Expand Down Expand Up @@ -529,10 +532,6 @@ struct HttpParser {
return {HTTP_ERROR_400_BAD_REQUEST, FULLPTR};
}

/* Parse query */
const char *querySeparatorPtr = (const char *) memchr(req->headers->value.data(), '?', req->headers->value.length());
req->querySeparator = (unsigned int) ((querySeparatorPtr ? querySeparatorPtr : req->headers->value.data() + req->headers->value.length()) - req->headers->value.data());

/* If returned socket is not what we put in we need
* to break here as we either have upgraded to
* WebSockets or otherwise closed the socket. */
Expand Down Expand Up @@ -615,7 +614,7 @@ struct HttpParser {
}

public:
std::pair<unsigned int, void *> consumePostPadded(char *data, unsigned int length, void *user, void *reserved, MoveOnlyFunction<void *(void *, HttpRequest *)> &&requestHandler, MoveOnlyFunction<void *(void *, std::string_view, bool)> &&dataHandler) {
std::pair<unsigned int, void *> consumePostPadded(char *data, unsigned int length, void *user, void *reserved, MoveOnlyFunction<void *(void *, HttpRequest *)> &&requestHandler, MoveOnlyFunction<void *(void *, std::string_view, bool)> &&dataHandler, MoveOnlyFunction<void(HttpRequest *, unsigned int)> &&errorHandler) {

/* This resets BloomFilter by construction, but later we also reset it again.
* Optimize this to skip resetting twice (req could be made global) */
Expand All @@ -630,6 +629,7 @@ struct HttpParser {
dataHandler(user, chunk, chunk.length() == 0);
}
if (isParsingInvalidChunkedEncoding(remainingStreamingBytes)) {
errorHandler(&req, HTTP_ERROR_400_BAD_REQUEST);
return {HTTP_ERROR_400_BAD_REQUEST, FULLPTR};
}
data = (char *) dataToConsume.data();
Expand Down Expand Up @@ -667,6 +667,7 @@ struct HttpParser {
// break here on break
std::pair<unsigned int, void *> consumed = fenceAndConsumePostPadded<true>(fallback.data(), (unsigned int) fallback.length(), user, reserved, &req, requestHandler, dataHandler);
if (consumed.second != user) {
errorHandler(&req, consumed.first);
return consumed;
}

Expand All @@ -687,6 +688,7 @@ struct HttpParser {
dataHandler(user, chunk, chunk.length() == 0);
}
if (isParsingInvalidChunkedEncoding(remainingStreamingBytes)) {
errorHandler(&req, HTTP_ERROR_400_BAD_REQUEST);
return {HTTP_ERROR_400_BAD_REQUEST, FULLPTR};
}
data = (char *) dataToConsume.data();
Expand Down Expand Up @@ -714,6 +716,7 @@ struct HttpParser {

} else {
if (fallback.length() == MAX_FALLBACK_SIZE) {
errorHandler(&req, HTTP_ERROR_431_REQUEST_HEADER_FIELDS_TOO_LARGE);
return {HTTP_ERROR_431_REQUEST_HEADER_FIELDS_TOO_LARGE, FULLPTR};
}
return {0, user};
Expand All @@ -722,6 +725,7 @@ struct HttpParser {

std::pair<unsigned int, void *> consumed = fenceAndConsumePostPadded<false>(data, length, user, reserved, &req, requestHandler, dataHandler);
if (consumed.second != user) {
errorHandler(&req, consumed.first);
return consumed;
}

Expand All @@ -732,6 +736,7 @@ struct HttpParser {
if (length < MAX_FALLBACK_SIZE) {
fallback.append(data, length);
} else {
errorHandler(&req, HTTP_ERROR_431_REQUEST_HEADER_FIELDS_TOO_LARGE);
return {HTTP_ERROR_431_REQUEST_HEADER_FIELDS_TOO_LARGE, FULLPTR};
}
}
Expand Down
1 change: 1 addition & 0 deletions tests/HttpParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ int main() {
/* Return ok */
return user;

}, [](uWS::HttpRequest */*req*/, unsigned int /*errorCode*/) {
});

std::cout << "HTTP DONE" << std::endl;
Expand Down