Skip to content

WIP compat(node:http) clientError #19195

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 8 commits into
base: main
Choose a base branch
from
Draft
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
2 changes: 1 addition & 1 deletion packages/bun-usockets/src/crypto/openssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -1617,7 +1617,7 @@ struct us_socket_t *us_internal_ssl_socket_context_connect(
2, &context->sc, host, port, options,
sizeof(struct us_internal_ssl_socket_t) - sizeof(struct us_socket_t) +
socket_ext_size, is_connecting);
if (*is_connecting) {
if (*is_connecting && s) {
us_internal_zero_ssl_data_for_connected_socket_before_onopen(s);
}

Expand Down
8 changes: 6 additions & 2 deletions packages/bun-uws/src/App.h
Original file line number Diff line number Diff line change
Expand Up @@ -614,18 +614,22 @@ struct TemplatedApp {
httpContext->getSocketContextData()->onSocketClosed = onClose;
}

void setOnClientError(HttpContextData<SSL>::OnClientErrorCallback onClientError) {
httpContext->getSocketContextData()->onClientError = std::move(onClientError);
}

TemplatedApp &&run() {
uWS::run();
return std::move(*this);
}

TemplatedApp &&setUsingCustomExpectHandler(bool value) {
httpContext->getSocketContextData()->usingCustomExpectHandler = value;
httpContext->getSocketContextData()->flags.usingCustomExpectHandler = value;
return std::move(*this);
}

TemplatedApp &&setRequireHostHeader(bool value) {
httpContext->getSocketContextData()->requireHostHeader = value;
httpContext->getSocketContextData()->flags.requireHostHeader = value;
return std::move(*this);
}

Expand Down
32 changes: 21 additions & 11 deletions packages/bun-uws/src/HttpContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
#include <string_view>
#include <iostream>
#include "MoveOnlyFunction.h"

#include "HttpParser.h"
namespace uWS {
template<bool> struct HttpResponse;

Expand Down Expand Up @@ -73,8 +73,8 @@ struct HttpContext {
// 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)) {
HttpContextData<SSL> *httpContextData = getSocketContextDataS(s);

if(httpContextData->rejectUnauthorized) {
httpContextData->flags.isSecure = success;
if(httpContextData->flags.rejectUnauthorized) {
if(!success || verify_error.error != 0) {
// we failed to handshake, close the socket
us_socket_close(SSL, s, 0, nullptr);
Expand Down Expand Up @@ -118,8 +118,15 @@ struct HttpContext {
/* Get socket ext */
auto *httpResponseData = reinterpret_cast<HttpResponseData<SSL> *>(us_socket_ext(SSL, s));



/* Call filter */
HttpContextData<SSL> *httpContextData = getSocketContextDataS(s);
if(httpContextData->flags.isParsingHttp) {
if(httpContextData->onClientError) {
httpContextData->onClientError(SSL, s,uWS::HTTP_PARSER_ERROR_INVALID_EOF, nullptr, 0);
}
}
for (auto &f : httpContextData->filterHandlers) {
f((HttpResponse<SSL> *) s, -1);
}
Expand Down Expand Up @@ -163,7 +170,7 @@ struct HttpContext {
((AsyncSocket<SSL> *) s)->cork();

/* Mark that we are inside the parser now */
httpContextData->isParsingHttp = true;
httpContextData->flags.isParsingHttp = true;

// 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?
Expand All @@ -174,7 +181,7 @@ struct HttpContext {
#endif

/* The return value is entirely up to us to interpret. The HttpParser cares only for whether the returned value is DIFFERENT from passed user */
auto [err, returnedSocket] = httpResponseData->consumePostPadded(httpContextData->requireHostHeader,data, (unsigned int) length, s, proxyParser, [httpContextData](void *s, HttpRequest *httpRequest) -> void * {
auto [err, parserError, returnedSocket] = httpResponseData->consumePostPadded(httpContextData->flags.requireHostHeader,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);
Expand All @@ -201,6 +208,7 @@ struct HttpContext {

httpResponseData->fromAncientRequest = httpRequest->isAncient();


/* Select the router based on SNI (only possible for SSL) */
auto *selectedRouter = &httpContextData->router;
if constexpr (SSL) {
Expand Down Expand Up @@ -290,10 +298,12 @@ struct HttpContext {
});

/* Mark that we are no longer parsing Http */
httpContextData->isParsingHttp = false;

httpContextData->flags.isParsingHttp = false;
/* If we got fullptr that means the parser wants us to close the socket from error (same as calling the errorHandler) */
if (returnedSocket == FULLPTR) {
if(httpContextData->onClientError) {
httpContextData->onClientError(SSL, s, parserError, data, length);
}
/* 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);
Expand Down Expand Up @@ -467,7 +477,7 @@ struct HttpContext {
/* 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) {
httpContextData->rejectUnauthorized = true;
httpContextData->flags.rejectUnauthorized = true;
}
return httpContext->init();
}
Expand Down Expand Up @@ -515,15 +525,15 @@ struct HttpContext {
}
}

const bool &customContinue = httpContextData->usingCustomExpectHandler;


httpContextData->currentRouter->add(methods, pattern, [handler = std::move(handler), parameterOffsets = std::move(parameterOffsets), &customContinue](auto *r) mutable {
httpContextData->currentRouter->add(methods, pattern, [handler = std::move(handler), parameterOffsets = std::move(parameterOffsets), httpContextData](auto *r) mutable {
auto user = r->getUserData();
user.httpRequest->setYield(false);
user.httpRequest->setParameters(r->getParameters());
user.httpRequest->setParameterOffsets(&parameterOffsets);

if (!customContinue) {
if (!httpContextData->flags.usingCustomExpectHandler) {
/* Middleware? Automatically respond to expectations */
std::string_view expect = user.httpRequest->getHeader("expect");
if (expect.length() && expect == "100-continue") {
Expand Down
24 changes: 18 additions & 6 deletions packages/bun-uws/src/HttpContextData.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,19 @@

#include <vector>
#include "MoveOnlyFunction.h"

#include "HttpParser.h"
namespace uWS {
template<bool> struct HttpResponse;
struct HttpRequest;

struct HttpFlags {
bool isParsingHttp: 1 = false;
bool rejectUnauthorized: 1 = false;
bool usingCustomExpectHandler: 1 = false;
bool requireHostHeader: 1 = true;
bool isSecure: 1 = false;
};

template <bool SSL>
struct alignas(16) HttpContextData {
template <bool> friend struct HttpContext;
Expand All @@ -35,6 +43,7 @@ struct alignas(16) HttpContextData {
private:
std::vector<MoveOnlyFunction<void(HttpResponse<SSL> *, int)>> filterHandlers;
using OnSocketClosedCallback = void (*)(void* userData, int is_ssl, struct us_socket_t *rawSocket);
using OnClientErrorCallback = MoveOnlyFunction<void(int is_ssl, struct us_socket_t *rawSocket, uWS::HttpParserError errorCode, char *rawPacket, int rawPacketLength)>;

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

Expand All @@ -49,20 +58,23 @@ struct alignas(16) HttpContextData {
/* This is the default router for default SNI or non-SSL */
HttpRouter<RouterData> router;
void *upgradedWebSocket = nullptr;
bool isParsingHttp = false;
bool rejectUnauthorized = false;
bool usingCustomExpectHandler = false;
bool requireHostHeader = true;

/* Used to simulate Node.js socket events. */
OnSocketClosedCallback onSocketClosed = nullptr;
OnClientErrorCallback onClientError = nullptr;

HttpFlags flags;

// TODO: SNI
void clearRoutes() {
this->router = HttpRouter<RouterData>{};
this->currentRouter = &router;
filterHandlers.clear();
}

public:
bool isSecure() const {
return flags.isSecure;
}
};

}
Expand Down
Loading