diff --git a/packages/bun-types/bun.d.ts b/packages/bun-types/bun.d.ts index 66414b6ac3..f6b3b2ae95 100644 --- a/packages/bun-types/bun.d.ts +++ b/packages/bun-types/bun.d.ts @@ -3846,6 +3846,11 @@ declare module "bun" { * @category HTTP & Networking */ interface Server extends Disposable { + /* + * Closes all connections connected to this server which are not sending a request or waiting for a response. Does not close the listen socket. + */ + closeIdleConnections(): void; + /** * Stop listening to prevent new connections from being accepted. * diff --git a/packages/bun-usockets/src/context.c b/packages/bun-usockets/src/context.c index 605bb6de11..6e2c3f3e18 100644 --- a/packages/bun-usockets/src/context.c +++ b/packages/bun-usockets/src/context.c @@ -153,7 +153,7 @@ void us_internal_socket_context_unlink_connecting_socket(int ssl, struct us_sock } /* We always add in the top, so we don't modify any s.next */ -void us_internal_socket_context_link_listen_socket(struct us_socket_context_t *context, struct us_listen_socket_t *ls) { +void us_internal_socket_context_link_listen_socket(int ssl, struct us_socket_context_t *context, struct us_listen_socket_t *ls) { struct us_socket_t* s = &ls->s; s->context = context; s->next = (struct us_socket_t *) context->head_listen_sockets; @@ -162,7 +162,7 @@ void us_internal_socket_context_link_listen_socket(struct us_socket_context_t *c context->head_listen_sockets->s.prev = s; } context->head_listen_sockets = ls; - us_socket_context_ref(0, context); + us_socket_context_ref(ssl, context); } void us_internal_socket_context_link_connecting_socket(int ssl, struct us_socket_context_t *context, struct us_connecting_socket_t *c) { @@ -179,7 +179,7 @@ void us_internal_socket_context_link_connecting_socket(int ssl, struct us_socket /* We always add in the top, so we don't modify any s.next */ -void us_internal_socket_context_link_socket(struct us_socket_context_t *context, struct us_socket_t *s) { +void us_internal_socket_context_link_socket(int ssl, struct us_socket_context_t *context, struct us_socket_t *s) { s->context = context; s->next = context->head_sockets; s->prev = 0; @@ -187,7 +187,7 @@ void us_internal_socket_context_link_socket(struct us_socket_context_t *context, context->head_sockets->prev = s; } context->head_sockets = s; - us_socket_context_ref(0, context); + us_socket_context_ref(ssl, context); us_internal_enable_sweep_timer(context->loop); } @@ -388,7 +388,7 @@ struct us_listen_socket_t *us_socket_context_listen(int ssl, struct us_socket_co s->flags.is_ipc = 0; s->next = 0; s->flags.allow_half_open = (options & LIBUS_SOCKET_ALLOW_HALF_OPEN); - us_internal_socket_context_link_listen_socket(context, ls); + us_internal_socket_context_link_listen_socket(ssl, context, ls); ls->socket_ext_size = socket_ext_size; @@ -423,7 +423,7 @@ struct us_listen_socket_t *us_socket_context_listen_unix(int ssl, struct us_sock s->flags.is_paused = 0; s->flags.is_ipc = 0; s->next = 0; - us_internal_socket_context_link_listen_socket(context, ls); + us_internal_socket_context_link_listen_socket(ssl, context, ls); ls->socket_ext_size = socket_ext_size; @@ -456,7 +456,7 @@ struct us_socket_t* us_socket_context_connect_resolved_dns(struct us_socket_cont socket->connect_state = NULL; socket->connect_next = NULL; - us_internal_socket_context_link_socket(context, socket); + us_internal_socket_context_link_socket(0, context, socket); return socket; } @@ -584,7 +584,7 @@ int start_connections(struct us_connecting_socket_t *c, int count) { flags->is_paused = 0; flags->is_ipc = 0; /* Link it into context so that timeout fires properly */ - us_internal_socket_context_link_socket(context, s); + us_internal_socket_context_link_socket(0, context, s); // TODO check this, specifically how it interacts with the SSL code // does this work when we create multiple sockets at once? will we need multiple SSL contexts? @@ -762,7 +762,7 @@ struct us_socket_t *us_socket_context_connect_unix(int ssl, struct us_socket_con connect_socket->flags.is_ipc = 0; connect_socket->connect_state = NULL; connect_socket->connect_next = NULL; - us_internal_socket_context_link_socket(context, connect_socket); + us_internal_socket_context_link_socket(ssl, context, connect_socket); return connect_socket; } @@ -804,12 +804,9 @@ struct us_socket_t *us_socket_context_adopt_socket(int ssl, struct us_socket_con } struct us_connecting_socket_t *c = s->connect_state; - struct us_socket_t *new_s = s; - if (ext_size != -1) { struct us_poll_t *pool_ref = &s->p; - new_s = (struct us_socket_t *) us_poll_resize(pool_ref, loop, sizeof(struct us_socket_t) + ext_size); if (c) { c->connecting_head = new_s; @@ -831,7 +828,7 @@ struct us_socket_t *us_socket_context_adopt_socket(int ssl, struct us_socket_con /* We manually ref/unref context to handle context life cycle with low-priority queue */ us_socket_context_ref(ssl, context); } else { - us_internal_socket_context_link_socket(context, new_s); + us_internal_socket_context_link_socket(ssl, context, new_s); } /* We can safely unref the old context here with can potentially be freed */ us_socket_context_unref(ssl, old_context); diff --git a/packages/bun-usockets/src/internal/internal.h b/packages/bun-usockets/src/internal/internal.h index 360a676954..7ee718e723 100644 --- a/packages/bun-usockets/src/internal/internal.h +++ b/packages/bun-usockets/src/internal/internal.h @@ -150,16 +150,12 @@ void us_internal_init_loop_ssl_data(us_loop_r loop); void us_internal_free_loop_ssl_data(us_loop_r loop); /* Socket context related */ -void us_internal_socket_context_link_socket(us_socket_context_r context, - us_socket_r s); -void us_internal_socket_context_unlink_socket(int ssl, - us_socket_context_r context, us_socket_r s); +void us_internal_socket_context_link_socket(int ssl, us_socket_context_r context, us_socket_r s); +void us_internal_socket_context_unlink_socket(int ssl, us_socket_context_r context, us_socket_r s); void us_internal_socket_after_resolve(struct us_connecting_socket_t *s); void us_internal_socket_after_open(us_socket_r s, int error); -struct us_internal_ssl_socket_t * -us_internal_ssl_socket_close(us_internal_ssl_socket_r s, int code, - void *reason); +struct us_internal_ssl_socket_t *us_internal_ssl_socket_close(us_internal_ssl_socket_r s, int code, void *reason); int us_internal_handle_dns_results(us_loop_r loop); @@ -271,7 +267,7 @@ struct us_listen_socket_t { }; /* Listen sockets are keps in their own list */ -void us_internal_socket_context_link_listen_socket( +void us_internal_socket_context_link_listen_socket(int ssl, us_socket_context_r context, struct us_listen_socket_t *s); void us_internal_socket_context_unlink_listen_socket(int ssl, us_socket_context_r context, struct us_listen_socket_t *s); @@ -288,8 +284,7 @@ struct us_socket_context_t { struct us_socket_t *iterator; struct us_socket_context_t *prev, *next; - struct us_socket_t *(*on_open)(struct us_socket_t *, int is_client, char *ip, - int ip_length); + struct us_socket_t *(*on_open)(struct us_socket_t *, int is_client, char *ip, int ip_length); struct us_socket_t *(*on_data)(struct us_socket_t *, char *data, int length); struct us_socket_t *(*on_fd)(struct us_socket_t *, int fd); struct us_socket_t *(*on_writable)(struct us_socket_t *); @@ -301,7 +296,6 @@ struct us_socket_context_t { struct us_connecting_socket_t *(*on_connect_error)(struct us_connecting_socket_t *, int code); struct us_socket_t *(*on_socket_connect_error)(struct us_socket_t *, int code); int (*is_low_prio)(struct us_socket_t *); - }; /* Internal SSL interface */ diff --git a/packages/bun-usockets/src/loop.c b/packages/bun-usockets/src/loop.c index 2129561d02..b1605dcfab 100644 --- a/packages/bun-usockets/src/loop.c +++ b/packages/bun-usockets/src/loop.c @@ -40,7 +40,6 @@ void us_internal_enable_sweep_timer(struct us_loop_t *loop) { us_timer_set(loop->data.sweep_timer, (void (*)(struct us_timer_t *)) sweep_timer_cb, LIBUS_TIMEOUT_GRANULARITY * 1000, LIBUS_TIMEOUT_GRANULARITY * 1000); Bun__internal_ensureDateHeaderTimerIsEnabled(loop); } - } void us_internal_disable_sweep_timer(struct us_loop_t *loop) { @@ -183,7 +182,7 @@ void us_internal_handle_low_priority_sockets(struct us_loop_t *loop) { if (s->next) s->next->prev = 0; s->next = 0; - us_internal_socket_context_link_socket(s->context, s); + us_internal_socket_context_link_socket(0, s->context, s); us_poll_change(&s->p, us_socket_context(0, s)->loop, us_poll_events(&s->p) | LIBUS_SOCKET_READABLE); s->flags.low_prio_state = 2; @@ -340,7 +339,7 @@ void us_internal_dispatch_ready_poll(struct us_poll_t *p, int error, int eof, in /* We always use nodelay */ bsd_socket_nodelay(client_fd, 1); - us_internal_socket_context_link_socket(listen_socket->s.context, s); + us_internal_socket_context_link_socket(0, listen_socket->s.context, s); listen_socket->s.context->on_open(s, 0, bsd_addr_get_ip(&addr), bsd_addr_get_ip_length(&addr)); @@ -364,7 +363,7 @@ void us_internal_dispatch_ready_poll(struct us_poll_t *p, int error, int eof, in /* Note: if we failed a write as a socket of one loop then adopted * to another loop, this will be wrong. Absurd case though */ loop->data.last_write_failed = 0; - + s = s->context->on_writable(s); if (!s || us_socket_is_closed(0, s)) { diff --git a/packages/bun-usockets/src/socket.c b/packages/bun-usockets/src/socket.c index 8b3a8723e3..a4b02a7f42 100644 --- a/packages/bun-usockets/src/socket.c +++ b/packages/bun-usockets/src/socket.c @@ -329,7 +329,7 @@ struct us_socket_t *us_socket_from_fd(struct us_socket_context_t *ctx, int socke bsd_socket_nodelay(fd, 1); apple_no_sigpipe(fd); bsd_set_nonblocking(fd); - us_internal_socket_context_link_socket(ctx, s); + us_internal_socket_context_link_socket(0, ctx, s); return s; #endif diff --git a/packages/bun-uws/src/App.h b/packages/bun-uws/src/App.h index a68f306de4..d98389e787 100644 --- a/packages/bun-uws/src/App.h +++ b/packages/bun-uws/src/App.h @@ -298,6 +298,22 @@ public: return std::move(*this); } + /** Closes all connections connected to this server which are not sending a request or waiting for a response. Does not close the listen socket. */ + TemplatedApp &&closeIdle() { + auto context = (struct us_socket_context_t *)this->httpContext; + struct us_socket_t *s = context->head_sockets; + while (s) { + HttpResponseData *httpResponseData = HttpResponse::getHttpResponseDataS(s); + httpResponseData->shouldCloseOnceIdle = true; + struct us_socket_t *next = s->next; + if (httpResponseData->isIdle) { + us_socket_close(SSL, s, LIBUS_SOCKET_CLOSE_CODE_CLEAN_SHUTDOWN, 0); + } + s = next; + } + return std::move(*this); + } + template TemplatedApp &&ws(std::string_view pattern, WebSocketBehavior &&behavior) { /* Don't compile if alignment rules cannot be satisfied */ diff --git a/packages/bun-uws/src/AsyncSocket.h b/packages/bun-uws/src/AsyncSocket.h index e5bcf5cabb..540e7ee7f5 100644 --- a/packages/bun-uws/src/AsyncSocket.h +++ b/packages/bun-uws/src/AsyncSocket.h @@ -386,6 +386,9 @@ public: /* We do not need to care for buffering here, write does that */ return {0, true}; } + if (length == 0) { + return {written, failed}; + } } /* We should only return with new writes, not things written to cork already */ diff --git a/packages/bun-uws/src/HttpContext.h b/packages/bun-uws/src/HttpContext.h index 0fc7cf9f56..c0866ffdde 100644 --- a/packages/bun-uws/src/HttpContext.h +++ b/packages/bun-uws/src/HttpContext.h @@ -137,10 +137,6 @@ private: return (HttpContextData *) us_socket_context_ext(SSL, getSocketContext()); } - static HttpContextData *getSocketContextDataS(us_socket_t *s) { - return (HttpContextData *) us_socket_context_ext(SSL, getSocketContext(s)); - } - /* Init the HttpContext by registering libusockets event handlers */ HttpContext *init() { @@ -247,6 +243,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? @@ -398,6 +395,7 @@ private: /* Timeout on uncork failure */ auto [written, failed] = ((AsyncSocket *) returnedData)->uncork(); if (written > 0 || failed) { + httpResponseData->isIdle = true; /* All Http sockets timeout by this, and this behavior match the one in HttpResponse::cork */ ((HttpResponse *) s)->resetTimeout(); } @@ -642,6 +640,10 @@ public: }, priority); } + static HttpContextData *getSocketContextDataS(us_socket_t *s) { + return (HttpContextData *) us_socket_context_ext(SSL, getSocketContext(s)); + } + /* Listen to port using this HttpContext */ us_listen_socket_t *listen(const char *host, int port, int options) { int error = 0; diff --git a/packages/bun-uws/src/HttpContextData.h b/packages/bun-uws/src/HttpContextData.h index 49c094c64e..48ec202dd1 100644 --- a/packages/bun-uws/src/HttpContextData.h +++ b/packages/bun-uws/src/HttpContextData.h @@ -63,7 +63,6 @@ private: OnSocketClosedCallback onSocketClosed = nullptr; OnClientErrorCallback onClientError = nullptr; - HttpFlags flags; uint64_t maxHeaderSize = 0; // 0 means no limit // TODO: SNI @@ -73,10 +72,8 @@ private: filterHandlers.clear(); } - public: - bool isAuthorized() const { - return flags.isAuthorized; - } +public: + HttpFlags flags; }; } diff --git a/packages/bun-uws/src/HttpResponse.h b/packages/bun-uws/src/HttpResponse.h index 8a92248960..03c82ca77d 100644 --- a/packages/bun-uws/src/HttpResponse.h +++ b/packages/bun-uws/src/HttpResponse.h @@ -50,6 +50,11 @@ public: HttpResponseData *getHttpResponseData() { return (HttpResponseData *) Super::getAsyncSocketData(); } + + static HttpResponseData *getHttpResponseDataS(us_socket_t *s) { + return (HttpResponseData *) us_socket_ext(SSL, s); + } + void setTimeout(uint8_t seconds) { auto* data = getHttpResponseData(); data->idleTimeout = seconds; @@ -132,7 +137,7 @@ public: /* Terminating 0 chunk */ Super::write("0\r\n\r\n", 5); - httpResponseData->markDone(); + httpResponseData->markDone(this); /* We need to check if we should close this socket here now */ if (!Super::isCorked()) { @@ -198,7 +203,7 @@ public: /* Remove onAborted function if we reach the end */ if (httpResponseData->offset == totalSize) { - httpResponseData->markDone(); + httpResponseData->markDone(this); /* We need to check if we should close this socket here now */ if (!Super::isCorked()) { diff --git a/packages/bun-uws/src/HttpResponseData.h b/packages/bun-uws/src/HttpResponseData.h index eda5a15b2c..26c3428049 100644 --- a/packages/bun-uws/src/HttpResponseData.h +++ b/packages/bun-uws/src/HttpResponseData.h @@ -22,11 +22,15 @@ #include "HttpParser.h" #include "AsyncSocketData.h" #include "ProxyParser.h" +#include "HttpContext.h" #include "MoveOnlyFunction.h" namespace uWS { +template +struct HttpContext; + template struct HttpResponseData : AsyncSocketData, HttpParser { template friend struct HttpResponse; @@ -38,7 +42,7 @@ struct HttpResponseData : AsyncSocketData, HttpParser { using OnDataCallback = void (*)(uWS::HttpResponse* response, const char* chunk, size_t chunk_length, bool, void*); /* When we are done with a response we mark it like so */ - void markDone() { + void markDone(uWS::HttpResponse *uwsRes) { onAborted = nullptr; /* Also remove onWritable so that we do not emit when draining behind the scenes. */ onWritable = nullptr; @@ -50,6 +54,9 @@ struct HttpResponseData : AsyncSocketData, HttpParser { /* We are done with this request */ this->state &= ~HttpResponseData::HTTP_RESPONSE_PENDING; + + HttpResponseData *httpResponseData = uwsRes->getHttpResponseData(); + httpResponseData->isIdle = true; } /* Caller of onWritable. It is possible onWritable calls markDone so we need to borrow it. */ @@ -101,6 +108,8 @@ struct HttpResponseData : AsyncSocketData, HttpParser { uint8_t state = 0; uint8_t idleTimeout = 10; // default HTTP_TIMEOUT 10 seconds bool fromAncientRequest = false; + bool isIdle = true; + bool shouldCloseOnceIdle = false; #ifdef UWS_WITH_PROXY diff --git a/src/bun.js/api/server.classes.ts b/src/bun.js/api/server.classes.ts index 44cb521d86..ccbd36e8fe 100644 --- a/src/bun.js/api/server.classes.ts +++ b/src/bun.js/api/server.classes.ts @@ -29,6 +29,10 @@ function generate(name) { fn: "dispose", length: 0, }, + closeIdleConnections: { + fn: "closeIdleConnections", + length: 0, + }, stop: { fn: "doStop", length: 1, diff --git a/src/bun.js/api/server.zig b/src/bun.js/api/server.zig index 38bec4a432..f26f455258 100644 --- a/src/bun.js/api/server.zig +++ b/src/bun.js/api/server.zig @@ -741,12 +741,7 @@ pub fn NewServer(protocol_enum: enum { http, https }, development_kind: enum { d } } - pub fn onUpgrade( - this: *ThisServer, - globalThis: *jsc.JSGlobalObject, - object: jsc.JSValue, - optional: ?JSValue, - ) bun.JSError!JSValue { + pub fn onUpgrade(this: *ThisServer, globalThis: *jsc.JSGlobalObject, object: jsc.JSValue, optional: ?JSValue) bun.JSError!JSValue { if (this.config.websocket == null) { return globalThis.throwInvalidArguments("To enable websocket support, set the \"websocket\" object in Bun.serve({})", .{}); } @@ -1132,11 +1127,7 @@ pub fn NewServer(protocol_enum: enum { http, https }, development_kind: enum { d return this.js_value.get(); } - pub fn onFetch( - this: *ThisServer, - ctx: *jsc.JSGlobalObject, - callframe: *jsc.CallFrame, - ) bun.JSError!jsc.JSValue { + pub fn onFetch(this: *ThisServer, ctx: *jsc.JSGlobalObject, callframe: *jsc.CallFrame) bun.JSError!jsc.JSValue { jsc.markBinding(@src()); if (this.config.onRequest == .zero) { @@ -1253,6 +1244,14 @@ pub fn NewServer(protocol_enum: enum { http, https }, development_kind: enum { d return jsc.JSPromise.resolvedPromiseValue(ctx, response_value); } + pub fn closeIdleConnections(this: *ThisServer, globalObject: *jsc.JSGlobalObject, callframe: *jsc.CallFrame) bun.JSError!jsc.JSValue { + _ = globalObject; + _ = callframe; + if (this.app == null) return .js_undefined; + this.app.?.closeIdleConnections(); + return .js_undefined; + } + pub fn stopFromJS(this: *ThisServer, abruptly: ?JSValue) jsc.JSValue { const rc = this.getAllClosedPromise(this.globalThis); @@ -1280,10 +1279,7 @@ pub fn NewServer(protocol_enum: enum { http, https }, development_kind: enum { d return .js_undefined; } - pub fn getPort( - this: *ThisServer, - _: *jsc.JSGlobalObject, - ) jsc.JSValue { + pub fn getPort(this: *ThisServer, _: *jsc.JSGlobalObject) jsc.JSValue { switch (this.config.address) { .unix => return .js_undefined, else => {}, @@ -1412,10 +1408,7 @@ pub fn NewServer(protocol_enum: enum { http, https }, development_kind: enum { d return bun.String.static(if (ssl_enabled) "https" else "http").toJS(globalThis); } - pub fn getDevelopment( - _: *ThisServer, - _: *jsc.JSGlobalObject, - ) jsc.JSValue { + pub fn getDevelopment(_: *ThisServer, _: *jsc.JSGlobalObject) jsc.JSValue { return jsc.JSValue.jsBoolean(debug_mode); } @@ -1989,11 +1982,7 @@ pub fn NewServer(protocol_enum: enum { http, https }, development_kind: enum { d } } - pub fn onNodeHTTPRequest( - this: *ThisServer, - req: *uws.Request, - resp: *App.Response, - ) void { + pub fn onNodeHTTPRequest(this: *ThisServer, req: *uws.Request, resp: *App.Response) void { jsc.markBinding(@src()); onNodeHTTPRequestWithUpgradeCtx(this, req, resp, null); } @@ -2073,11 +2062,7 @@ pub fn NewServer(protocol_enum: enum { http, https }, development_kind: enum { d ctx.toAsync(req, prepared.request_object); } - pub fn onRequest( - this: *ThisServer, - req: *uws.Request, - resp: *App.Response, - ) void { + pub fn onRequest(this: *ThisServer, req: *uws.Request, resp: *App.Response) void { var should_deinit_context = false; const prepared = this.prepareJsRequestContext(req, resp, &should_deinit_context, true, null) orelse return; @@ -2094,14 +2079,7 @@ pub fn NewServer(protocol_enum: enum { http, https }, development_kind: enum { d this.handleRequest(&should_deinit_context, prepared, req, response_value); } - pub fn onRequestFromSaved( - this: *ThisServer, - req: SavedRequest.Union, - resp: *App.Response, - callback: JSValue, - comptime arg_count: comptime_int, - extra_args: [arg_count]JSValue, - ) void { + pub fn onRequestFromSaved(this: *ThisServer, req: SavedRequest.Union, resp: *App.Response, callback: JSValue, comptime arg_count: comptime_int, extra_args: [arg_count]JSValue) void { const prepared: PreparedRequest = switch (req) { .stack => |r| this.prepareJsRequestContext(r, resp, null, true, null) orelse return, .saved => |data| .{ @@ -2291,13 +2269,7 @@ pub fn NewServer(protocol_enum: enum { http, https }, development_kind: enum { d server.handleRequest(&should_deinit_context, prepared, req, response_value); } - pub fn onWebSocketUpgrade( - this: *ThisServer, - resp: *App.Response, - req: *uws.Request, - upgrade_ctx: *uws.SocketContext, - id: usize, - ) void { + pub fn onWebSocketUpgrade(this: *ThisServer, resp: *App.Response, req: *uws.Request, upgrade_ctx: *uws.SocketContext, id: usize) void { jsc.markBinding(@src()); if (id == 1) { // This is actually a UserRoute if id is 1 so it's safe to cast diff --git a/src/bun.js/bindings/NodeHTTP.cpp b/src/bun.js/bindings/NodeHTTP.cpp index 3a16e75f42..29eca691c0 100644 --- a/src/bun.js/bindings/NodeHTTP.cpp +++ b/src/bun.js/bindings/NodeHTTP.cpp @@ -150,7 +150,7 @@ public: if (!context) return false; auto* data = (uWS::HttpContextData*)us_socket_context_ext(is_ssl, context); if (!data) return false; - return data->isAuthorized(); + return data->flags.isAuthorized; } ~JSNodeHTTPServerSocket() { diff --git a/src/deps/libuwsockets.cpp b/src/deps/libuwsockets.cpp index 1efae06d80..07bcff0e42 100644 --- a/src/deps/libuwsockets.cpp +++ b/src/deps/libuwsockets.cpp @@ -377,6 +377,19 @@ extern "C" } } + void uws_app_close_idle(int ssl, uws_app_t *app) + { + if (ssl) + { + uWS::SSLApp *uwsApp = (uWS::SSLApp *)app; + uwsApp->closeIdle(); + } + else + { + uWS::App *uwsApp = (uWS::App *)app; + uwsApp->closeIdle(); + } + } void uws_app_set_on_clienterror(int ssl, uws_app_t *app, void (*handler)(void *user_data, int is_ssl, struct us_socket_t *rawSocket, uint8_t errorCode, char *rawPacket, int rawPacketLength), void *user_data) { @@ -1277,7 +1290,7 @@ extern "C" auto *data = uwsRes->getHttpResponseData(); data->offset = offset; data->state |= uWS::HttpResponseData::HTTP_END_CALLED; - data->markDone(); + data->markDone(uwsRes); uwsRes->resetTimeout(); } else @@ -1285,8 +1298,8 @@ extern "C" uWS::HttpResponse *uwsRes = (uWS::HttpResponse *)res; auto *data = uwsRes->getHttpResponseData(); data->offset = offset; - data->state |= uWS::HttpResponseData::HTTP_END_CALLED; - data->markDone(); + data->state |= uWS::HttpResponseData::HTTP_END_CALLED; + data->markDone(uwsRes); uwsRes->resetTimeout(); } } @@ -1328,7 +1341,7 @@ extern "C" uwsRes->AsyncSocket::write("\r\n", 2); } data->state |= uWS::HttpResponseData::HTTP_END_CALLED; - data->markDone(); + data->markDone(uwsRes); uwsRes->resetTimeout(); } else @@ -1350,7 +1363,7 @@ extern "C" uwsRes->AsyncSocket::write("\r\n", 2); } data->state |= uWS::HttpResponseData::HTTP_END_CALLED; - data->markDone(); + data->markDone(uwsRes); uwsRes->resetTimeout(); } } @@ -1793,7 +1806,7 @@ __attribute__((callback (corker, ctx))) uWS::HttpResponse *uwsRes = (uWS::HttpResponse *)res; uwsRes->flushHeaders(); } else { - uWS::HttpResponse *uwsRes = (uWS::HttpResponse *)res; + uWS::HttpResponse *uwsRes = (uWS::HttpResponse *)res; uwsRes->flushHeaders(); } } diff --git a/src/deps/uws/App.zig b/src/deps/uws/App.zig index 7280854773..be6c1950f6 100644 --- a/src/deps/uws/App.zig +++ b/src/deps/uws/App.zig @@ -43,9 +43,14 @@ pub fn NewApp(comptime ssl: bool) type { return c.uws_app_close(ssl_flag, @as(*uws_app_s, @ptrCast(this))); } + pub fn closeIdleConnections(this: *ThisApp) void { + return c.uws_app_close_idle(ssl_flag, @as(*uws_app_s, @ptrCast(this))); + } + pub fn create(opts: BunSocketContextOptions) ?*ThisApp { return @ptrCast(c.uws_create_app(ssl_flag, opts)); } + pub fn destroy(app: *ThisApp) void { return c.uws_app_destroy(ssl_flag, @as(*uws_app_s, @ptrCast(app))); } @@ -393,6 +398,7 @@ pub const c = struct { pub const uws_missing_server_handler = ?*const fn ([*c]const u8, ?*anyopaque) callconv(.C) void; pub extern fn uws_app_close(ssl: i32, app: *uws_app_s) void; + pub extern fn uws_app_close_idle(ssl: i32, app: *uws_app_s) void; pub extern fn uws_app_set_on_clienterror(ssl: c_int, app: *uws_app_s, handler: *const fn (*anyopaque, c_int, *us_socket_t, u8, ?[*]u8, c_int) callconv(.C) void, user_data: *anyopaque) void; pub extern fn uws_create_app(ssl: i32, options: BunSocketContextOptions) ?*uws_app_t; pub extern fn uws_app_destroy(ssl: i32, app: *uws_app_t) void; diff --git a/src/js/node/_http_server.ts b/src/js/node/_http_server.ts index 9c096a1c1b..873efe4f7b 100644 --- a/src/js/node/_http_server.ts +++ b/src/js/node/_http_server.ts @@ -303,7 +303,8 @@ Server.prototype.closeAllConnections = function () { }; Server.prototype.closeIdleConnections = function () { - // not actually implemented + const server = this[serverSymbol]; + server.closeIdleConnections(); }; Server.prototype.close = function (optionalCallback?) { @@ -319,6 +320,7 @@ Server.prototype.close = function (optionalCallback?) { } if (typeof optionalCallback === "function") setCloseCallback(this, optionalCallback); this.listening = false; + server.closeIdleConnections(); server.stop(); }; diff --git a/test/js/node/test/parallel/test-http-client-keep-alive-release-before-finish.js b/test/js/node/test/parallel/test-http-client-keep-alive-release-before-finish.js deleted file mode 100644 index e6e0bac1bb..0000000000 --- a/test/js/node/test/parallel/test-http-client-keep-alive-release-before-finish.js +++ /dev/null @@ -1,39 +0,0 @@ -'use strict'; -const common = require('../common'); -const http = require('http'); - -const server = http.createServer((req, res) => { - res.end(); -}).listen(0, common.mustCall(() => { - const agent = new http.Agent({ - maxSockets: 1, - keepAlive: true - }); - - const port = server.address().port; - - const post = http.request({ - agent, - method: 'POST', - port, - }, common.mustCall((res) => { - res.resume(); - })); - - // What happens here is that the server `end`s the response before we send - // `something`, and the client thought that this is a green light for sending - // next GET request - post.write(Buffer.alloc(16 * 1024, 'X')); - setTimeout(() => { - post.end('something'); - }, 100); - - http.request({ - agent, - method: 'GET', - port, - }, common.mustCall((res) => { - server.close(); - res.connection.end(); - })).end(); -})); diff --git a/test/js/node/test/parallel/test-http-flush-response-headers.js b/test/js/node/test/parallel/test-http-flush-response-headers.js index 1745d42285..0f0a1387b5 100644 --- a/test/js/node/test/parallel/test-http-flush-response-headers.js +++ b/test/js/node/test/parallel/test-http-flush-response-headers.js @@ -22,6 +22,6 @@ server.listen(0, common.localhostIPv4, function() { function onResponse(res) { assert.strictEqual(res.headers.foo, 'bar'); res.destroy(); - server.closeAllConnections(); + server.close(); } }); diff --git a/test/js/node/test/parallel/test-http-response-close.js b/test/js/node/test/parallel/test-http-response-close.js index 2ec1c260e9..848d316d8a 100644 --- a/test/js/node/test/parallel/test-http-response-close.js +++ b/test/js/node/test/parallel/test-http-response-close.js @@ -43,7 +43,7 @@ const assert = require('assert'); assert.strictEqual(res.destroyed, false); res.on('close', common.mustCall(() => { assert.strictEqual(res.destroyed, true); - server.closeAllConnections(); + server.close(); })); }) );