Merge branch 'ciro/fetch-upgrade' into ciro/fetch-upgrade-node-http

This commit is contained in:
Ciro Spaciari
2025-09-04 15:22:02 -07:00
committed by GitHub
20 changed files with 119 additions and 134 deletions

View File

@@ -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.
*

View File

@@ -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);

View File

@@ -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 */

View File

@@ -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)) {

View File

@@ -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

View File

@@ -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<SSL> *httpResponseData = HttpResponse<SSL>::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 <typename UserData>
TemplatedApp &&ws(std::string_view pattern, WebSocketBehavior<UserData> &&behavior) {
/* Don't compile if alignment rules cannot be satisfied */

View File

@@ -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 */

View File

@@ -137,10 +137,6 @@ private:
return (HttpContextData<SSL> *) us_socket_context_ext(SSL, getSocketContext());
}
static HttpContextData<SSL> *getSocketContextDataS(us_socket_t *s) {
return (HttpContextData<SSL> *) us_socket_context_ext(SSL, getSocketContext(s));
}
/* Init the HttpContext by registering libusockets event handlers */
HttpContext<SSL> *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<SSL> *) 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<SSL> *) s)->resetTimeout();
}
@@ -642,6 +640,10 @@ public:
}, priority);
}
static HttpContextData<SSL> *getSocketContextDataS(us_socket_t *s) {
return (HttpContextData<SSL> *) 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;

View File

@@ -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;
};
}

View File

@@ -50,6 +50,11 @@ public:
HttpResponseData<SSL> *getHttpResponseData() {
return (HttpResponseData<SSL> *) Super::getAsyncSocketData();
}
static HttpResponseData<SSL> *getHttpResponseDataS(us_socket_t *s) {
return (HttpResponseData<SSL> *) 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()) {

View File

@@ -22,11 +22,15 @@
#include "HttpParser.h"
#include "AsyncSocketData.h"
#include "ProxyParser.h"
#include "HttpContext.h"
#include "MoveOnlyFunction.h"
namespace uWS {
template <bool SSL>
struct HttpContext;
template <bool SSL>
struct HttpResponseData : AsyncSocketData<SSL>, HttpParser {
template <bool> friend struct HttpResponse;
@@ -38,7 +42,7 @@ struct HttpResponseData : AsyncSocketData<SSL>, HttpParser {
using OnDataCallback = void (*)(uWS::HttpResponse<SSL>* 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<SSL> *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<SSL>, HttpParser {
/* We are done with this request */
this->state &= ~HttpResponseData<SSL>::HTTP_RESPONSE_PENDING;
HttpResponseData<SSL> *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<SSL>, 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

View File

@@ -29,6 +29,10 @@ function generate(name) {
fn: "dispose",
length: 0,
},
closeIdleConnections: {
fn: "closeIdleConnections",
length: 0,
},
stop: {
fn: "doStop",
length: 1,

View File

@@ -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

View File

@@ -150,7 +150,7 @@ public:
if (!context) return false;
auto* data = (uWS::HttpContextData<true>*)us_socket_context_ext(is_ssl, context);
if (!data) return false;
return data->isAuthorized();
return data->flags.isAuthorized;
}
~JSNodeHTTPServerSocket()
{

View File

@@ -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<true>::HTTP_END_CALLED;
data->markDone();
data->markDone(uwsRes);
uwsRes->resetTimeout();
}
else
@@ -1285,8 +1298,8 @@ extern "C"
uWS::HttpResponse<false> *uwsRes = (uWS::HttpResponse<false> *)res;
auto *data = uwsRes->getHttpResponseData();
data->offset = offset;
data->state |= uWS::HttpResponseData<true>::HTTP_END_CALLED;
data->markDone();
data->state |= uWS::HttpResponseData<false>::HTTP_END_CALLED;
data->markDone(uwsRes);
uwsRes->resetTimeout();
}
}
@@ -1328,7 +1341,7 @@ extern "C"
uwsRes->AsyncSocket<true>::write("\r\n", 2);
}
data->state |= uWS::HttpResponseData<true>::HTTP_END_CALLED;
data->markDone();
data->markDone(uwsRes);
uwsRes->resetTimeout();
}
else
@@ -1350,7 +1363,7 @@ extern "C"
uwsRes->AsyncSocket<false>::write("\r\n", 2);
}
data->state |= uWS::HttpResponseData<false>::HTTP_END_CALLED;
data->markDone();
data->markDone(uwsRes);
uwsRes->resetTimeout();
}
}
@@ -1793,7 +1806,7 @@ __attribute__((callback (corker, ctx)))
uWS::HttpResponse<true> *uwsRes = (uWS::HttpResponse<true> *)res;
uwsRes->flushHeaders();
} else {
uWS::HttpResponse<false> *uwsRes = (uWS::HttpResponse<false> *)res;
uWS::HttpResponse<false> *uwsRes = (uWS::HttpResponse<false> *)res;
uwsRes->flushHeaders();
}
}

View File

@@ -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;

View File

@@ -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();
};

View File

@@ -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();
}));

View File

@@ -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();
}
});

View File

@@ -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();
}));
})
);