mirror of
https://github.com/oven-sh/bun
synced 2026-02-16 13:51:47 +00:00
Compare commits
20 Commits
claude/fix
...
ciro/fix-s
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f9c23893fd | ||
|
|
3d906a6a53 | ||
|
|
61e85a1aaf | ||
|
|
007db52dde | ||
|
|
8128e74a24 | ||
|
|
ccf0691608 | ||
|
|
20979eee57 | ||
|
|
31876c9845 | ||
|
|
b367efa86f | ||
|
|
39af21ecbd | ||
|
|
abd6be55e9 | ||
|
|
6440fbb7af | ||
|
|
61e4dc44ec | ||
|
|
ab3343c4a3 | ||
|
|
f3e33b0049 | ||
|
|
e5abb2c223 | ||
|
|
e2d21cf37a | ||
|
|
f274daf642 | ||
|
|
860e7a9311 | ||
|
|
7847eb95b3 |
@@ -14,7 +14,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// clang-format off
|
||||
#include "libusockets.h"
|
||||
#include "internal/internal.h"
|
||||
#include <stdlib.h>
|
||||
@@ -264,8 +264,61 @@ struct us_bun_verify_error_t us_socket_verify_error(int ssl, struct us_socket_t
|
||||
return (struct us_bun_verify_error_t) { .error = 0, .code = NULL, .reason = NULL };
|
||||
}
|
||||
|
||||
static struct us_socket_t * us_socket_context_on_open_dummy(struct us_socket_t *s, int is_client, char *ip, int ip_length) {
|
||||
return s;
|
||||
}
|
||||
|
||||
static struct us_socket_t *us_socket_context_on_close_dummy(struct us_socket_t *s, int code, void *reason) {
|
||||
return s;
|
||||
}
|
||||
|
||||
static struct us_socket_t *us_socket_context_on_data_dummy(struct us_socket_t *s, char *data, int length){
|
||||
return NULL; // null ends data loop
|
||||
}
|
||||
|
||||
static struct us_socket_t *us_socket_context_on_writable_dummy(struct us_socket_t *s) {
|
||||
return s;
|
||||
}
|
||||
|
||||
static struct us_socket_t *us_socket_context_on_timeout_dummy(struct us_socket_t *s) {
|
||||
return s;
|
||||
}
|
||||
|
||||
static struct us_socket_t *us_socket_context_on_connect_error_dummy(struct us_socket_t *s, int code) {
|
||||
return s;
|
||||
}
|
||||
|
||||
static struct us_socket_t *us_socket_context_on_end_dummy(struct us_socket_t *s) {
|
||||
return s;
|
||||
}
|
||||
|
||||
static void us_socket_context_on_handshake_dummy(struct us_socket_t *s, int success, struct us_bun_verify_error_t verify_error, void* custom_data) {
|
||||
return;
|
||||
}
|
||||
static struct us_socket_t *us_socket_context_on_long_timeout_dummy(struct us_socket_t *s) {
|
||||
return s;
|
||||
}
|
||||
|
||||
void us_socket_context_clean_callbacks(int ssl, struct us_socket_context_t *context) {
|
||||
us_socket_context_on_open(ssl, context, us_socket_context_on_open_dummy);
|
||||
us_socket_context_on_close(ssl, context, us_socket_context_on_close_dummy);
|
||||
us_socket_context_on_data(ssl, context, us_socket_context_on_data_dummy);
|
||||
us_socket_context_on_writable(ssl, context, us_socket_context_on_writable_dummy);
|
||||
us_socket_context_on_timeout(ssl, context, us_socket_context_on_timeout_dummy);
|
||||
us_socket_context_on_connect_error(ssl, context, us_socket_context_on_connect_error_dummy);
|
||||
us_socket_context_on_end(ssl, context, us_socket_context_on_end_dummy);
|
||||
us_socket_context_on_handshake(ssl, context, us_socket_context_on_handshake_dummy, NULL);
|
||||
us_socket_context_on_long_timeout(ssl, context, us_socket_context_on_long_timeout_dummy);
|
||||
}
|
||||
|
||||
void us_socket_context_free(int ssl, struct us_socket_context_t *context) {
|
||||
/* We clean callbacks so we avoid UAF when closing/deiniting */
|
||||
us_socket_context_clean_callbacks(ssl, context);
|
||||
/* We are deiniting so we garantee that we are closed */
|
||||
us_socket_context_close(ssl, context);
|
||||
// TODO: if we are listening and we have some sockets in accept loop, we cannot deinit because we will have UAF
|
||||
// we need to wait for all sockets to be closed before closing the context
|
||||
|
||||
#ifndef LIBUS_NO_SSL
|
||||
if (ssl) {
|
||||
/* This function will call us again with SSL=false */
|
||||
|
||||
@@ -180,6 +180,11 @@ int BIO_s_custom_read(BIO *bio, char *dst, int length) {
|
||||
return length;
|
||||
}
|
||||
|
||||
static void us_internal_ssl_on_socket_shutdown(struct us_internal_ssl_socket_t *s) {
|
||||
s->received_ssl_shutdown = 1;
|
||||
s->handshake_state = HANDSHAKE_COMPLETED;
|
||||
}
|
||||
|
||||
struct us_internal_ssl_socket_t *ssl_on_open(struct us_internal_ssl_socket_t *s,
|
||||
int is_client, char *ip,
|
||||
int ip_length) {
|
||||
@@ -345,7 +350,7 @@ void us_internal_update_handshake(struct us_internal_ssl_socket_t *s) {
|
||||
int result = SSL_do_handshake(s->ssl);
|
||||
|
||||
if (SSL_get_shutdown(s->ssl) & SSL_RECEIVED_SHUTDOWN) {
|
||||
s->received_ssl_shutdown = 1;
|
||||
us_internal_ssl_on_socket_shutdown(s);
|
||||
us_internal_ssl_socket_close(s, 0, NULL);
|
||||
return;
|
||||
}
|
||||
@@ -398,6 +403,7 @@ ssl_on_end(struct us_internal_ssl_socket_t *s) {
|
||||
return us_internal_ssl_socket_close(s, 0, NULL);
|
||||
}
|
||||
|
||||
|
||||
// this whole function needs a complete clean-up
|
||||
struct us_internal_ssl_socket_t *ssl_on_data(struct us_internal_ssl_socket_t *s,
|
||||
void *data, int length) {
|
||||
@@ -429,7 +435,9 @@ struct us_internal_ssl_socket_t *ssl_on_data(struct us_internal_ssl_socket_t *s,
|
||||
// two phase shutdown is complete here
|
||||
|
||||
/* Todo: this should also report some kind of clean shutdown */
|
||||
return us_internal_ssl_socket_close(s, 0, NULL);
|
||||
us_internal_ssl_on_socket_shutdown(s);
|
||||
us_internal_ssl_socket_close(s, 0, NULL);
|
||||
return NULL;
|
||||
} else if (ret < 0) {
|
||||
|
||||
int err = SSL_get_error(s->ssl, ret);
|
||||
@@ -442,7 +450,7 @@ struct us_internal_ssl_socket_t *ssl_on_data(struct us_internal_ssl_socket_t *s,
|
||||
}
|
||||
|
||||
// no further processing of data when in shutdown state
|
||||
return s;
|
||||
return NULL;
|
||||
}
|
||||
// bug checking: this loop needs a lot of attention and clean-ups and
|
||||
// check-ups
|
||||
@@ -456,8 +464,8 @@ restart:
|
||||
LIBUS_RECV_BUFFER_LENGTH - read);
|
||||
// we need to check if we received a shutdown here
|
||||
if (SSL_get_shutdown(s->ssl) & SSL_RECEIVED_SHUTDOWN) {
|
||||
s->received_ssl_shutdown = 1;
|
||||
// we will only close after we handle the data and errors
|
||||
us_internal_ssl_on_socket_shutdown(s);
|
||||
// process the data we might've received before
|
||||
}
|
||||
|
||||
if (just_read <= 0) {
|
||||
@@ -555,13 +563,9 @@ restart:
|
||||
}
|
||||
}
|
||||
|
||||
// we received the shutdown after reading so we close
|
||||
if (s->received_ssl_shutdown) {
|
||||
us_internal_ssl_socket_close(s, 0, NULL);
|
||||
return NULL;
|
||||
}
|
||||
// trigger writable if we failed last write with want read
|
||||
if (s->ssl_write_wants_read) {
|
||||
// if ssl is shutdown, ignore this
|
||||
if (s->ssl_write_wants_read && !s->received_ssl_shutdown) {
|
||||
s->ssl_write_wants_read = 0;
|
||||
|
||||
// make sure to update context before we call (context can change if the
|
||||
@@ -1693,7 +1697,7 @@ void *us_internal_ssl_socket_ext(struct us_internal_ssl_socket_t *s) {
|
||||
|
||||
int us_internal_ssl_socket_is_shut_down(struct us_internal_ssl_socket_t *s) {
|
||||
return us_socket_is_shut_down(0, &s->s) ||
|
||||
SSL_get_shutdown(s->ssl) & SSL_SENT_SHUTDOWN;
|
||||
(SSL_get_shutdown(s->ssl) & SSL_SENT_SHUTDOWN);
|
||||
}
|
||||
|
||||
void us_internal_ssl_socket_shutdown(struct us_internal_ssl_socket_t *s) {
|
||||
|
||||
@@ -804,7 +804,7 @@ pub const Listener = struct {
|
||||
.this_value = .zero,
|
||||
.socket = socket,
|
||||
.protos = listener.protos,
|
||||
.owned_protos = false,
|
||||
.flags = .{ .owned_protos = false },
|
||||
};
|
||||
if (listener.strong_data.get()) |default_data| {
|
||||
const globalObject = listener.handlers.globalObject;
|
||||
@@ -1140,20 +1140,26 @@ fn selectALPNCallback(
|
||||
}
|
||||
|
||||
fn NewSocket(comptime ssl: bool) type {
|
||||
const SocketFlags = packed struct(u8) {
|
||||
detached: bool = false,
|
||||
finalizing: bool = false,
|
||||
is_active: bool = false,
|
||||
owned_protos: bool = true,
|
||||
authorized: bool = false,
|
||||
// future flags
|
||||
_reserved: u3 = 0,
|
||||
};
|
||||
return struct {
|
||||
pub const Socket = uws.NewSocketHandler(ssl);
|
||||
socket: Socket,
|
||||
detached: bool = false,
|
||||
flags: SocketFlags = .{},
|
||||
wrapped: WrappedType = .none,
|
||||
handlers: *Handlers,
|
||||
this_value: JSC.JSValue = .zero,
|
||||
poll_ref: Async.KeepAlive = Async.KeepAlive.init(),
|
||||
is_active: bool = false,
|
||||
last_4: [4]u8 = .{ 0, 0, 0, 0 },
|
||||
authorized: bool = false,
|
||||
connection: ?Listener.UnixOrHost = null,
|
||||
protos: ?[]const u8,
|
||||
owned_protos: bool = true,
|
||||
server_name: ?[]const u8 = null,
|
||||
|
||||
// TODO: switch to something that uses `visitAggregate` and have the
|
||||
@@ -1221,7 +1227,7 @@ fn NewSocket(comptime ssl: bool) type {
|
||||
) void {
|
||||
JSC.markBinding(@src());
|
||||
log("onWritable", .{});
|
||||
if (this.detached) return;
|
||||
if (this.flags.detached) return;
|
||||
const handlers = this.handlers;
|
||||
const callback = handlers.onWritable;
|
||||
if (callback == .zero) return;
|
||||
@@ -1245,7 +1251,7 @@ fn NewSocket(comptime ssl: bool) type {
|
||||
) void {
|
||||
JSC.markBinding(@src());
|
||||
log("onTimeout", .{});
|
||||
if (this.detached) return;
|
||||
if (this.flags.detached) return;
|
||||
|
||||
const handlers = this.handlers;
|
||||
const callback = handlers.onTimeout;
|
||||
@@ -1268,8 +1274,8 @@ fn NewSocket(comptime ssl: bool) type {
|
||||
}
|
||||
fn handleConnectError(this: *This, errno: c_int) void {
|
||||
log("onConnectError({d})", .{errno});
|
||||
if (this.detached) return;
|
||||
this.detached = true;
|
||||
if (this.flags.detached) return;
|
||||
this.flags.detached = true;
|
||||
defer this.markInactive();
|
||||
|
||||
const handlers = this.handlers;
|
||||
@@ -1328,27 +1334,27 @@ fn NewSocket(comptime ssl: bool) type {
|
||||
}
|
||||
|
||||
pub fn markActive(this: *This) void {
|
||||
if (!this.is_active) {
|
||||
if (!this.flags.is_active) {
|
||||
this.handlers.markActive();
|
||||
this.is_active = true;
|
||||
this.flags.is_active = true;
|
||||
this.has_pending_activity.store(true, .Release);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn markInactive(this: *This) void {
|
||||
if (this.is_active) {
|
||||
if (!this.detached) {
|
||||
if (this.flags.is_active) {
|
||||
if (!this.flags.detached) {
|
||||
// we have to close the socket before the socket context is closed
|
||||
// otherwise we will get a segfault
|
||||
// uSockets will defer closing the TCP socket until the next tick
|
||||
if (!this.socket.isClosed()) {
|
||||
this.detached = true;
|
||||
this.flags.detached = true;
|
||||
this.socket.close(0, null);
|
||||
// onClose will call markInactive again
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.is_active = false;
|
||||
this.flags.is_active = false;
|
||||
const vm = this.handlers.vm;
|
||||
|
||||
this.handlers.markInactive(ssl, this.socket.context(), this.wrapped);
|
||||
@@ -1393,7 +1399,7 @@ fn NewSocket(comptime ssl: bool) type {
|
||||
}
|
||||
}
|
||||
|
||||
this.detached = false;
|
||||
this.flags.detached = false;
|
||||
this.socket = socket;
|
||||
|
||||
if (this.wrapped == .none) {
|
||||
@@ -1426,7 +1432,7 @@ fn NewSocket(comptime ssl: bool) type {
|
||||
});
|
||||
|
||||
if (result.toError()) |err| {
|
||||
this.detached = true;
|
||||
this.flags.detached = true;
|
||||
defer this.markInactive();
|
||||
if (!this.socket.isClosed()) {
|
||||
log("Closing due to error", .{});
|
||||
@@ -1453,7 +1459,7 @@ fn NewSocket(comptime ssl: bool) type {
|
||||
pub fn onEnd(this: *This, socket: Socket) void {
|
||||
JSC.markBinding(@src());
|
||||
log("onEnd", .{});
|
||||
if (this.detached) return;
|
||||
if (this.flags.detached) return;
|
||||
|
||||
const handlers = this.handlers;
|
||||
|
||||
@@ -1485,11 +1491,11 @@ fn NewSocket(comptime ssl: bool) type {
|
||||
pub fn onHandshake(this: *This, socket: Socket, success: i32, ssl_error: uws.us_bun_verify_error_t) void {
|
||||
log("onHandshake({d})", .{success});
|
||||
JSC.markBinding(@src());
|
||||
if (this.detached) return;
|
||||
if (this.flags.detached) return;
|
||||
|
||||
const authorized = if (success == 1) true else false;
|
||||
|
||||
this.authorized = authorized;
|
||||
this.flags.authorized = authorized;
|
||||
|
||||
const handlers = this.handlers;
|
||||
var callback = handlers.onHandshake;
|
||||
@@ -1548,8 +1554,11 @@ fn NewSocket(comptime ssl: bool) type {
|
||||
|
||||
pub fn onClose(this: *This, socket: Socket, err: c_int, _: ?*anyopaque) void {
|
||||
JSC.markBinding(@src());
|
||||
// We call close inside the finalizer, before this we always mark as finalizing
|
||||
// this is to avoid calling the close/error callbacks and interaction with JS at this point
|
||||
if (this.flags.finalizing) return;
|
||||
log("onClose", .{});
|
||||
this.detached = true;
|
||||
this.flags.detached = true;
|
||||
defer this.markInactive();
|
||||
|
||||
const handlers = this.handlers;
|
||||
@@ -1579,7 +1588,7 @@ fn NewSocket(comptime ssl: bool) type {
|
||||
pub fn onData(this: *This, socket: Socket, data: []const u8) void {
|
||||
JSC.markBinding(@src());
|
||||
log("onData({d})", .{data.len});
|
||||
if (this.detached) return;
|
||||
if (this.flags.detached) return;
|
||||
|
||||
const handlers = this.handlers;
|
||||
const callback = handlers.onData;
|
||||
@@ -1627,7 +1636,7 @@ fn NewSocket(comptime ssl: bool) type {
|
||||
this: *This,
|
||||
_: *JSC.JSGlobalObject,
|
||||
) callconv(.C) JSValue {
|
||||
if (!this.handlers.is_server or this.detached) {
|
||||
if (!this.handlers.is_server or this.flags.detached) {
|
||||
return JSValue.jsUndefined();
|
||||
}
|
||||
|
||||
@@ -1640,7 +1649,7 @@ fn NewSocket(comptime ssl: bool) type {
|
||||
) callconv(.C) JSValue {
|
||||
log("getReadyState()", .{});
|
||||
|
||||
if (this.detached) {
|
||||
if (this.flags.detached) {
|
||||
return JSValue.jsNumber(@as(i32, -1));
|
||||
} else if (this.socket.isClosed()) {
|
||||
return JSValue.jsNumber(@as(i32, 0));
|
||||
@@ -1658,7 +1667,7 @@ fn NewSocket(comptime ssl: bool) type {
|
||||
_: *JSC.JSGlobalObject,
|
||||
) callconv(.C) JSValue {
|
||||
log("getAuthorized()", .{});
|
||||
return JSValue.jsBoolean(this.authorized);
|
||||
return JSValue.jsBoolean(this.flags.authorized);
|
||||
}
|
||||
pub fn timeout(
|
||||
this: *This,
|
||||
@@ -1667,7 +1676,7 @@ fn NewSocket(comptime ssl: bool) type {
|
||||
) callconv(.C) JSValue {
|
||||
JSC.markBinding(@src());
|
||||
const args = callframe.arguments(1);
|
||||
if (this.detached) return JSValue.jsUndefined();
|
||||
if (this.flags.detached) return JSValue.jsUndefined();
|
||||
if (args.len == 0) {
|
||||
globalObject.throw("Expected 1 argument, got 0", .{});
|
||||
return .zero;
|
||||
@@ -1690,7 +1699,7 @@ fn NewSocket(comptime ssl: bool) type {
|
||||
) callconv(.C) JSValue {
|
||||
JSC.markBinding(@src());
|
||||
|
||||
if (this.detached) {
|
||||
if (this.flags.detached) {
|
||||
return JSValue.jsNull();
|
||||
}
|
||||
|
||||
@@ -1720,7 +1729,7 @@ fn NewSocket(comptime ssl: bool) type {
|
||||
) callconv(.C) JSValue {
|
||||
JSC.markBinding(@src());
|
||||
|
||||
if (this.detached) {
|
||||
if (this.flags.detached) {
|
||||
return JSValue.jsNumber(@as(i32, -1));
|
||||
}
|
||||
|
||||
@@ -1741,7 +1750,7 @@ fn NewSocket(comptime ssl: bool) type {
|
||||
this: *This,
|
||||
_: *JSC.JSGlobalObject,
|
||||
) callconv(.C) JSValue {
|
||||
if (this.detached) {
|
||||
if (this.flags.detached) {
|
||||
return JSValue.jsUndefined();
|
||||
}
|
||||
|
||||
@@ -1752,7 +1761,7 @@ fn NewSocket(comptime ssl: bool) type {
|
||||
this: *This,
|
||||
globalThis: *JSC.JSGlobalObject,
|
||||
) callconv(.C) JSValue {
|
||||
if (this.detached) {
|
||||
if (this.flags.detached) {
|
||||
return JSValue.jsUndefined();
|
||||
}
|
||||
|
||||
@@ -1773,7 +1782,7 @@ fn NewSocket(comptime ssl: bool) type {
|
||||
}
|
||||
|
||||
fn writeMaybeCorked(this: *This, buffer: []const u8, is_end: bool) i32 {
|
||||
if (this.detached or this.socket.isShutdown() or this.socket.isClosed()) {
|
||||
if (this.flags.detached or this.socket.isShutdown() or this.socket.isClosed()) {
|
||||
return -1;
|
||||
}
|
||||
// we don't cork yet but we might later
|
||||
@@ -1943,7 +1952,7 @@ fn NewSocket(comptime ssl: bool) type {
|
||||
_: *JSC.CallFrame,
|
||||
) callconv(.C) JSValue {
|
||||
JSC.markBinding(@src());
|
||||
if (!this.detached)
|
||||
if (!this.flags.detached)
|
||||
this.socket.flush();
|
||||
|
||||
return JSValue.jsUndefined();
|
||||
@@ -1956,7 +1965,7 @@ fn NewSocket(comptime ssl: bool) type {
|
||||
) callconv(.C) JSValue {
|
||||
JSC.markBinding(@src());
|
||||
const args = callframe.arguments(1);
|
||||
if (!this.detached) {
|
||||
if (!this.flags.detached) {
|
||||
if (args.len > 0 and args.ptr[0].toBoolean()) {
|
||||
this.socket.shutdownRead();
|
||||
} else {
|
||||
@@ -1978,7 +1987,7 @@ fn NewSocket(comptime ssl: bool) type {
|
||||
|
||||
log("end({d} args)", .{args.len});
|
||||
|
||||
if (this.detached) {
|
||||
if (this.flags.detached) {
|
||||
return JSValue.jsNumber(@as(i32, -1));
|
||||
}
|
||||
|
||||
@@ -1997,7 +2006,7 @@ fn NewSocket(comptime ssl: bool) type {
|
||||
|
||||
pub fn ref(this: *This, globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSValue {
|
||||
JSC.markBinding(@src());
|
||||
if (this.detached) return JSValue.jsUndefined();
|
||||
if (this.flags.detached) return JSValue.jsUndefined();
|
||||
this.poll_ref.ref(globalObject.bunVM());
|
||||
return JSValue.jsUndefined();
|
||||
}
|
||||
@@ -2010,8 +2019,9 @@ fn NewSocket(comptime ssl: bool) type {
|
||||
|
||||
pub fn finalize(this: *This) callconv(.C) void {
|
||||
log("finalize() {d}", .{@intFromPtr(this)});
|
||||
if (!this.detached) {
|
||||
this.detached = true;
|
||||
this.flags.finalizing = true;
|
||||
if (!this.flags.detached) {
|
||||
this.flags.detached = true;
|
||||
if (!this.socket.isClosed()) {
|
||||
this.socket.close(0, null);
|
||||
}
|
||||
@@ -2021,7 +2031,7 @@ fn NewSocket(comptime ssl: bool) type {
|
||||
|
||||
this.poll_ref.unref(JSC.VirtualMachine.get());
|
||||
// need to deinit event without being attached
|
||||
if (this.owned_protos) {
|
||||
if (this.flags.owned_protos) {
|
||||
if (this.protos) |protos| {
|
||||
this.protos = null;
|
||||
default_allocator.free(protos);
|
||||
@@ -2047,7 +2057,7 @@ fn NewSocket(comptime ssl: bool) type {
|
||||
return .zero;
|
||||
}
|
||||
|
||||
if (this.detached) {
|
||||
if (this.flags.detached) {
|
||||
return JSValue.jsUndefined();
|
||||
}
|
||||
|
||||
@@ -2085,7 +2095,7 @@ fn NewSocket(comptime ssl: bool) type {
|
||||
if (comptime ssl == false) {
|
||||
return JSValue.jsUndefined();
|
||||
}
|
||||
if (this.detached) {
|
||||
if (this.flags.detached) {
|
||||
return JSValue.jsUndefined();
|
||||
}
|
||||
|
||||
@@ -2102,7 +2112,7 @@ fn NewSocket(comptime ssl: bool) type {
|
||||
if (comptime ssl == false) {
|
||||
return JSValue.jsUndefined();
|
||||
}
|
||||
if (this.detached) {
|
||||
if (this.flags.detached) {
|
||||
return JSValue.jsUndefined();
|
||||
}
|
||||
|
||||
@@ -2143,7 +2153,7 @@ fn NewSocket(comptime ssl: bool) type {
|
||||
if (comptime ssl == false) {
|
||||
return JSValue.jsUndefined();
|
||||
}
|
||||
if (this.detached) {
|
||||
if (this.flags.detached) {
|
||||
return JSValue.jsUndefined();
|
||||
}
|
||||
|
||||
@@ -2164,7 +2174,7 @@ fn NewSocket(comptime ssl: bool) type {
|
||||
return JSValue.jsUndefined();
|
||||
}
|
||||
|
||||
if (this.detached) {
|
||||
if (this.flags.detached) {
|
||||
return JSValue.jsUndefined();
|
||||
}
|
||||
|
||||
@@ -2191,7 +2201,7 @@ fn NewSocket(comptime ssl: bool) type {
|
||||
return JSValue.jsUndefined();
|
||||
}
|
||||
|
||||
if (this.detached) {
|
||||
if (this.flags.detached) {
|
||||
return JSValue.jsUndefined();
|
||||
}
|
||||
|
||||
@@ -2237,7 +2247,7 @@ fn NewSocket(comptime ssl: bool) type {
|
||||
return JSValue.jsUndefined();
|
||||
}
|
||||
|
||||
if (this.detached) {
|
||||
if (this.flags.detached) {
|
||||
return JSValue.jsUndefined();
|
||||
}
|
||||
|
||||
@@ -2265,7 +2275,7 @@ fn NewSocket(comptime ssl: bool) type {
|
||||
return JSValue.jsBoolean(false);
|
||||
}
|
||||
|
||||
if (this.detached) {
|
||||
if (this.flags.detached) {
|
||||
return JSValue.jsBoolean(false);
|
||||
}
|
||||
|
||||
@@ -2297,7 +2307,7 @@ fn NewSocket(comptime ssl: bool) type {
|
||||
return JSValue.jsUndefined();
|
||||
}
|
||||
|
||||
if (this.detached) {
|
||||
if (this.flags.detached) {
|
||||
return JSValue.jsUndefined();
|
||||
}
|
||||
|
||||
@@ -2385,7 +2395,7 @@ fn NewSocket(comptime ssl: bool) type {
|
||||
return JSValue.jsNull();
|
||||
}
|
||||
|
||||
if (this.detached) {
|
||||
if (this.flags.detached) {
|
||||
return JSValue.jsNull();
|
||||
}
|
||||
|
||||
@@ -2454,7 +2464,7 @@ fn NewSocket(comptime ssl: bool) type {
|
||||
return JSValue.jsUndefined();
|
||||
}
|
||||
|
||||
if (this.detached) {
|
||||
if (this.flags.detached) {
|
||||
return JSValue.jsUndefined();
|
||||
}
|
||||
var result = JSValue.createEmptyObject(globalObject, 3);
|
||||
@@ -2501,7 +2511,7 @@ fn NewSocket(comptime ssl: bool) type {
|
||||
return JSValue.jsUndefined();
|
||||
}
|
||||
|
||||
if (this.detached) {
|
||||
if (this.flags.detached) {
|
||||
return JSValue.jsUndefined();
|
||||
}
|
||||
|
||||
@@ -2533,7 +2543,7 @@ fn NewSocket(comptime ssl: bool) type {
|
||||
return JSValue.jsUndefined();
|
||||
}
|
||||
|
||||
if (this.detached) {
|
||||
if (this.flags.detached) {
|
||||
return JSValue.jsUndefined();
|
||||
}
|
||||
|
||||
@@ -2566,7 +2576,7 @@ fn NewSocket(comptime ssl: bool) type {
|
||||
return JSValue.jsNull();
|
||||
}
|
||||
|
||||
if (this.detached) {
|
||||
if (this.flags.detached) {
|
||||
return JSValue.jsNull();
|
||||
}
|
||||
const ssl_ptr = @as(*BoringSSL.SSL, @ptrCast(this.socket.getNativeHandle()));
|
||||
@@ -2657,7 +2667,7 @@ fn NewSocket(comptime ssl: bool) type {
|
||||
return JSValue.jsNull();
|
||||
}
|
||||
|
||||
if (this.detached) {
|
||||
if (this.flags.detached) {
|
||||
return JSValue.jsNull();
|
||||
}
|
||||
|
||||
@@ -2680,7 +2690,7 @@ fn NewSocket(comptime ssl: bool) type {
|
||||
return JSValue.jsBoolean(false);
|
||||
}
|
||||
|
||||
if (this.detached) {
|
||||
if (this.flags.detached) {
|
||||
return JSValue.jsBoolean(false);
|
||||
}
|
||||
|
||||
@@ -2719,7 +2729,7 @@ fn NewSocket(comptime ssl: bool) type {
|
||||
return JSValue.jsUndefined();
|
||||
}
|
||||
|
||||
if (this.detached) {
|
||||
if (this.flags.detached) {
|
||||
return JSValue.jsUndefined();
|
||||
}
|
||||
|
||||
@@ -2773,7 +2783,7 @@ fn NewSocket(comptime ssl: bool) type {
|
||||
return JSValue.jsUndefined();
|
||||
}
|
||||
|
||||
if (this.detached) {
|
||||
if (this.flags.detached) {
|
||||
return JSValue.jsUndefined();
|
||||
}
|
||||
|
||||
@@ -2836,7 +2846,7 @@ fn NewSocket(comptime ssl: bool) type {
|
||||
this.server_name = slice;
|
||||
}
|
||||
|
||||
if (this.detached) {
|
||||
if (this.flags.detached) {
|
||||
// will be attached onOpen
|
||||
return JSValue.jsUndefined();
|
||||
}
|
||||
@@ -2871,7 +2881,7 @@ fn NewSocket(comptime ssl: bool) type {
|
||||
return JSValue.jsUndefined();
|
||||
}
|
||||
|
||||
if (this.detached) {
|
||||
if (this.flags.detached) {
|
||||
return JSValue.jsUndefined();
|
||||
}
|
||||
|
||||
@@ -3026,10 +3036,10 @@ fn NewSocket(comptime ssl: bool) type {
|
||||
new_socket.startTLS(!this.handlers.is_server);
|
||||
|
||||
//detach and invalidate the old instance
|
||||
this.detached = true;
|
||||
if (this.is_active) {
|
||||
this.flags.detached = true;
|
||||
if (this.flags.is_active) {
|
||||
const vm = this.handlers.vm;
|
||||
this.is_active = false;
|
||||
this.flags.is_active = false;
|
||||
// will free handlers and the old_context when hits 0 active connections
|
||||
// the connection can be upgraded inside a handler call so we need to garantee that it will be still alive
|
||||
this.handlers.markInactive(ssl, old_context, this.wrapped);
|
||||
|
||||
@@ -56,6 +56,40 @@ describe("WebSocket", () => {
|
||||
await closed;
|
||||
Bun.gc(true);
|
||||
});
|
||||
it("should handle shutdown properly", async () => {
|
||||
using server = Bun.serve({
|
||||
port: 0,
|
||||
tls: COMMON_CERT,
|
||||
fetch(req, server) {
|
||||
if (server.upgrade(req)) {
|
||||
return;
|
||||
}
|
||||
return new Response("Upgrade failed :(", { status: 500 });
|
||||
},
|
||||
websocket: {
|
||||
message(ws, message) {
|
||||
// echo
|
||||
ws.send(message);
|
||||
},
|
||||
open(ws) {},
|
||||
},
|
||||
});
|
||||
|
||||
const websockets = [];
|
||||
|
||||
for (let i = 0; i < 10_000; i++) {
|
||||
const ws = new WebSocket(server.url.href, { tls: { rejectUnauthorized: false } });
|
||||
const { promise, resolve, reject } = Promise.withResolvers();
|
||||
ws.onopen = () => {
|
||||
ws.send("message");
|
||||
resolve();
|
||||
};
|
||||
ws.onerror = reject;
|
||||
|
||||
websockets.push(promise);
|
||||
}
|
||||
await Promise.all(websockets);
|
||||
}, 60_000);
|
||||
|
||||
it("should connect many times over https", async () => {
|
||||
using server = Bun.serve({
|
||||
@@ -75,21 +109,18 @@ describe("WebSocket", () => {
|
||||
open(ws) {},
|
||||
},
|
||||
});
|
||||
{
|
||||
for (let i = 0; i < 1000; i++) {
|
||||
const ws = new WebSocket(server.url.href, { tls: { rejectUnauthorized: false } });
|
||||
await new Promise((resolve, reject) => {
|
||||
ws.onopen = resolve;
|
||||
ws.onerror = reject;
|
||||
});
|
||||
var closed = new Promise((resolve, reject) => {
|
||||
ws.onclose = resolve;
|
||||
});
|
||||
|
||||
ws.close();
|
||||
await closed;
|
||||
for (let i = 0; i < 1000; i++) {
|
||||
const ws = new WebSocket(server.url.href, { tls: { rejectUnauthorized: false } });
|
||||
{
|
||||
const { promise, resolve, reject } = Promise.withResolvers();
|
||||
ws.onopen = resolve;
|
||||
ws.onerror = reject;
|
||||
await promise;
|
||||
}
|
||||
Bun.gc(true);
|
||||
const { promise: closed, resolve: resolveOnClose } = Promise.withResolvers();
|
||||
ws.onclose = resolveOnClose;
|
||||
ws.close();
|
||||
await closed;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user