aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bench.node.cjs0
-rw-r--r--src/deps/uws.zig7
-rw-r--r--src/env.zig1
-rw-r--r--src/global.zig37
-rw-r--r--src/http/websocket.zig28
-rw-r--r--src/http/websocket_http_client.zig576
-rw-r--r--src/javascript/jsc/bindings/ScriptExecutionContext.cpp74
-rw-r--r--src/javascript/jsc/bindings/ScriptExecutionContext.h10
-rw-r--r--src/javascript/jsc/bindings/headers-cpp.h2
-rw-r--r--src/javascript/jsc/bindings/headers-handwritten.h2
-rw-r--r--src/javascript/jsc/bindings/headers-replacements.zig2
-rw-r--r--src/javascript/jsc/bindings/headers.h529
-rw-r--r--src/javascript/jsc/bindings/headers.zig2
-rw-r--r--src/javascript/jsc/bindings/webcore/WebSocket.cpp219
-rw-r--r--src/javascript/jsc/bindings/webcore/WebSocket.h6
-rw-r--r--src/javascript/jsc/event_loop.zig2
-rw-r--r--src/linear_fifo.zig536
17 files changed, 1363 insertions, 670 deletions
diff --git a/bench.node.cjs b/bench.node.cjs
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/bench.node.cjs
diff --git a/src/deps/uws.zig b/src/deps/uws.zig
index 48c93ab53..695cef9cb 100644
--- a/src/deps/uws.zig
+++ b/src/deps/uws.zig
@@ -48,7 +48,8 @@ pub fn NewSocketHandler(comptime ssl: bool) type {
comptime ssl_int,
this.socket,
data.ptr,
- @intCast(c_int, data.len),
+ // truncate to 31 bits since sign bit exists
+ @intCast(c_int, @truncate(u31, data.len)),
@as(c_int, @boolToInt(msg_more)),
);
}
@@ -214,10 +215,10 @@ pub fn NewSocketHandler(comptime ssl: bool) type {
comptime socket_field_name: []const u8,
ctx: Context,
) ?*Context {
- var adopted = Socket{ .socket = us_socket_context_adopt_socket(comptime ssl_int, socket_ctx, socket, @sizeOf(Context)) orelse return null };
+ var adopted = ThisSocket{ .socket = us_socket_context_adopt_socket(comptime ssl_int, socket_ctx, socket, @sizeOf(Context)) orelse return null };
var holder = adopted.ext(Context) orelse {
if (comptime bun.Environment.allow_assert) unreachable;
- _ = us_socket_close(comptime ssl_int, socket);
+ _ = us_socket_close(comptime ssl_int, socket, 0, null);
return null;
};
holder.* = ctx;
diff --git a/src/env.zig b/src/env.zig
index 9801966c0..0e4cbc414 100644
--- a/src/env.zig
+++ b/src/env.zig
@@ -25,3 +25,4 @@ pub const isX86 = @import("builtin").target.cpu.arch.isX86();
pub const isX64 = @import("builtin").target.cpu.arch == .x86_64;
pub const allow_assert = isDebug or isTest;
pub const analytics_url = if (isDebug) "http://localhost:4000/events" else "http://i.bun.sh/events";
+pub const simd = isX86 or isAarch64;
diff --git a/src/global.zig b/src/global.zig
index 6160ce781..adf7bed06 100644
--- a/src/global.zig
+++ b/src/global.zig
@@ -201,3 +201,40 @@ pub inline fn range(comptime min: anytype, comptime max: anytype) [max - min]usi
break :brk slice;
};
}
+
+pub fn copy(comptime Type: type, dest: []Type, src: []const Type) void {
+ std.debug.assert(dest.len >= src.len);
+ var input = std.mem.sliceAsBytes(src);
+ var output = std.mem.sliceAsBytes(dest);
+ var input_end = input.ptr + input.len;
+ const output_end = output.ptr + output.len;
+
+ if (@ptrToInt(input.ptr) <= @ptrToInt(output.ptr) and @ptrToInt(output_end) <= @ptrToInt(input_end)) {
+ // input is overlapping with output
+ if (input.len > strings.ascii_vector_size) {
+ const input_end_vectorized = input.ptr + input.len - (input.len % strings.ascii_vector_size);
+ while (input.ptr != input_end_vectorized) {
+ const input_vec = @as(@Vector(strings.ascii_vector_size, u8), input[0..strings.ascii_vector_size].*);
+ output[0..strings.ascii_vector_size].* = input_vec;
+ input = input[strings.ascii_vector_size..];
+ output = output[strings.ascii_vector_size..];
+ }
+ }
+
+ while (input.len >= @sizeOf(usize)) {
+ output[0..@sizeOf(usize)].* = input[0..@sizeOf(usize)].*;
+ input = input[@sizeOf(usize)..];
+ output = output[@sizeOf(usize)..];
+ }
+
+ while (input.ptr != input_end) {
+ output[0] = input[0];
+ input = input[1..];
+ output = output[1..];
+ }
+ } else {
+ @memcpy(output.ptr, input.ptr, input.len);
+ }
+}
+
+pub const LinearFifo = @import("./linear_fifo.zig").LinearFifo;
diff --git a/src/http/websocket.zig b/src/http/websocket.zig
index 6bb433ea3..12348f83c 100644
--- a/src/http/websocket.zig
+++ b/src/http/websocket.zig
@@ -54,20 +54,26 @@ pub const WebsocketHeader = packed struct {
final: bool = true,
pub fn writeHeader(header: WebsocketHeader, writer: anytype, n: usize) anyerror!void {
- try writer.writeIntBig(u16, @bitCast(u16, header));
-
- // Write extended length if needed
- switch (n) {
- 0...126 => {}, // Included in header
- 127...0xFFFF => try writer.writeIntBig(u16, @truncate(u16, n)),
- else => try writer.writeIntBig(u64, n),
+ // packed structs are sometimes buggy
+ // lets check it worked right
+ if (comptime Environment.allow_assert) {
+ var buf_ = [2]u8{ 0, 0 };
+ var stream = std.io.fixedBufferStream(&buf_);
+ stream.writer().writeIntBig(u16, @bitCast(u16, header)) catch unreachable;
+ stream.pos = 0;
+ const casted = stream.reader().readIntBig(u16) catch unreachable;
+ std.debug.assert(casted == @bitCast(u16, header));
+ std.debug.assert(std.meta.eql(@bitCast(WebsocketHeader, casted), header));
}
+
+ try writer.writeIntBig(u16, @bitCast(u16, header));
+ std.debug.assert(header.len == packLength(n));
}
pub fn packLength(length: usize) u7 {
return switch (length) {
- 0...126 => @truncate(u7, length),
- 127...0xFFFF => 126,
+ 0...125 => @truncate(u7, length),
+ 126...0xFFFF => 126,
else => 127,
};
}
@@ -77,8 +83,8 @@ pub const WebsocketHeader = packed struct {
pub fn lengthByteCount(byte_length: usize) usize {
return switch (byte_length) {
- 0...126 => 0,
- 127...0xFFFF => @sizeOf(u16),
+ 0...125 => 0,
+ 126...0xFFFF => @sizeOf(u16),
else => @sizeOf(u64),
};
}
diff --git a/src/http/websocket_http_client.zig b/src/http/websocket_http_client.zig
index 2be382dbe..8522c6038 100644
--- a/src/http/websocket_http_client.zig
+++ b/src/http/websocket_http_client.zig
@@ -92,6 +92,7 @@ const ErrorCode = enum(i32) {
compression_unsupported,
unexpected_mask_from_server,
expected_control_frame,
+ unsupported_control_frame,
unexpected_opcode,
invalid_utf8,
};
@@ -101,9 +102,9 @@ extern fn WebSocket__didConnect(
buffered_data: ?[*]u8,
buffered_len: usize,
) void;
-extern fn WebSocket__didFailWithErrorCode(websocket_context: *anyopaque, reason: ErrorCode) void;
+extern fn WebSocket__didCloseWithErrorCode(websocket_context: *anyopaque, reason: ErrorCode) void;
extern fn WebSocket__didReceiveText(websocket_context: *anyopaque, clone: bool, text: *const JSC.ZigString) void;
-extern fn WebSocket__didReceiveBytes(websocket_context: *anyopaque, bytes: []const u8) void;
+extern fn WebSocket__didReceiveBytes(websocket_context: *anyopaque, bytes: [*]const u8, byte_len: usize) void;
const body_buf_len = 16384 - 16;
const BodyBufBytes = [body_buf_len]u8;
@@ -159,7 +160,7 @@ pub fn NewHTTPUpgradeClient(comptime ssl: bool) type {
var client_protocol_hash: u64 = 0;
var body = buildRequestBody(global.bunVM(), pathname, host, client_protocol, &client_protocol_hash) catch return null;
var client: HTTPClient = HTTPClient{
- .socket = undefined,
+ .tcp = undefined,
.outgoing_websocket = websocket,
.input_body_buf = body,
.websocket_protocol = client_protocol_hash,
@@ -200,14 +201,14 @@ pub fn NewHTTPUpgradeClient(comptime ssl: bool) type {
pub fn fail(this: *HTTPClient, code: ErrorCode) void {
JSC.markBinding();
- WebSocket__didFailWithErrorCode(this.outgoing_websocket, code);
+ WebSocket__didCloseWithErrorCode(this.outgoing_websocket, code);
this.cancel();
}
pub fn handleClose(this: *HTTPClient, _: Socket, _: c_int, _: ?*anyopaque) void {
JSC.markBinding();
this.clearData();
- WebSocket__didFailWithErrorCode(this.outgoing_websocket, ErrorCode.ended);
+ WebSocket__didCloseWithErrorCode(this.outgoing_websocket, ErrorCode.ended);
}
pub fn terminate(this: *HTTPClient, code: ErrorCode) void {
@@ -407,6 +408,7 @@ pub fn NewHTTPUpgradeClient(comptime ssl: bool) type {
this.clearData();
JSC.markBinding();
+ this.tcp.timeout(0);
WebSocket__didConnect(this.outgoing_websocket, this.tcp.socket, overflow.ptr, overflow.len);
}
@@ -500,17 +502,45 @@ pub const Mask = struct {
pub fn fill(this: *Mask, mask_buf: *[4]u8, output_: []u8, input_: []const u8) void {
const mask = this.get();
mask_buf.* = mask;
+
+ const skip_mask = @bitCast(u32, mask) == 0;
+ if (!skip_mask) {
+ fillWithSkipMask(mask, output_, input_, false);
+ } else {
+ fillWithSkipMask(mask, output_, input_, true);
+ }
+ }
+
+ fn fillWithSkipMask(mask: [4]u8, output_: []u8, input_: []const u8, comptime skip_mask: bool) void {
var input = input_;
var output = output_;
+
if (comptime Environment.isAarch64 or Environment.isX64) {
if (input.len >= strings.ascii_vector_size) {
- const vec: strings.AsciiVector = @as(strings.AsciiVector, mask ** (strings.ascii_vector_size / 4));
+ const vec: strings.AsciiVector = brk: {
+ var in: [strings.ascii_vector_size]u8 = undefined;
+ comptime var i: usize = 0;
+ inline while (i < strings.ascii_vector_size) : (i += 4) {
+ in[i..][0..4].* = mask;
+ }
+ break :brk @as(strings.AsciiVector, in);
+ };
const end_ptr_wrapped_to_last_16 = input.ptr + input.len - (input.len % strings.ascii_vector_size);
- while (input.ptr != end_ptr_wrapped_to_last_16) {
- const input_vec: strings.AsciiVector = @as(strings.AsciiVector, input[0..strings.ascii_vector_size].*);
- output.ptr[0..strings.ascii_vector_size].* = input_vec ^ vec;
- output = output[strings.ascii_vector_size..];
- input = input[strings.ascii_vector_size..];
+
+ if (comptime skip_mask) {
+ while (input.ptr != end_ptr_wrapped_to_last_16) {
+ const input_vec: strings.AsciiVector = @as(strings.AsciiVector, input[0..strings.ascii_vector_size].*);
+ output.ptr[0..strings.ascii_vector_size].* = input_vec;
+ output = output[strings.ascii_vector_size..];
+ input = input[strings.ascii_vector_size..];
+ }
+ } else {
+ while (input.ptr != end_ptr_wrapped_to_last_16) {
+ const input_vec: strings.AsciiVector = @as(strings.AsciiVector, input[0..strings.ascii_vector_size].*);
+ output.ptr[0..strings.ascii_vector_size].* = input_vec ^ vec;
+ output = output[strings.ascii_vector_size..];
+ input = input[strings.ascii_vector_size..];
+ }
}
}
@@ -518,20 +548,35 @@ pub const Mask = struct {
std.debug.assert(input.len < strings.ascii_vector_size);
}
- while (input.len >= 4) {
- const input_vec: [4]u8 = input[0..4].*;
- output.ptr[0..4].* = [4]u8{
- input_vec[0] ^ mask[0],
- input_vec[1] ^ mask[1],
- input_vec[2] ^ mask[2],
- input_vec[3] ^ mask[3],
- };
- output = output[4..];
- input = input[4..];
+ if (comptime !skip_mask) {
+ while (input.len >= 4) {
+ const input_vec: [4]u8 = input[0..4].*;
+ output.ptr[0..4].* = [4]u8{
+ input_vec[0] ^ mask[0],
+ input_vec[1] ^ mask[1],
+ input_vec[2] ^ mask[2],
+ input_vec[3] ^ mask[3],
+ };
+ output = output[4..];
+ input = input[4..];
+ }
+ } else {
+ while (input.len >= 4) {
+ const input_vec: [4]u8 = input[0..4].*;
+ output.ptr[0..4].* = input_vec;
+ output = output[4..];
+ input = input[4..];
+ }
}
- for (input) |c, i| {
- output[i] = c ^ mask[i % 4];
+ if (comptime !skip_mask) {
+ for (input) |c, i| {
+ output[i] = c ^ mask[i % 4];
+ }
+ } else {
+ for (input) |c, i| {
+ output[i] = c;
+ }
}
}
@@ -548,7 +593,8 @@ const ReceiveState = enum {
extended_payload_length_16,
extended_payload_length_64,
ping,
- closing,
+ pong,
+ close,
fail,
pub fn needControlFrame(this: ReceiveState) bool {
@@ -629,71 +675,82 @@ const Copy = union(enum) {
return WebsocketHeader.frameSizeIncludingMask(byte_len.*);
},
.latin1 => {
- byte_len.* = strings.elementLengthLatin1IntoUTF8([]const u8, this.latin1);
+ byte_len.* = this.latin1.len;
return WebsocketHeader.frameSizeIncludingMask(byte_len.*);
},
.bytes => {
byte_len.* = this.bytes.len;
return WebsocketHeader.frameSizeIncludingMask(byte_len.*);
},
- .raw => return this.raw.len,
+ .raw => {
+ byte_len.* = this.raw.len;
+ return this.raw.len;
+ },
}
}
pub fn copy(this: @This(), globalThis: *JSC.JSGlobalObject, buf: []u8, content_byte_len: usize) void {
+ if (this == .raw) {
+ std.debug.assert(buf.len >= this.raw.len);
+ std.debug.assert(buf.ptr != this.raw.ptr);
+ @memcpy(buf.ptr, this.raw.ptr, this.raw.len);
+ return;
+ }
+
+ const how_big_is_the_length_integer = WebsocketHeader.lengthByteCount(content_byte_len);
+ const how_big_is_the_mask = 4;
+ const mask_offset = 2 + how_big_is_the_length_integer;
+ const content_offset = mask_offset + how_big_is_the_mask;
+
+ // 2 byte header
+ // 4 byte mask
+ // 0, 2, 8 byte length
+ var to_mask = buf[content_offset..];
+
+ var header = @bitCast(WebsocketHeader, @as(u16, 0));
+
+ // Write extended length if needed
+ switch (how_big_is_the_length_integer) {
+ 0 => {},
+ 2 => std.mem.writeIntBig(u16, buf[2..][0..2], @truncate(u16, content_byte_len)),
+ 8 => std.mem.writeIntBig(u64, buf[2..][0..8], @truncate(u64, content_byte_len)),
+ else => unreachable,
+ }
+
+ header.mask = true;
+ header.compressed = false;
+ header.final = true;
+
+ std.debug.assert(WebsocketHeader.frameSizeIncludingMask(content_byte_len) == buf.len);
+
switch (this) {
.utf16 => |utf16| {
- const length_offset = 2;
- const length_length = WebsocketHeader.lengthByteCount(content_byte_len);
- const mask_offset = length_offset + length_length;
- const content_offset = mask_offset + 4;
- var to_mask = buf[content_offset..];
- const encode_into_result = strings.copyUTF16IntoUTF8(utf16, to_mask);
+ header.len = WebsocketHeader.packLength(content_byte_len);
+ const encode_into_result = strings.copyUTF16IntoUTF8(to_mask, []const u16, utf16);
std.debug.assert(@as(usize, encode_into_result.written) == content_byte_len);
std.debug.assert(@as(usize, encode_into_result.read) == utf16.len);
- var header = @bitCast(WebsocketHeader, @as(u16, 0));
- header.len = WebsocketHeader.packLength(content_byte_len);
- header.mask = true;
+ header.len = WebsocketHeader.packLength(encode_into_result.written);
header.opcode = Opcode.Text;
- header.compressed = false;
- header.final = true;
- header.writeHeader(std.io.fixedBufferStream(buf), content_byte_len) catch unreachable;
- Mask.from(globalThis).fill(buf[mask_offset..][0..4], to_mask, to_mask);
+ header.writeHeader(std.io.fixedBufferStream(buf).writer(), encode_into_result.written) catch unreachable;
+
+ Mask.from(globalThis).fill(buf[mask_offset..][0..4], to_mask[0..content_byte_len], to_mask[0..content_byte_len]);
},
.latin1 => |latin1| {
- const length_offset = 2;
- const length_length = WebsocketHeader.lengthByteCount(content_byte_len);
- const mask_offset = length_offset + length_length;
- const content_offset = mask_offset + 4;
- var to_mask = buf[content_offset..];
- const encode_into_result = strings.copyLatin1IntoUTF8(latin1, to_mask);
+ const encode_into_result = strings.copyLatin1IntoUTF8(to_mask, []const u8, latin1);
std.debug.assert(@as(usize, encode_into_result.written) == content_byte_len);
std.debug.assert(@as(usize, encode_into_result.read) == latin1.len);
- var header = @bitCast(WebsocketHeader, @as(u16, 0));
- header.len = WebsocketHeader.packLength(content_byte_len);
- header.mask = true;
+ header.len = WebsocketHeader.packLength(encode_into_result.written);
header.opcode = Opcode.Text;
- header.compressed = false;
- header.final = true;
- header.writeHeader(std.io.fixedBufferStream(buf), content_byte_len) catch unreachable;
- Mask.from(globalThis).fill(buf[mask_offset..][0..4], to_mask, to_mask);
+ header.writeHeader(std.io.fixedBufferStream(buf).writer(), encode_into_result.written) catch unreachable;
+ Mask.from(globalThis).fill(buf[mask_offset..][0..4], to_mask[0..content_byte_len], to_mask[0..content_byte_len]);
},
.bytes => |bytes| {
- const length_offset = 2;
- const length_length = WebsocketHeader.lengthByteCount(bytes.len);
- const mask_offset = length_offset + length_length;
- const content_offset = mask_offset + 4;
- var to_mask = buf[content_offset..];
- var header = @bitCast(WebsocketHeader, @as(u16, 0));
header.len = WebsocketHeader.packLength(bytes.len);
- header.mask = true;
- header.opcode = Opcode.Text;
- header.compressed = false;
- header.final = true;
- header.writeHeader(std.io.fixedBufferStream(buf), bytes.len) catch unreachable;
- Mask.from(globalThis).fill(buf[mask_offset..][0..4], to_mask, to_mask);
+ header.opcode = Opcode.Binary;
+ header.writeHeader(std.io.fixedBufferStream(buf).writer(), bytes.len) catch unreachable;
+ Mask.from(globalThis).fill(buf[mask_offset..][0..4], to_mask[0..content_byte_len], bytes);
},
- .raw => @memcpy(buf.ptr, this.raw.ptr, this.raw.len),
+ .raw => unreachable,
}
}
};
@@ -709,7 +766,7 @@ pub fn NewWebSocketClient(comptime ssl: bool) type {
receive_remaining: usize = 0,
receiving_type: Opcode = Opcode.ResB,
- ping_frame_bytes: [128 + 6]u8 = [_]u8{0} ** 128 + 6,
+ ping_frame_bytes: [128 + 6]u8 = [_]u8{0} ** (128 + 6),
ping_len: u8 = 0,
receive_frame: usize = 0,
@@ -717,9 +774,8 @@ pub fn NewWebSocketClient(comptime ssl: bool) type {
receive_pending_chunk_len: usize = 0,
receive_body_buf: ?*BodyBuf = null,
receive_overflow_buffer: std.ArrayListUnmanaged(u8) = .{},
- send_overflow_buffer: std.ArrayListUnmanaged(u8) = .{},
- send_body_buf: ?*BodyBuf = null,
+ send_buffer: bun.LinearFifo(u8, .Dynamic),
send_len: usize = 0,
send_off: usize = 0,
@@ -728,10 +784,11 @@ pub fn NewWebSocketClient(comptime ssl: bool) type {
pub const name = if (ssl) "WebSocketClientTLS" else "WebSocketClient";
pub const shim = JSC.Shimmer("Bun", name, @This());
+ const stack_frame_size = 1024;
- const HTTPClient = @This();
+ const WebSocket = @This();
- pub fn register(global: *JSC.JSGlobalObject, loop_: *anyopaque, parent: *anyopaque, ctx_: *anyopaque) callconv(.C) void {
+ pub fn register(global: *JSC.JSGlobalObject, loop_: *anyopaque, ctx_: *anyopaque) callconv(.C) void {
var vm = global.bunVM();
var loop = @ptrCast(*uws.Loop, loop_);
var ctx: *uws.us_socket_context_t = @ptrCast(*uws.us_socket_context_t, ctx_);
@@ -742,10 +799,9 @@ pub fn NewWebSocketClient(comptime ssl: bool) type {
vm.uws_event_loop = loop;
- Socket.configureChild(
+ Socket.configure(
ctx,
- parent,
- HTTPClient,
+ WebSocket,
null,
handleClose,
handleData,
@@ -756,7 +812,7 @@ pub fn NewWebSocketClient(comptime ssl: bool) type {
);
}
- pub fn clearData(this: *HTTPClient) void {
+ pub fn clearData(this: *WebSocket) void {
this.clearReceiveBuffers(true);
this.clearSendBuffers(true);
this.ping_len = 0;
@@ -764,7 +820,7 @@ pub fn NewWebSocketClient(comptime ssl: bool) type {
this.receive_remaining = 0;
}
- pub fn cancel(this: *HTTPClient) callconv(.C) void {
+ pub fn cancel(this: *WebSocket) callconv(.C) void {
this.clearData();
if (this.tcp.isClosed() or this.tcp.isShutdown())
@@ -777,34 +833,34 @@ pub fn NewWebSocketClient(comptime ssl: bool) type {
}
}
- pub fn fail(this: *HTTPClient, code: ErrorCode) void {
+ pub fn fail(this: *WebSocket, code: ErrorCode) void {
JSC.markBinding();
if (this.outgoing_websocket) |ws|
- WebSocket__didFailWithErrorCode(ws, code);
+ WebSocket__didCloseWithErrorCode(ws, code);
this.cancel();
}
- pub fn handleClose(this: *HTTPClient, _: Socket, _: c_int, _: ?*anyopaque) void {
+ pub fn handleClose(this: *WebSocket, _: Socket, _: c_int, _: ?*anyopaque) void {
JSC.markBinding();
this.clearData();
if (this.outgoing_websocket) |ws|
- WebSocket__didFailWithErrorCode(ws, ErrorCode.ended);
+ WebSocket__didCloseWithErrorCode(ws, ErrorCode.ended);
}
- pub fn terminate(this: *HTTPClient, code: ErrorCode) void {
+ pub fn terminate(this: *WebSocket, code: ErrorCode) void {
this.fail(code);
}
- fn getBody(this: *HTTPClient) *BodyBufBytes {
- if (this.send_body_buf == null) {
- this.send_body_buf = BodyBufPool.get(bun.default_allocator);
- }
+ // fn getBody(this: *WebSocket) *BodyBufBytes {
+ // if (this.send_body_buf == null) {
+ // this.send_body_buf = BodyBufPool.get(bun.default_allocator);
+ // }
- return &this.send_body_buf.?.data;
- }
+ // return &this.send_body_buf.?.data;
+ // }
- fn getReceiveBody(this: *HTTPClient) *BodyBufBytes {
+ fn getReceiveBody(this: *WebSocket) *BodyBufBytes {
if (this.receive_body_buf == null) {
this.receive_body_buf = BodyBufPool.get(bun.default_allocator);
}
@@ -812,7 +868,7 @@ pub fn NewWebSocketClient(comptime ssl: bool) type {
return &this.receive_body_buf.?.data;
}
- fn clearReceiveBuffers(this: *HTTPClient, free: bool) void {
+ fn clearReceiveBuffers(this: *WebSocket, free: bool) void {
if (this.receive_body_buf) |receive_buf| {
receive_buf.release();
this.receive_body_buf = null;
@@ -825,50 +881,58 @@ pub fn NewWebSocketClient(comptime ssl: bool) type {
}
}
- fn clearSendBuffers(this: *HTTPClient, free: bool) void {
- if (this.send_body_buf) |buf| {
- buf.release();
- this.send_body_buf = null;
- }
+ fn clearSendBuffers(this: *WebSocket, free: bool) void {
+ // if (this.send_body_buf) |buf| {
+ // buf.release();
+ // this.send_body_buf = null;
+ // }
+ this.send_buffer.discard(this.send_buffer.count);
if (free) {
- this.send_overflow_buffer.clearAndFree(bun.default_allocator);
- } else {
- this.send_overflow_buffer.clearRetainingCapacity();
+ this.send_buffer.deinit();
+ this.send_buffer.buf.len = 0;
}
+
this.send_off = 0;
this.send_len = 0;
}
- fn dispatchData(this: *HTTPClient, data_: []const u8, kind: Opcode) void {
+ fn dispatchData(this: *WebSocket, data_: []const u8, kind: Opcode) void {
+ var out = this.outgoing_websocket orelse {
+ this.clearData();
+ return;
+ };
switch (kind) {
.Text => {
// this function encodes to UTF-16 if > 127
// so we don't need to worry about latin1 non-ascii code points
const utf16_bytes_ = strings.toUTF16Alloc(bun.default_allocator, data_, true) catch {
this.terminate(ErrorCode.invalid_utf8);
- return 0;
+ return;
};
defer this.clearReceiveBuffers(false);
var outstring = JSC.ZigString.Empty;
if (utf16_bytes_) |utf16| {
outstring = JSC.ZigString.from16Slice(utf16);
outstring.markUTF16();
- WebSocket__didReceiveText(this.outgoing_websocket, false, &outstring);
+ JSC.markBinding();
+ WebSocket__didReceiveText(out, false, &outstring);
} else {
outstring = JSC.ZigString.init(data_);
- WebSocket__didReceiveText(this.outgoing_websocket, true, &outstring);
+ JSC.markBinding();
+ WebSocket__didReceiveText(out, true, &outstring);
}
},
.Binary => {
- WebSocket__didReceiveBytes(this.outgoing_websocket, data_);
+ JSC.markBinding();
+ WebSocket__didReceiveBytes(out, data_.ptr, data_.len);
this.clearReceiveBuffers(false);
},
else => unreachable,
}
}
- pub fn consume(this: *HTTPClient, data_: []const u8, max: usize, kind: Opcode, is_final: bool) usize {
+ pub fn consume(this: *WebSocket, data_: []const u8, max: usize, kind: Opcode, is_final: bool) usize {
std.debug.assert(kind == .Text or kind == .Binary);
std.debug.assert(data_.len <= max);
@@ -898,7 +962,7 @@ pub fn NewWebSocketClient(comptime ssl: bool) type {
if (new_pending_chunk_len <= body_buf_len) {
// if our previously-allocated buffer is too small or we don't have one, use from the pool
var body = this.getReceiveBody();
- @memcpy(body + this.receive_pending_chunk_len, data_.ptr, data_.len);
+ @memcpy(body[this.receive_pending_chunk_len..].ptr, data_.ptr, data_.len);
if (can_dispatch_data) {
this.dispatchData(body[0..new_pending_chunk_len], kind);
this.receive_pending_chunk_len = 0;
@@ -924,7 +988,7 @@ pub fn NewWebSocketClient(comptime ssl: bool) type {
}
}
- pub fn handleData(this: *HTTPClient, socket: Socket, data_: []const u8) void {
+ pub fn handleData(this: *WebSocket, socket: Socket, data_: []const u8) void {
var data = data_;
var receive_state = this.receive_state;
var terminated = false;
@@ -983,7 +1047,7 @@ pub fn NewWebSocketClient(comptime ssl: bool) type {
is_final = false;
receive_state = parseWebSocketHeader(
- header_bytes[0..2],
+ header_bytes[0..2].*,
&receiving_type,
&receive_body_remain,
&is_fragmented,
@@ -1005,7 +1069,7 @@ pub fn NewWebSocketClient(comptime ssl: bool) type {
switch (receiving_type) {
.Continue, .Text, .Binary, .Ping, .Pong, .Close => {},
else => {
- this.terminate(ErrorCode.unexpected_opcode);
+ this.terminate(ErrorCode.unsupported_control_frame);
terminated = true;
break;
},
@@ -1037,8 +1101,8 @@ pub fn NewWebSocketClient(comptime ssl: bool) type {
// Multibyte length quantities are expressed in network byte order
receive_body_remain = switch (byte_size) {
- 8 => @as(usize, std.mem.readIntBig(u64, data[0..8].*)),
- 2 => @as(usize, std.mem.readIntBig(u16, data[0..2].*)),
+ 8 => @as(usize, std.mem.readIntBig(u64, data[0..8])),
+ 2 => @as(usize, std.mem.readIntBig(u16, data[0..2])),
else => unreachable,
};
data = data[byte_size..];
@@ -1058,7 +1122,7 @@ pub fn NewWebSocketClient(comptime ssl: bool) type {
this.ping_len = @truncate(u8, ping_len);
if (ping_len > 0) {
- @memcpy(&this.ping_frame_bytes + 6, data.ptr, ping_len);
+ @memcpy(this.ping_frame_bytes[6..], data.ptr, ping_len);
data = data[ping_len..];
}
@@ -1110,7 +1174,7 @@ pub fn NewWebSocketClient(comptime ssl: bool) type {
break;
},
.fail => {
- this.terminate(ErrorCode.unexpected_control_frame);
+ this.terminate(ErrorCode.unsupported_control_frame);
terminated = true;
break;
},
@@ -1118,18 +1182,18 @@ pub fn NewWebSocketClient(comptime ssl: bool) type {
}
}
- pub fn sendClose(this: *HTTPClient) void {
- this.sendCloseWithBody(this.tcp, 1001, null, null, 0);
+ pub fn sendClose(this: *WebSocket) void {
+ this.sendCloseWithBody(this.tcp, 1001, null, 0);
}
fn enqueueEncodedBytesMaybeFinal(
- this: *HTTPClient,
+ this: *WebSocket,
socket: Socket,
bytes: []const u8,
is_closing: bool,
) bool {
// fast path: no backpressure, no queue, just send the bytes.
- if (this.send_len == 0) {
+ if (!this.hasBackpressure()) {
const wrote = socket.write(bytes, !is_closing);
const expected = @intCast(c_int, bytes.len);
if (wrote == expected) {
@@ -1141,82 +1205,87 @@ pub fn NewWebSocketClient(comptime ssl: bool) type {
return false;
}
- _ = this.copyToSendBuffer(bytes[bytes.len - @intCast(usize, wrote) ..], false, is_closing, false);
+ _ = this.copyToSendBuffer(bytes[@intCast(usize, wrote)..], false, is_closing);
return true;
}
- return this.copyToSendBuffer(bytes, true, is_closing, false);
+ return this.copyToSendBuffer(bytes, true, is_closing);
}
- fn copyToSendBuffer(this: *HTTPClient, bytes: []const u8, do_write: bool, is_closing: bool) bool {
+ fn copyToSendBuffer(this: *WebSocket, bytes: []const u8, do_write: bool, is_closing: bool) bool {
return this.sendData(.{ .raw = bytes }, do_write, is_closing);
}
- fn sendData(this: *HTTPClient, bytes: Copy, do_write: bool, is_closing: bool) bool {
+ fn sendData(this: *WebSocket, bytes: Copy, do_write: bool, is_closing: bool) bool {
var content_byte_len: usize = 0;
const write_len = bytes.len(&content_byte_len);
std.debug.assert(write_len > 0);
- this.send_len += write_len;
- var out_buf: []const u8 = "";
- const send_end = this.send_off + this.send_len;
- var ring_buffer = &this.send_overflow_buffer;
-
- if (send_end <= ring_buffer.capacity) {
- bytes.copy(this.globalThis, ring_buffer.items.ptr[send_end - write_len .. send_end], content_byte_len);
- ring_buffer.items.len += write_len;
-
- out_buf = ring_buffer.items[this.send_off..send_end];
- } else if (send_end <= body_buf_len) {
- var buf = this.getBody();
- bytes.copy(this.globalThis, buf[send_end - write_len ..][0..write_len], content_byte_len);
- out_buf = buf[this.send_off..send_end];
- } else {
- if (this.send_body_buf) |send_body| {
- // transfer existing send buffer to overflow buffer
- ring_buffer.ensureTotalCapacityPrecise(bun.default_allocator, this.send_len) catch {
- this.terminate(ErrorCode.failed_to_allocate_memory);
- return false;
- };
- const off = this.send_len - write_len;
- @memcpy(ring_buffer.items.ptr, send_body + this.send_off, off);
- send_body.release();
- bytes.copy(this.globalThis, (ring_buffer.items.ptr + off)[0..write_len], content_byte_len);
- ring_buffer.items.len = this.send_len;
- this.send_body_buf = null;
- this.send_off = 0;
- } else if (send_end <= ring_buffer.capacity) {
- bytes.copy(this.globalThis, (ring_buffer.items.ptr + (send_end - write_len))[0..write_len], content_byte_len);
- ring_buffer.items.len += write_len;
- // can we treat it as a ring buffer without re-allocating the array?
- } else if (send_end - this.send_off <= ring_buffer.capacity) {
- std.mem.copyBackwards(u8, ring_buffer.items[0..this.send_len], ring_buffer.items[this.send_off..send_end]);
- bytes.copy(this.globalThis, ring_buffer.items.ptr[this.send_len - write_len .. this.send_len], content_byte_len);
- ring_buffer.items.len = this.send_len;
- this.send_off = 0;
- } else {
- // we need to re-allocate the array
- ring_buffer.ensureTotalCapacity(bun.default_allocator, this.send_len) catch {
- this.terminate(ErrorCode.failed_to_allocate_memory);
- return false;
- };
-
- const data_to_copy = ring_buffer.items[this.send_off..][0..(this.send_len - write_len)];
- const position_in_slice = ring_buffer.items[0 .. this.send_len - write_len];
- if (data_to_copy.len > 0 and data_to_copy.ptr != position_in_slice.ptr)
- std.mem.copyBackwards(
- u8,
- position_in_slice,
- data_to_copy,
- );
-
- ring_buffer.items.len = this.send_len;
- bytes.copy(this.globalThis, ring_buffer.items[this.send_len - write_len ..], content_byte_len);
- this.send_off = 0;
- }
-
- out_buf = ring_buffer.items[this.send_off..send_end];
- }
+ // this.send_len += write_len;
+ var writable = this.send_buffer.writableWithSize(write_len) catch unreachable;
+ bytes.copy(this.globalThis, writable[0..write_len], content_byte_len);
+ this.send_buffer.update(write_len);
+ // const send_end = this.send_off + this.send_len;
+ // var ring_buffer = &this.send_overflow_buffer;
+ // const prev_len = this.send_len - write_len;
+
+ // if (send_end <= ring_buffer.capacity) {
+ // const mid = ring_buffer.capacity / 2;
+ // if (send_end > mid and this.send_len < mid) {
+ // std.mem.copyBackwards(u8, ring_buffer.items.ptr[0..prev_len], ring_buffer.items.ptr[this.send_off..ring_buffer.capacity][0..prev_len]);
+ // this.send_off = 0;
+ // ring_buffer.items.len = prev_len;
+ // bytes.copy(this.globalThis, ring_buffer.items.ptr[prev_len..this.send_len], content_byte_len);
+ // } else {
+ // bytes.copy(this.globalThis, ring_buffer.items.ptr[send_end - write_len .. send_end], content_byte_len);
+ // }
+ // ring_buffer.items.len += write_len;
+
+ // out_buf = ring_buffer.items[this.send_off..][0..this.send_len];
+ // } else if (send_end <= body_buf_len) {
+ // var buf = this.getBody();
+ // bytes.copy(this.globalThis, buf[send_end - write_len ..][0..write_len], content_byte_len);
+ // out_buf = buf[this.send_off..][0..this.send_len];
+ // } else {
+ // if (this.send_body_buf) |send_body| {
+ // var buf = &send_body.data;
+ // // transfer existing send buffer to overflow buffer
+ // ring_buffer.ensureTotalCapacityPrecise(bun.default_allocator, this.send_len) catch {
+ // this.terminate(ErrorCode.failed_to_allocate_memory);
+ // return false;
+ // };
+ // @memcpy(ring_buffer.items.ptr, buf[this.send_off..].ptr, prev_len);
+ // send_body.release();
+ // bytes.copy(this.globalThis, (ring_buffer.items.ptr + prev_len)[0..write_len], content_byte_len);
+ // ring_buffer.items.len = this.send_len;
+ // this.send_body_buf = null;
+ // this.send_off = 0;
+ // } else if (send_end <= ring_buffer.capacity) {
+ // bytes.copy(this.globalThis, (ring_buffer.items.ptr + (send_end - write_len))[0..write_len], content_byte_len);
+ // ring_buffer.items.len += write_len;
+ // } else {
+ // // we need to re-allocate the array
+ // ring_buffer.ensureTotalCapacity(bun.default_allocator, this.send_len) catch {
+ // this.terminate(ErrorCode.failed_to_allocate_memory);
+ // return false;
+ // };
+
+ // const data_to_copy = ring_buffer.items[this.send_off..][0..prev_len];
+ // const position_in_slice = ring_buffer.items[0..prev_len];
+ // if (data_to_copy.len > 0 and data_to_copy.ptr != position_in_slice.ptr)
+ // std.mem.copyBackwards(
+ // u8,
+ // position_in_slice,
+ // data_to_copy,
+ // );
+
+ // ring_buffer.items.len = this.send_len;
+ // bytes.copy(this.globalThis, ring_buffer.items[prev_len..], content_byte_len);
+ // this.send_off = 0;
+ // }
+
+ // out_buf = ring_buffer.items[this.send_off..][0..this.send_len];
+ // }
if (do_write) {
if (comptime Environment.allow_assert) {
@@ -1224,61 +1293,66 @@ pub fn NewWebSocketClient(comptime ssl: bool) type {
std.debug.assert(!this.tcp.isClosed());
std.debug.assert(this.tcp.isEstablished());
}
- return this.sendBuffer(out_buf, is_closing);
+ return this.sendBuffer(this.send_buffer.readableSlice(0), is_closing, !is_closing);
}
return true;
}
- fn sendBuffer(this: *HTTPClient, out_buf: []const u8, is_closing: bool) bool {
+ fn sendBuffer(
+ this: *WebSocket,
+ out_buf: []const u8,
+ is_closing: bool,
+ _: bool,
+ ) bool {
std.debug.assert(out_buf.len > 0);
- const wrote = this.tcp.write(out_buf, !is_closing);
- const expected = @intCast(c_int, out_buf.len);
- if (wrote == expected) {
- this.clearSendBuffers(false);
- return true;
- }
-
+ _ = is_closing;
+ // set msg_more to false
+ // it seems to improve perf by ~20%
+ const wrote = this.tcp.write(out_buf, false);
if (wrote < 0) {
this.terminate(ErrorCode.failed_to_write);
return false;
}
+ const expected = @intCast(usize, wrote);
+ var readable = this.send_buffer.readableSlice(0);
+ if (readable.ptr == out_buf.ptr) {
+ this.send_buffer.discard(expected);
+ }
- this.send_len -= @intCast(usize, wrote);
- this.send_off += @intCast(usize, wrote);
return true;
}
- fn enqueueEncodedBytes(this: *HTTPClient, socket: Socket, bytes: []const u8) bool {
+ fn enqueueEncodedBytes(this: *WebSocket, socket: Socket, bytes: []const u8) bool {
return this.enqueueEncodedBytesMaybeFinal(socket, bytes, false);
}
- fn sendPong(this: *HTTPClient, socket: Socket) bool {
+ fn sendPong(this: *WebSocket, socket: Socket) bool {
if (socket.isClosed() or socket.isShutdown()) {
this.dispatchClose();
return false;
}
var header = @bitCast(WebsocketHeader, @as(u16, 0));
- header.fin = true;
+ header.final = true;
header.opcode = .Pong;
- var to_mask = &this.ping_frame_bytes[6..][0..this.ping_len];
+ var to_mask = this.ping_frame_bytes[6..][0..this.ping_len];
header.mask = to_mask.len > 0;
header.len = @truncate(u7, this.ping_len);
this.ping_frame_bytes[0..2].* = @bitCast(u16, header);
if (to_mask.len > 0) {
- Mask.from(this.globalThis).fill(&this.ping_frame_bytes[2..6], &to_mask, &to_mask);
+ Mask.from(this.globalThis).fill(this.ping_frame_bytes[2..6], to_mask, to_mask);
return this.enqueueEncodedBytes(socket, this.ping_frame_bytes[0 .. 6 + @as(usize, this.ping_len)]);
} else {
- return this.enqueueEncodedBytes(socket, &this.ping_frame_bytes[0..2]);
+ return this.enqueueEncodedBytes(socket, this.ping_frame_bytes[0..2]);
}
}
fn sendCloseWithBody(
- this: *HTTPClient,
+ this: *WebSocket,
socket: Socket,
code: u16,
body: ?*[125]u8,
@@ -1293,16 +1367,16 @@ pub fn NewWebSocketClient(comptime ssl: bool) type {
socket.shutdownRead();
var final_body_bytes: [128 + 8]u8 = undefined;
var header = @bitCast(WebsocketHeader, @as(u16, 0));
- header.fin = true;
+ header.final = true;
header.opcode = .Close;
header.mask = true;
- header.len = body_len + 2;
+ header.len = @truncate(u7, body_len + 2);
final_body_bytes[0..2].* = @bitCast([2]u8, @bitCast(u16, header));
- var mask_buf = &final_body_bytes[2..6];
- std.mem.writeIntSliceBig(u16, &final_body_bytes[6..8], code);
+ var mask_buf: *[4]u8 = final_body_bytes[2..6];
+ std.mem.writeIntSliceBig(u16, final_body_bytes[6..8], code);
if (body) |data| {
- if (body_len > 0) @memcpy(&final_body_bytes[8..], data, body_len);
+ if (body_len > 0) @memcpy(final_body_bytes[8..], data, body_len);
}
// we must mask the code
@@ -1315,41 +1389,37 @@ pub fn NewWebSocketClient(comptime ssl: bool) type {
}
}
- pub fn handleEnd(this: *HTTPClient, socket: Socket) void {
+ pub fn handleEnd(this: *WebSocket, socket: Socket) void {
std.debug.assert(socket.socket == this.tcp.socket);
this.terminate(ErrorCode.ended);
}
pub fn handleWritable(
- this: *HTTPClient,
+ this: *WebSocket,
socket: Socket,
) void {
std.debug.assert(socket.socket == this.tcp.socket);
- if (this.send_len == 0)
+ const send_buf = this.send_buffer.readableSlice(0);
+ if (send_buf.len == 0)
return;
-
- var send_buf: []const u8 = undefined;
- if (this.send_body_buf) |send_body_buf| {
- send_buf = send_body_buf[this.send_off..][0..this.send_len];
- std.debug.assert(this.send_overflow_buffer.items.len == 0);
- } else {
- send_buf = this.send_overflow_buffer.items[this.send_off..][0..this.send_len];
- }
- std.debug.assert(send_buf.len == this.send_len);
- _ = this.sendBuffer(send_buf, false);
+ _ = this.sendBuffer(send_buf, false, true);
}
pub fn handleTimeout(
- this: *HTTPClient,
+ this: *WebSocket,
_: Socket,
) void {
this.terminate(ErrorCode.timeout);
}
- pub fn handleConnectError(this: *HTTPClient, _: Socket, _: c_int) void {
+ pub fn handleConnectError(this: *WebSocket, _: Socket, _: c_int) void {
this.terminate(ErrorCode.failed_to_connect);
}
+ pub fn hasBackpressure(this: *const WebSocket) bool {
+ return this.send_buffer.count > 0;
+ }
+
pub fn writeBinaryData(
- this: *HTTPClient,
+ this: *WebSocket,
ptr: [*]const u8,
len: usize,
) callconv(.C) void {
@@ -1365,17 +1435,17 @@ pub fn NewWebSocketClient(comptime ssl: bool) type {
const bytes = Copy{ .bytes = slice };
// fast path: small frame, no backpressure, attempt to send without allocating
const frame_size = WebsocketHeader.frameSizeIncludingMask(len);
- if (this.send_len == 0 and frame_size < 128 + 4) {
- var inline_buf: [128 + 4]u8 = undefined;
- bytes.copy(this.globalThis, &inline_buf[0..frame_size], slice.len);
- _ = this.enqueueEncodedBytes(this.socket, inline_buf[0..frame_size]);
+ if (!this.hasBackpressure() and frame_size < stack_frame_size) {
+ var inline_buf: [stack_frame_size]u8 = undefined;
+ bytes.copy(this.globalThis, inline_buf[0..frame_size], slice.len);
+ _ = this.enqueueEncodedBytes(this.tcp, inline_buf[0..frame_size]);
return;
}
- _ = this.sendData(bytes, true, false);
+ _ = this.sendData(bytes, !this.hasBackpressure(), false);
}
pub fn writeString(
- this: *HTTPClient,
+ this: *WebSocket,
str_: *const JSC.ZigString,
) callconv(.C) void {
const str = str_.*;
@@ -1389,25 +1459,25 @@ pub fn NewWebSocketClient(comptime ssl: bool) type {
}
{
- var inline_buf: [128 + 4]u8 = undefined;
+ var inline_buf: [stack_frame_size]u8 = undefined;
// fast path: small frame, no backpressure, attempt to send without allocating
- if (!str.is16Bit() and str.len < 128 + 4) {
+ if (!str.is16Bit() and str.len < stack_frame_size) {
const bytes = Copy{ .latin1 = str.slice() };
const frame_size = WebsocketHeader.frameSizeIncludingMask(str.len);
- if (this.send_len == 0 and frame_size < 128 + 4) {
- bytes.copy(this.globalThis, &inline_buf[0..frame_size], str.len);
- _ = this.enqueueEncodedBytes(this.socket, inline_buf[0..frame_size]);
+ if (!this.hasBackpressure() and frame_size < stack_frame_size) {
+ bytes.copy(this.globalThis, inline_buf[0..frame_size], str.len);
+ _ = this.enqueueEncodedBytes(this.tcp, inline_buf[0..frame_size]);
return;
}
// max length of a utf16 -> utf8 conversion is 4 times the length of the utf16 string
- } else if ((str.len * 4) < (128 + 4) and this.send_len == 0) {
- const bytes = Copy{ .utf16 = str.slice() };
+ } else if ((str.len * 4) < (stack_frame_size) and !this.hasBackpressure()) {
+ const bytes = Copy{ .utf16 = str.utf16SliceAligned() };
var byte_len: usize = 0;
const frame_size = bytes.len(&byte_len);
- std.debug.assert(frame_size <= 128 + 4);
- bytes.copy(this.globalThis, &inline_buf[0..frame_size], byte_len);
- _ = this.enqueueEncodedBytes(this.socket, inline_buf[0..frame_size]);
+ std.debug.assert(frame_size <= stack_frame_size);
+ bytes.copy(this.globalThis, inline_buf[0..frame_size], byte_len);
+ _ = this.enqueueEncodedBytes(this.tcp, inline_buf[0..frame_size]);
return;
}
}
@@ -1417,12 +1487,18 @@ pub fn NewWebSocketClient(comptime ssl: bool) type {
Copy{ .utf16 = str.utf16SliceAligned() }
else
Copy{ .latin1 = str.slice() },
- true,
+ !this.hasBackpressure(),
false,
);
}
- pub fn close(this: *HTTPClient, code: u16, reason: ?*const JSC.ZigString) callconv(.C) void {
+ fn dispatchClose(this: *WebSocket) void {
+ var out = this.outgoing_websocket orelse return;
+ JSC.markBinding();
+ WebSocket__didCloseWithErrorCode(out, ErrorCode.closed);
+ }
+
+ pub fn close(this: *WebSocket, code: u16, reason: ?*const JSC.ZigString) callconv(.C) void {
if (this.tcp.isClosed() or this.tcp.isShutdown())
return;
@@ -1430,8 +1506,8 @@ pub fn NewWebSocketClient(comptime ssl: bool) type {
if (reason) |str| {
inner: {
var fixed_buffer = std.heap.FixedBufferAllocator.init(&close_reason_buf);
- const allocator = fixed_buffer.get();
- const wrote = std.fmt.allocPrint(allocator, "{}", str.*) catch break :inner;
+ const allocator = fixed_buffer.allocator();
+ const wrote = std.fmt.allocPrint(allocator, "{}", .{str.*}) catch break :inner;
this.sendCloseWithBody(this.tcp, code, wrote.ptr[0..125], wrote.len);
return;
}
@@ -1445,20 +1521,31 @@ pub fn NewWebSocketClient(comptime ssl: bool) type {
input_socket: *anyopaque,
socket_ctx: *anyopaque,
globalThis: *JSC.JSGlobalObject,
- ) callconv(.C) *anyopaque {
+ ) callconv(.C) ?*anyopaque {
var tcp = @ptrCast(*uws.Socket, input_socket);
var ctx = @ptrCast(*uws.us_socket_context_t, socket_ctx);
-
- return @ptrCast(
- *anyopaque,
- Socket.adopt(tcp, ctx, HTTPClient{
+ var adopted = Socket.adopt(
+ tcp,
+ ctx,
+ WebSocket,
+ "tcp",
+ WebSocket{
+ .tcp = undefined,
.outgoing_websocket = outgoing,
.globalThis = globalThis,
- }),
+ .receive_overflow_buffer = .{},
+ .send_buffer = bun.LinearFifo(u8, .Dynamic).init(bun.default_allocator),
+ },
+ ) orelse return null;
+ adopted.send_buffer.ensureTotalCapacity(2048) catch return null;
+ _ = globalThis.bunVM().eventLoop().ready_tasks_count.fetchAdd(1, .Monotonic);
+ return @ptrCast(
+ *anyopaque,
+ adopted,
);
}
- pub fn finalize(this: *HTTPClient) callconv(.C) void {
+ pub fn finalize(this: *WebSocket) callconv(.C) void {
this.clearData();
if (this.tcp.isClosed())
@@ -1466,6 +1553,7 @@ pub fn NewWebSocketClient(comptime ssl: bool) type {
this.tcp.close(0, null);
this.outgoing_websocket = null;
+ _ = this.globalThis.bunVM().eventLoop().ready_tasks_count.fetchSub(1, .Monotonic);
}
pub const Export = shim.exportFunctions(.{
diff --git a/src/javascript/jsc/bindings/ScriptExecutionContext.cpp b/src/javascript/jsc/bindings/ScriptExecutionContext.cpp
index 9c6735993..73b8b4c01 100644
--- a/src/javascript/jsc/bindings/ScriptExecutionContext.cpp
+++ b/src/javascript/jsc/bindings/ScriptExecutionContext.cpp
@@ -55,73 +55,25 @@ us_socket_context_t* ScriptExecutionContext::webSocketContextNoSSL()
}
template<bool SSL>
-static uWS::WebSocketContext<SSL, false, WebCore::WebSocket*>* registerWebSocketClientContext(ScriptExecutionContext* script, us_socket_context_t* parent)
+static us_socket_context_t* registerWebSocketClientContext(ScriptExecutionContext* script, us_socket_context_t* parent)
{
- uWS::Loop* loop = uWS::Loop::get();
- uWS::WebSocketContext<SSL, false, WebCore::WebSocket*>* ctx = uWS::WebSocketContext<SSL, false, WebCore::WebSocket*>::createClient(loop, parent);
-
- auto* opts = ctx->getExt();
-
- /* Maximum message size we can receive */
- unsigned int maxPayloadLength = 16 * 1024;
- /* 2 minutes timeout is good */
- unsigned short idleTimeout = 120;
- /* 64kb backpressure is probably good */
- unsigned int maxBackpressure = 64 * 1024;
- bool closeOnBackpressureLimit = false;
- /* This one depends on kernel timeouts and is a bad default */
- bool resetIdleTimeoutOnSend = false;
- /* A good default, esp. for newcomers */
- bool sendPingsAutomatically = false;
- /* Maximum socket lifetime in seconds before forced closure (defaults to disabled) */
- unsigned short maxLifetime = 0;
-
- opts->maxPayloadLength = maxPayloadLength;
- opts->maxBackpressure = maxBackpressure;
- opts->closeOnBackpressureLimit = closeOnBackpressureLimit;
- opts->resetIdleTimeoutOnSend = resetIdleTimeoutOnSend;
- opts->sendPingsAutomatically = sendPingsAutomatically;
- // opts->compression = compression;
- // TODO:
- opts->compression = uWS::CompressOptions::DISABLED;
-
- opts->openHandler = [](uWS::WebSocket<SSL, false, WebCore::WebSocket*>* ws) {
- WebCore::WebSocket* webSocket = *ws->getUserData();
- webSocket->didConnect();
- };
-
- opts->messageHandler = [](uWS::WebSocket<SSL, false, WebCore::WebSocket*>* ws, std::string_view input, uWS::OpCode opCode) {
- WebCore::WebSocket* webSocket = *ws->getUserData();
- if (opCode == uWS::OpCode::BINARY) {
- webSocket->didReceiveBinaryData({ const_cast<unsigned char*>(reinterpret_cast<const unsigned char*>(input.data())), input.length() });
- } else {
- webSocket->didReceiveMessage(WTF::String::fromUTF8(input.data(), input.length()));
- }
- };
-
- // pts->drainHandler = [](uWS::WebSocket<SSL, false, WebCore::WebSocket>* ws, std::string_view input, uWS::OpCode opCode) {
- // WebCore::WebSocket* webSocket = *ws->getUserData();
- // webSocket->didReceiveData(input.data(), input.length());
- // };
-
- opts->closeHandler = [](uWS::WebSocket<SSL, false, WebCore::WebSocket*>* ws, int code, std::string_view message) {
- WebCore::WebSocket* webSocket = *ws->getUserData();
- webSocket->didClose(
- ws->getBufferedAmount(),
- code,
- WTF::String::fromUTF8(
- message.data(),
- message.length()));
- };
-
- return ctx;
+ us_loop_t* loop = (us_loop_t*)uWS::Loop::get();
+ if constexpr (SSL) {
+ us_socket_context_t* child = us_create_child_socket_context(1, parent, sizeof(size_t));
+ Bun__WebSocketClientTLS__register(script->jsGlobalObject(), loop, child);
+ return child;
+ } else {
+ us_socket_context_t* child = us_create_child_socket_context(0, parent, sizeof(size_t));
+ Bun__WebSocketClient__register(script->jsGlobalObject(), loop, child);
+ return child;
+ }
}
-uWS::WebSocketContext<false, false, WebSocket*>* ScriptExecutionContext::connectedWebSocketKindClient()
+us_socket_context_t* ScriptExecutionContext::connectedWebSocketKindClient()
{
return registerWebSocketClientContext<false>(this, webSocketContextNoSSL());
}
-uWS::WebSocketContext<true, false, WebSocket*>* ScriptExecutionContext::connectedWebSocketKindClientSSL()
+us_socket_context_t* ScriptExecutionContext::connectedWebSocketKindClientSSL()
{
return registerWebSocketClientContext<true>(this, webSocketContextSSL());
}
diff --git a/src/javascript/jsc/bindings/ScriptExecutionContext.h b/src/javascript/jsc/bindings/ScriptExecutionContext.h
index ea8302356..e5644c2fc 100644
--- a/src/javascript/jsc/bindings/ScriptExecutionContext.h
+++ b/src/javascript/jsc/bindings/ScriptExecutionContext.h
@@ -118,18 +118,18 @@ private:
us_socket_context_t* webSocketContextSSL();
us_socket_context_t* webSocketContextNoSSL();
- uWS::WebSocketContext<true, false, WebSocket*>* connectedWebSocketKindClientSSL();
- uWS::WebSocketContext<false, false, WebSocket*>* connectedWebSocketKindClient();
+ us_socket_context_t* connectedWebSocketKindClientSSL();
+ us_socket_context_t* connectedWebSocketKindClient();
us_socket_context_t* m_ssl_client_websockets_ctx = nullptr;
us_socket_context_t* m_client_websockets_ctx = nullptr;
- uWS::WebSocketContext<true, false, WebSocket*>* m_connected_ssl_client_websockets_ctx = nullptr;
- uWS::WebSocketContext<false, false, WebSocket*>* m_connected_client_websockets_ctx = nullptr;
+ us_socket_context_t* m_connected_ssl_client_websockets_ctx = nullptr;
+ us_socket_context_t* m_connected_client_websockets_ctx = nullptr;
public:
template<bool isSSL, bool isServer>
- uWS::WebSocketContext<isSSL, isServer, WebSocket*>* connnectedWebSocketContext()
+ us_socket_context_t* connnectedWebSocketContext()
{
if constexpr (isSSL) {
if (!m_connected_ssl_client_websockets_ctx) {
diff --git a/src/javascript/jsc/bindings/headers-cpp.h b/src/javascript/jsc/bindings/headers-cpp.h
index 8d44b538a..ebf00db91 100644
--- a/src/javascript/jsc/bindings/headers-cpp.h
+++ b/src/javascript/jsc/bindings/headers-cpp.h
@@ -1,4 +1,4 @@
-//-- AUTOGENERATED FILE -- 1655526456
+//-- AUTOGENERATED FILE -- 1655637924
// clang-format off
#pragma once
diff --git a/src/javascript/jsc/bindings/headers-handwritten.h b/src/javascript/jsc/bindings/headers-handwritten.h
index 995393b93..3f0bf5327 100644
--- a/src/javascript/jsc/bindings/headers-handwritten.h
+++ b/src/javascript/jsc/bindings/headers-handwritten.h
@@ -189,6 +189,8 @@ typedef struct StringPointer {
typedef void WebSocketHTTPClient;
typedef void WebSocketHTTPSClient;
+typedef void WebSocketClient;
+typedef void WebSocketClientTLS;
#ifdef __cplusplus
diff --git a/src/javascript/jsc/bindings/headers-replacements.zig b/src/javascript/jsc/bindings/headers-replacements.zig
index 80f3dd6dc..712a63454 100644
--- a/src/javascript/jsc/bindings/headers-replacements.zig
+++ b/src/javascript/jsc/bindings/headers-replacements.zig
@@ -68,3 +68,5 @@ pub const ArrayBufferSink = @import("../webcore/streams.zig").ArrayBufferSink;
pub const WebSocketHTTPClient = bindings.WebSocketHTTPClient;
pub const WebSocketHTTPSClient = bindings.WebSocketHTTPSClient;
+pub const WebSocketClient = bindings.WebSocketClient;
+pub const WebSocketClientTLS = bindings.WebSocketClientTLS;
diff --git a/src/javascript/jsc/bindings/headers.h b/src/javascript/jsc/bindings/headers.h
index 0a23cdd2c..3db8ed275 100644
--- a/src/javascript/jsc/bindings/headers.h
+++ b/src/javascript/jsc/bindings/headers.h
@@ -1,5 +1,5 @@
// clang-format: off
-//-- AUTOGENERATED FILE -- 1655526456
+//-- AUTOGENERATED FILE -- 1655637924
#pragma once
#include <stddef.h>
@@ -7,11 +7,11 @@
#include <stdbool.h>
#ifdef __cplusplus
-#define AUTO_EXTERN_C extern "C"
-#define AUTO_EXTERN_C_ZIG extern "C" __attribute__((weak))
+ #define AUTO_EXTERN_C extern "C"
+ #define AUTO_EXTERN_C_ZIG extern "C" __attribute__((weak))
#else
-#define AUTO_EXTERN_C
-#define AUTO_EXTERN_C_ZIG __attribute__((weak))
+ #define AUTO_EXTERN_C
+ #define AUTO_EXTERN_C_ZIG __attribute__((weak))
#endif
#define ZIG_DECL AUTO_EXTERN_C_ZIG
#define CPP_DECL AUTO_EXTERN_C
@@ -26,287 +26,246 @@ typedef void* JSClassRef;
#include "JavaScriptCore/JSClassRef.h"
#endif
#include "headers-handwritten.h"
-typedef struct bJSC__SourceCode {
- unsigned char bytes[24];
-} bJSC__SourceCode;
-typedef char* bJSC__SourceCode_buf;
-typedef struct bWTF__URL {
- unsigned char bytes[40];
-} bWTF__URL;
-typedef char* bWTF__URL_buf;
-typedef struct bJSC__JSModuleRecord {
- unsigned char bytes[216];
-} bJSC__JSModuleRecord;
-typedef char* bJSC__JSModuleRecord_buf;
-typedef struct bJSC__ThrowScope {
- unsigned char bytes[8];
-} bJSC__ThrowScope;
-typedef char* bJSC__ThrowScope_buf;
-typedef struct bJSC__PropertyName {
- unsigned char bytes[8];
-} bJSC__PropertyName;
-typedef char* bJSC__PropertyName_buf;
-typedef struct bJSC__JSFunction {
- unsigned char bytes[32];
-} bJSC__JSFunction;
-typedef char* bJSC__JSFunction_buf;
-typedef struct bJSC__JSGlobalObject {
- unsigned char bytes[2312];
-} bJSC__JSGlobalObject;
-typedef char* bJSC__JSGlobalObject_buf;
-typedef struct bJSC__JSCell {
- unsigned char bytes[8];
-} bJSC__JSCell;
-typedef char* bJSC__JSCell_buf;
-typedef struct bJSC__CatchScope {
- unsigned char bytes[8];
-} bJSC__CatchScope;
-typedef char* bJSC__CatchScope_buf;
-typedef struct bWTF__String {
- unsigned char bytes[8];
-} bWTF__String;
-typedef char* bWTF__String_buf;
-typedef struct bWTF__StringView {
- unsigned char bytes[16];
-} bWTF__StringView;
-typedef char* bWTF__StringView_buf;
-typedef struct bJSC__JSModuleLoader {
- unsigned char bytes[16];
-} bJSC__JSModuleLoader;
-typedef char* bJSC__JSModuleLoader_buf;
-typedef struct bInspector__ScriptArguments {
- unsigned char bytes[32];
-} bInspector__ScriptArguments;
-typedef char* bInspector__ScriptArguments_buf;
-typedef struct bJSC__Exception {
- unsigned char bytes[40];
-} bJSC__Exception;
-typedef char* bJSC__Exception_buf;
-typedef struct bJSC__VM {
- unsigned char bytes[52168];
-} bJSC__VM;
-typedef char* bJSC__VM_buf;
-typedef struct bJSC__JSString {
- unsigned char bytes[16];
-} bJSC__JSString;
-typedef char* bJSC__JSString_buf;
-typedef struct bJSC__SourceOrigin {
- unsigned char bytes[48];
-} bJSC__SourceOrigin;
-typedef char* bJSC__SourceOrigin_buf;
-typedef struct bWTF__ExternalStringImpl {
- unsigned char bytes[40];
-} bWTF__ExternalStringImpl;
-typedef char* bWTF__ExternalStringImpl_buf;
-typedef struct bJSC__JSInternalPromise {
- unsigned char bytes[32];
-} bJSC__JSInternalPromise;
-typedef char* bJSC__JSInternalPromise_buf;
-typedef struct bWTF__StringImpl {
- unsigned char bytes[24];
-} bWTF__StringImpl;
-typedef char* bWTF__StringImpl_buf;
-typedef struct bJSC__JSPromise {
- unsigned char bytes[32];
-} bJSC__JSPromise;
-typedef char* bJSC__JSPromise_buf;
-typedef struct bJSC__JSObject {
- unsigned char bytes[16];
-} bJSC__JSObject;
-typedef char* bJSC__JSObject_buf;
-typedef struct bJSC__Identifier {
- unsigned char bytes[8];
-} bJSC__Identifier;
-typedef char* bJSC__Identifier_buf;
+ typedef struct bJSC__SourceCode { unsigned char bytes[24]; } bJSC__SourceCode;
+ typedef char* bJSC__SourceCode_buf;
+ typedef struct bWTF__URL { unsigned char bytes[40]; } bWTF__URL;
+ typedef char* bWTF__URL_buf;
+ typedef struct bJSC__JSModuleRecord { unsigned char bytes[216]; } bJSC__JSModuleRecord;
+ typedef char* bJSC__JSModuleRecord_buf;
+ typedef struct bJSC__ThrowScope { unsigned char bytes[8]; } bJSC__ThrowScope;
+ typedef char* bJSC__ThrowScope_buf;
+ typedef struct bJSC__PropertyName { unsigned char bytes[8]; } bJSC__PropertyName;
+ typedef char* bJSC__PropertyName_buf;
+ typedef struct bJSC__JSFunction { unsigned char bytes[32]; } bJSC__JSFunction;
+ typedef char* bJSC__JSFunction_buf;
+ typedef struct bJSC__JSGlobalObject { unsigned char bytes[2312]; } bJSC__JSGlobalObject;
+ typedef char* bJSC__JSGlobalObject_buf;
+ typedef struct bJSC__JSCell { unsigned char bytes[8]; } bJSC__JSCell;
+ typedef char* bJSC__JSCell_buf;
+ typedef struct bJSC__CatchScope { unsigned char bytes[8]; } bJSC__CatchScope;
+ typedef char* bJSC__CatchScope_buf;
+ typedef struct bWTF__String { unsigned char bytes[8]; } bWTF__String;
+ typedef char* bWTF__String_buf;
+ typedef struct bWTF__StringView { unsigned char bytes[16]; } bWTF__StringView;
+ typedef char* bWTF__StringView_buf;
+ typedef struct bJSC__JSModuleLoader { unsigned char bytes[16]; } bJSC__JSModuleLoader;
+ typedef char* bJSC__JSModuleLoader_buf;
+ typedef struct bInspector__ScriptArguments { unsigned char bytes[32]; } bInspector__ScriptArguments;
+ typedef char* bInspector__ScriptArguments_buf;
+ typedef struct bJSC__Exception { unsigned char bytes[40]; } bJSC__Exception;
+ typedef char* bJSC__Exception_buf;
+ typedef struct bJSC__VM { unsigned char bytes[52168]; } bJSC__VM;
+ typedef char* bJSC__VM_buf;
+ typedef struct bJSC__JSString { unsigned char bytes[16]; } bJSC__JSString;
+ typedef char* bJSC__JSString_buf;
+ typedef struct bJSC__SourceOrigin { unsigned char bytes[48]; } bJSC__SourceOrigin;
+ typedef char* bJSC__SourceOrigin_buf;
+ typedef struct bWTF__ExternalStringImpl { unsigned char bytes[40]; } bWTF__ExternalStringImpl;
+ typedef char* bWTF__ExternalStringImpl_buf;
+ typedef struct bJSC__JSInternalPromise { unsigned char bytes[32]; } bJSC__JSInternalPromise;
+ typedef char* bJSC__JSInternalPromise_buf;
+ typedef struct bWTF__StringImpl { unsigned char bytes[24]; } bWTF__StringImpl;
+ typedef char* bWTF__StringImpl_buf;
+ typedef struct bJSC__JSPromise { unsigned char bytes[32]; } bJSC__JSPromise;
+ typedef char* bJSC__JSPromise_buf;
+ typedef struct bJSC__JSObject { unsigned char bytes[16]; } bJSC__JSObject;
+ typedef char* bJSC__JSObject_buf;
+ typedef struct bJSC__Identifier { unsigned char bytes[8]; } bJSC__Identifier;
+ typedef char* bJSC__Identifier_buf;
#ifndef __cplusplus
-typedef bJSC__CatchScope JSC__CatchScope; // JSC::CatchScope
-typedef struct JSC__GeneratorPrototype JSC__GeneratorPrototype; // JSC::GeneratorPrototype
-typedef struct JSC__ArrayIteratorPrototype JSC__ArrayIteratorPrototype; // JSC::ArrayIteratorPrototype
-typedef ErrorableResolvedSource ErrorableResolvedSource;
-typedef struct JSC__JSPromisePrototype JSC__JSPromisePrototype; // JSC::JSPromisePrototype
-typedef ErrorableZigString ErrorableZigString;
-typedef bJSC__PropertyName JSC__PropertyName; // JSC::PropertyName
-typedef bJSC__JSObject JSC__JSObject; // JSC::JSObject
-typedef bWTF__ExternalStringImpl WTF__ExternalStringImpl; // WTF::ExternalStringImpl
-typedef struct JSC__AsyncIteratorPrototype JSC__AsyncIteratorPrototype; // JSC::AsyncIteratorPrototype
-typedef bJSC__JSModuleLoader JSC__JSModuleLoader; // JSC::JSModuleLoader
-typedef struct JSC__AsyncGeneratorPrototype JSC__AsyncGeneratorPrototype; // JSC::AsyncGeneratorPrototype
-typedef struct JSC__AsyncGeneratorFunctionPrototype JSC__AsyncGeneratorFunctionPrototype; // JSC::AsyncGeneratorFunctionPrototype
-typedef bJSC__Identifier JSC__Identifier; // JSC::Identifier
-typedef struct JSC__ArrayPrototype JSC__ArrayPrototype; // JSC::ArrayPrototype
-typedef struct Zig__JSMicrotaskCallback Zig__JSMicrotaskCallback; // Zig::JSMicrotaskCallback
-typedef bJSC__JSPromise JSC__JSPromise; // JSC::JSPromise
-typedef WebSocketHTTPClient WebSocketHTTPClient;
-typedef struct JSC__SetIteratorPrototype JSC__SetIteratorPrototype; // JSC::SetIteratorPrototype
-typedef SystemError SystemError;
-typedef bJSC__JSCell JSC__JSCell; // JSC::JSCell
-typedef bJSC__SourceOrigin JSC__SourceOrigin; // JSC::SourceOrigin
-typedef Bun__Writable Bun__Writable;
-typedef bJSC__JSModuleRecord JSC__JSModuleRecord; // JSC::JSModuleRecord
-typedef bWTF__String WTF__String; // WTF::String
-typedef bWTF__URL WTF__URL; // WTF::URL
-typedef struct JSC__IteratorPrototype JSC__IteratorPrototype; // JSC::IteratorPrototype
-typedef bJSC__JSInternalPromise JSC__JSInternalPromise; // JSC::JSInternalPromise
-typedef Bun__Readable Bun__Readable;
-typedef struct JSC__RegExpPrototype JSC__RegExpPrototype; // JSC::RegExpPrototype
-typedef struct JSC__MapIteratorPrototype JSC__MapIteratorPrototype; // JSC::MapIteratorPrototype
-typedef struct WebCore__FetchHeaders WebCore__FetchHeaders; // WebCore::FetchHeaders
-typedef struct JSC__CallFrame JSC__CallFrame; // JSC::CallFrame
-typedef bWTF__StringView WTF__StringView; // WTF::StringView
-typedef bJSC__ThrowScope JSC__ThrowScope; // JSC::ThrowScope
-typedef bWTF__StringImpl WTF__StringImpl; // WTF::StringImpl
-typedef WebSocketHTTPSClient WebSocketHTTPSClient;
-typedef bJSC__VM JSC__VM; // JSC::VM
-typedef JSClassRef JSClassRef;
-typedef Bun__ArrayBuffer Bun__ArrayBuffer;
-typedef bJSC__JSGlobalObject JSC__JSGlobalObject; // JSC::JSGlobalObject
-typedef bJSC__JSFunction JSC__JSFunction; // JSC::JSFunction
-typedef struct JSC__AsyncFunctionPrototype JSC__AsyncFunctionPrototype; // JSC::AsyncFunctionPrototype
-typedef ZigException ZigException;
-typedef bJSC__SourceCode JSC__SourceCode; // JSC::SourceCode
-typedef struct JSC__BigIntPrototype JSC__BigIntPrototype; // JSC::BigIntPrototype
-typedef struct JSC__GeneratorFunctionPrototype JSC__GeneratorFunctionPrototype; // JSC::GeneratorFunctionPrototype
-typedef ZigString ZigString;
-typedef struct WebCore__DOMURL WebCore__DOMURL; // WebCore::DOMURL
-typedef int64_t JSC__JSValue;
-typedef struct JSC__FunctionPrototype JSC__FunctionPrototype; // JSC::FunctionPrototype
-typedef bInspector__ScriptArguments Inspector__ScriptArguments; // Inspector::ScriptArguments
-typedef bJSC__Exception JSC__Exception; // JSC::Exception
-typedef bJSC__JSString JSC__JSString; // JSC::JSString
-typedef struct JSC__ObjectPrototype JSC__ObjectPrototype; // JSC::ObjectPrototype
-typedef struct JSC__StringPrototype JSC__StringPrototype; // JSC::StringPrototype
+ typedef bJSC__CatchScope JSC__CatchScope; // JSC::CatchScope
+ typedef struct JSC__GeneratorPrototype JSC__GeneratorPrototype; // JSC::GeneratorPrototype
+ typedef struct JSC__ArrayIteratorPrototype JSC__ArrayIteratorPrototype; // JSC::ArrayIteratorPrototype
+ typedef ErrorableResolvedSource ErrorableResolvedSource;
+ typedef struct JSC__JSPromisePrototype JSC__JSPromisePrototype; // JSC::JSPromisePrototype
+ typedef ErrorableZigString ErrorableZigString;
+ typedef bJSC__PropertyName JSC__PropertyName; // JSC::PropertyName
+ typedef bJSC__JSObject JSC__JSObject; // JSC::JSObject
+ typedef WebSocketClient WebSocketClient;
+ typedef bWTF__ExternalStringImpl WTF__ExternalStringImpl; // WTF::ExternalStringImpl
+ typedef struct JSC__AsyncIteratorPrototype JSC__AsyncIteratorPrototype; // JSC::AsyncIteratorPrototype
+ typedef bJSC__JSModuleLoader JSC__JSModuleLoader; // JSC::JSModuleLoader
+ typedef struct JSC__AsyncGeneratorPrototype JSC__AsyncGeneratorPrototype; // JSC::AsyncGeneratorPrototype
+ typedef struct JSC__AsyncGeneratorFunctionPrototype JSC__AsyncGeneratorFunctionPrototype; // JSC::AsyncGeneratorFunctionPrototype
+ typedef WebSocketClientTLS WebSocketClientTLS;
+ typedef bJSC__Identifier JSC__Identifier; // JSC::Identifier
+ typedef struct JSC__ArrayPrototype JSC__ArrayPrototype; // JSC::ArrayPrototype
+ typedef struct Zig__JSMicrotaskCallback Zig__JSMicrotaskCallback; // Zig::JSMicrotaskCallback
+ typedef bJSC__JSPromise JSC__JSPromise; // JSC::JSPromise
+ typedef WebSocketHTTPClient WebSocketHTTPClient;
+ typedef struct JSC__SetIteratorPrototype JSC__SetIteratorPrototype; // JSC::SetIteratorPrototype
+ typedef SystemError SystemError;
+ typedef bJSC__JSCell JSC__JSCell; // JSC::JSCell
+ typedef bJSC__SourceOrigin JSC__SourceOrigin; // JSC::SourceOrigin
+ typedef Bun__Writable Bun__Writable;
+ typedef bJSC__JSModuleRecord JSC__JSModuleRecord; // JSC::JSModuleRecord
+ typedef bWTF__String WTF__String; // WTF::String
+ typedef bWTF__URL WTF__URL; // WTF::URL
+ typedef struct JSC__IteratorPrototype JSC__IteratorPrototype; // JSC::IteratorPrototype
+ typedef bJSC__JSInternalPromise JSC__JSInternalPromise; // JSC::JSInternalPromise
+ typedef Bun__Readable Bun__Readable;
+ typedef struct JSC__RegExpPrototype JSC__RegExpPrototype; // JSC::RegExpPrototype
+ typedef struct JSC__MapIteratorPrototype JSC__MapIteratorPrototype; // JSC::MapIteratorPrototype
+ typedef struct WebCore__FetchHeaders WebCore__FetchHeaders; // WebCore::FetchHeaders
+ typedef struct JSC__CallFrame JSC__CallFrame; // JSC::CallFrame
+ typedef bWTF__StringView WTF__StringView; // WTF::StringView
+ typedef bJSC__ThrowScope JSC__ThrowScope; // JSC::ThrowScope
+ typedef bWTF__StringImpl WTF__StringImpl; // WTF::StringImpl
+ typedef WebSocketHTTPSClient WebSocketHTTPSClient;
+ typedef bJSC__VM JSC__VM; // JSC::VM
+ typedef JSClassRef JSClassRef;
+ typedef Bun__ArrayBuffer Bun__ArrayBuffer;
+ typedef bJSC__JSGlobalObject JSC__JSGlobalObject; // JSC::JSGlobalObject
+ typedef bJSC__JSFunction JSC__JSFunction; // JSC::JSFunction
+ typedef struct JSC__AsyncFunctionPrototype JSC__AsyncFunctionPrototype; // JSC::AsyncFunctionPrototype
+ typedef ZigException ZigException;
+ typedef bJSC__SourceCode JSC__SourceCode; // JSC::SourceCode
+ typedef struct JSC__BigIntPrototype JSC__BigIntPrototype; // JSC::BigIntPrototype
+ typedef struct JSC__GeneratorFunctionPrototype JSC__GeneratorFunctionPrototype; // JSC::GeneratorFunctionPrototype
+ typedef ZigString ZigString;
+ typedef struct WebCore__DOMURL WebCore__DOMURL; // WebCore::DOMURL
+ typedef int64_t JSC__JSValue;
+ typedef struct JSC__FunctionPrototype JSC__FunctionPrototype; // JSC::FunctionPrototype
+ typedef bInspector__ScriptArguments Inspector__ScriptArguments; // Inspector::ScriptArguments
+ typedef bJSC__Exception JSC__Exception; // JSC::Exception
+ typedef bJSC__JSString JSC__JSString; // JSC::JSString
+ typedef struct JSC__ObjectPrototype JSC__ObjectPrototype; // JSC::ObjectPrototype
+ typedef struct JSC__StringPrototype JSC__StringPrototype; // JSC::StringPrototype
#endif
#ifdef __cplusplus
-namespace JSC {
-class JSCell;
-class Exception;
-class JSPromisePrototype;
-class StringPrototype;
-class GeneratorFunctionPrototype;
-class ArrayPrototype;
-class JSString;
-class JSObject;
-class AsyncIteratorPrototype;
-class AsyncGeneratorFunctionPrototype;
-class Identifier;
-class JSPromise;
-class RegExpPrototype;
-class AsyncFunctionPrototype;
-class CatchScope;
-class VM;
-class BigIntPrototype;
-class SourceOrigin;
-class ThrowScope;
-class SetIteratorPrototype;
-class AsyncGeneratorPrototype;
-class PropertyName;
-class MapIteratorPrototype;
-class JSModuleRecord;
-class JSInternalPromise;
-class ArrayIteratorPrototype;
-class JSFunction;
-class JSModuleLoader;
-class GeneratorPrototype;
-class JSGlobalObject;
-class SourceCode;
-class FunctionPrototype;
-class IteratorPrototype;
-class CallFrame;
-class ObjectPrototype;
-}
-namespace WTF {
-class URL;
-class StringImpl;
-class String;
-class StringView;
-class ExternalStringImpl;
-}
-namespace Zig {
-class JSMicrotaskCallback;
-}
-namespace WebCore {
-class DOMURL;
-class FetchHeaders;
-}
-namespace Inspector {
-class ScriptArguments;
-}
-
-typedef ErrorableResolvedSource ErrorableResolvedSource;
-typedef ErrorableZigString ErrorableZigString;
-typedef WebSocketHTTPClient WebSocketHTTPClient;
-typedef SystemError SystemError;
-typedef Bun__Writable Bun__Writable;
-typedef Bun__Readable Bun__Readable;
-typedef WebSocketHTTPSClient WebSocketHTTPSClient;
-typedef JSClassRef JSClassRef;
-typedef Bun__ArrayBuffer Bun__ArrayBuffer;
-typedef ZigException ZigException;
-typedef ZigString ZigString;
-typedef int64_t JSC__JSValue;
-using JSC__JSCell = JSC::JSCell;
-using JSC__Exception = JSC::Exception;
-using JSC__JSPromisePrototype = JSC::JSPromisePrototype;
-using JSC__StringPrototype = JSC::StringPrototype;
-using JSC__GeneratorFunctionPrototype = JSC::GeneratorFunctionPrototype;
-using JSC__ArrayPrototype = JSC::ArrayPrototype;
-using JSC__JSString = JSC::JSString;
-using JSC__JSObject = JSC::JSObject;
-using JSC__AsyncIteratorPrototype = JSC::AsyncIteratorPrototype;
-using JSC__AsyncGeneratorFunctionPrototype = JSC::AsyncGeneratorFunctionPrototype;
-using JSC__Identifier = JSC::Identifier;
-using JSC__JSPromise = JSC::JSPromise;
-using JSC__RegExpPrototype = JSC::RegExpPrototype;
-using JSC__AsyncFunctionPrototype = JSC::AsyncFunctionPrototype;
-using JSC__CatchScope = JSC::CatchScope;
-using JSC__VM = JSC::VM;
-using JSC__BigIntPrototype = JSC::BigIntPrototype;
-using JSC__SourceOrigin = JSC::SourceOrigin;
-using JSC__ThrowScope = JSC::ThrowScope;
-using JSC__SetIteratorPrototype = JSC::SetIteratorPrototype;
-using JSC__AsyncGeneratorPrototype = JSC::AsyncGeneratorPrototype;
-using JSC__PropertyName = JSC::PropertyName;
-using JSC__MapIteratorPrototype = JSC::MapIteratorPrototype;
-using JSC__JSModuleRecord = JSC::JSModuleRecord;
-using JSC__JSInternalPromise = JSC::JSInternalPromise;
-using JSC__ArrayIteratorPrototype = JSC::ArrayIteratorPrototype;
-using JSC__JSFunction = JSC::JSFunction;
-using JSC__JSModuleLoader = JSC::JSModuleLoader;
-using JSC__GeneratorPrototype = JSC::GeneratorPrototype;
-using JSC__JSGlobalObject = JSC::JSGlobalObject;
-using JSC__SourceCode = JSC::SourceCode;
-using JSC__FunctionPrototype = JSC::FunctionPrototype;
-using JSC__IteratorPrototype = JSC::IteratorPrototype;
-using JSC__CallFrame = JSC::CallFrame;
-using JSC__ObjectPrototype = JSC::ObjectPrototype;
-using WTF__URL = WTF::URL;
-using WTF__StringImpl = WTF::StringImpl;
-using WTF__String = WTF::String;
-using WTF__StringView = WTF::StringView;
-using WTF__ExternalStringImpl = WTF::ExternalStringImpl;
-using Zig__JSMicrotaskCallback = Zig::JSMicrotaskCallback;
-using WebCore__DOMURL = WebCore::DOMURL;
-using WebCore__FetchHeaders = WebCore::FetchHeaders;
-using Inspector__ScriptArguments = Inspector::ScriptArguments;
+ namespace JSC {
+ class JSCell;
+ class Exception;
+ class JSPromisePrototype;
+ class StringPrototype;
+ class GeneratorFunctionPrototype;
+ class ArrayPrototype;
+ class JSString;
+ class JSObject;
+ class AsyncIteratorPrototype;
+ class AsyncGeneratorFunctionPrototype;
+ class Identifier;
+ class JSPromise;
+ class RegExpPrototype;
+ class AsyncFunctionPrototype;
+ class CatchScope;
+ class VM;
+ class BigIntPrototype;
+ class SourceOrigin;
+ class ThrowScope;
+ class SetIteratorPrototype;
+ class AsyncGeneratorPrototype;
+ class PropertyName;
+ class MapIteratorPrototype;
+ class JSModuleRecord;
+ class JSInternalPromise;
+ class ArrayIteratorPrototype;
+ class JSFunction;
+ class JSModuleLoader;
+ class GeneratorPrototype;
+ class JSGlobalObject;
+ class SourceCode;
+ class FunctionPrototype;
+ class IteratorPrototype;
+ class CallFrame;
+ class ObjectPrototype;
+ }
+ namespace WTF {
+ class URL;
+ class StringImpl;
+ class String;
+ class StringView;
+ class ExternalStringImpl;
+ }
+ namespace Zig {
+ class JSMicrotaskCallback;
+ }
+ namespace WebCore {
+ class DOMURL;
+ class FetchHeaders;
+ }
+ namespace Inspector {
+ class ScriptArguments;
+ }
+
+ typedef ErrorableResolvedSource ErrorableResolvedSource;
+ typedef ErrorableZigString ErrorableZigString;
+ typedef WebSocketClient WebSocketClient;
+ typedef WebSocketClientTLS WebSocketClientTLS;
+ typedef WebSocketHTTPClient WebSocketHTTPClient;
+ typedef SystemError SystemError;
+ typedef Bun__Writable Bun__Writable;
+ typedef Bun__Readable Bun__Readable;
+ typedef WebSocketHTTPSClient WebSocketHTTPSClient;
+ typedef JSClassRef JSClassRef;
+ typedef Bun__ArrayBuffer Bun__ArrayBuffer;
+ typedef ZigException ZigException;
+ typedef ZigString ZigString;
+ typedef int64_t JSC__JSValue;
+ using JSC__JSCell = JSC::JSCell;
+ using JSC__Exception = JSC::Exception;
+ using JSC__JSPromisePrototype = JSC::JSPromisePrototype;
+ using JSC__StringPrototype = JSC::StringPrototype;
+ using JSC__GeneratorFunctionPrototype = JSC::GeneratorFunctionPrototype;
+ using JSC__ArrayPrototype = JSC::ArrayPrototype;
+ using JSC__JSString = JSC::JSString;
+ using JSC__JSObject = JSC::JSObject;
+ using JSC__AsyncIteratorPrototype = JSC::AsyncIteratorPrototype;
+ using JSC__AsyncGeneratorFunctionPrototype = JSC::AsyncGeneratorFunctionPrototype;
+ using JSC__Identifier = JSC::Identifier;
+ using JSC__JSPromise = JSC::JSPromise;
+ using JSC__RegExpPrototype = JSC::RegExpPrototype;
+ using JSC__AsyncFunctionPrototype = JSC::AsyncFunctionPrototype;
+ using JSC__CatchScope = JSC::CatchScope;
+ using JSC__VM = JSC::VM;
+ using JSC__BigIntPrototype = JSC::BigIntPrototype;
+ using JSC__SourceOrigin = JSC::SourceOrigin;
+ using JSC__ThrowScope = JSC::ThrowScope;
+ using JSC__SetIteratorPrototype = JSC::SetIteratorPrototype;
+ using JSC__AsyncGeneratorPrototype = JSC::AsyncGeneratorPrototype;
+ using JSC__PropertyName = JSC::PropertyName;
+ using JSC__MapIteratorPrototype = JSC::MapIteratorPrototype;
+ using JSC__JSModuleRecord = JSC::JSModuleRecord;
+ using JSC__JSInternalPromise = JSC::JSInternalPromise;
+ using JSC__ArrayIteratorPrototype = JSC::ArrayIteratorPrototype;
+ using JSC__JSFunction = JSC::JSFunction;
+ using JSC__JSModuleLoader = JSC::JSModuleLoader;
+ using JSC__GeneratorPrototype = JSC::GeneratorPrototype;
+ using JSC__JSGlobalObject = JSC::JSGlobalObject;
+ using JSC__SourceCode = JSC::SourceCode;
+ using JSC__FunctionPrototype = JSC::FunctionPrototype;
+ using JSC__IteratorPrototype = JSC::IteratorPrototype;
+ using JSC__CallFrame = JSC::CallFrame;
+ using JSC__ObjectPrototype = JSC::ObjectPrototype;
+ using WTF__URL = WTF::URL;
+ using WTF__StringImpl = WTF::StringImpl;
+ using WTF__String = WTF::String;
+ using WTF__StringView = WTF::StringView;
+ using WTF__ExternalStringImpl = WTF::ExternalStringImpl;
+ using Zig__JSMicrotaskCallback = Zig::JSMicrotaskCallback;
+ using WebCore__DOMURL = WebCore::DOMURL;
+ using WebCore__FetchHeaders = WebCore::FetchHeaders;
+ using Inspector__ScriptArguments = Inspector::ScriptArguments;
#endif
+
#pragma mark - JSC::JSObject
-CPP_DECL JSC__JSValue JSC__JSObject__create(JSC__JSGlobalObject* arg0, size_t arg1, void* arg2, void (*ArgFn3)(void* arg0, JSC__JSObject* arg1, JSC__JSGlobalObject* arg2));
+CPP_DECL JSC__JSValue JSC__JSObject__create(JSC__JSGlobalObject* arg0, size_t arg1, void* arg2, void (* ArgFn3)(void* arg0, JSC__JSObject* arg1, JSC__JSGlobalObject* arg2));
CPP_DECL size_t JSC__JSObject__getArrayLength(JSC__JSObject* arg0);
CPP_DECL JSC__JSValue JSC__JSObject__getDirect(JSC__JSObject* arg0, JSC__JSGlobalObject* arg1, const ZigString* arg2);
CPP_DECL JSC__JSValue JSC__JSObject__getIndex(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1, uint32_t arg2);
CPP_DECL void JSC__JSObject__putRecord(JSC__JSObject* arg0, JSC__JSGlobalObject* arg1, ZigString* arg2, ZigString* arg3, size_t arg4);
-CPP_DECL JSC__JSValue ZigString__external(const ZigString* arg0, JSC__JSGlobalObject* arg1, void* arg2, void (*ArgFn3)(void* arg0, void* arg1, size_t arg2));
+CPP_DECL JSC__JSValue ZigString__external(const ZigString* arg0, JSC__JSGlobalObject* arg1, void* arg2, void (* ArgFn3)(void* arg0, void* arg1, size_t arg2));
CPP_DECL JSC__JSValue ZigString__to16BitValue(const ZigString* arg0, JSC__JSGlobalObject* arg1);
CPP_DECL JSC__JSValue ZigString__toErrorInstance(const ZigString* arg0, JSC__JSGlobalObject* arg1);
CPP_DECL JSC__JSValue ZigString__toExternalU16(const uint16_t* arg0, size_t arg1, JSC__JSGlobalObject* arg2);
CPP_DECL JSC__JSValue ZigString__toExternalValue(const ZigString* arg0, JSC__JSGlobalObject* arg1);
-CPP_DECL JSC__JSValue ZigString__toExternalValueWithCallback(const ZigString* arg0, JSC__JSGlobalObject* arg1, void (*ArgFn2)(void* arg0, void* arg1, size_t arg2));
+CPP_DECL JSC__JSValue ZigString__toExternalValueWithCallback(const ZigString* arg0, JSC__JSGlobalObject* arg1, void (* ArgFn2)(void* arg0, void* arg1, size_t arg2));
CPP_DECL JSC__JSValue ZigString__toValue(const ZigString* arg0, JSC__JSGlobalObject* arg1);
CPP_DECL JSC__JSValue ZigString__toValueGC(const ZigString* arg0, JSC__JSGlobalObject* arg1);
CPP_DECL WebCore__DOMURL* WebCore__DOMURL__cast_(JSC__JSValue JSValue0, JSC__VM* arg1);
@@ -503,7 +462,7 @@ CPP_DECL size_t WTF__String__length(WTF__String* arg0);
#pragma mark - JSC::JSValue
-CPP_DECL void JSC__JSValue___then(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1, void* arg2, void (*ArgFn3)(JSC__JSGlobalObject* arg0, void* arg1, void** arg2, size_t arg3), void (*ArgFn4)(JSC__JSGlobalObject* arg0, void* arg1, void** arg2, size_t arg3));
+CPP_DECL void JSC__JSValue___then(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1, void* arg2, void (* ArgFn3)(JSC__JSGlobalObject* arg0, void* arg1, void** arg2, size_t arg3), void (* ArgFn4)(JSC__JSGlobalObject* arg0, void* arg1, void** arg2, size_t arg3));
CPP_DECL bool JSC__JSValue__asArrayBuffer_(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1, Bun__ArrayBuffer* arg2);
CPP_DECL JSC__JSCell* JSC__JSValue__asCell(JSC__JSValue JSValue0);
CPP_DECL JSC__JSInternalPromise* JSC__JSValue__asInternalPromise(JSC__JSValue JSValue0);
@@ -521,7 +480,7 @@ CPP_DECL JSC__JSValue JSC__JSValue__createTypeError(const ZigString* arg0, const
CPP_DECL JSC__JSValue JSC__JSValue__createUninitializedUint8Array(JSC__JSGlobalObject* arg0, size_t arg1);
CPP_DECL bool JSC__JSValue__eqlCell(JSC__JSValue JSValue0, JSC__JSCell* arg1);
CPP_DECL bool JSC__JSValue__eqlValue(JSC__JSValue JSValue0, JSC__JSValue JSValue1);
-CPP_DECL void JSC__JSValue__forEach(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1, void* arg2, void (*ArgFn3)(JSC__VM* arg0, JSC__JSGlobalObject* arg1, void* arg2, JSC__JSValue JSValue3));
+CPP_DECL void JSC__JSValue__forEach(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1, void* arg2, void (* ArgFn3)(JSC__VM* arg0, JSC__JSGlobalObject* arg1, void* arg2, JSC__JSValue JSValue3));
CPP_DECL JSC__JSValue JSC__JSValue__fromEntries(JSC__JSGlobalObject* arg0, ZigString* arg1, ZigString* arg2, size_t arg3, bool arg4);
CPP_DECL JSC__JSValue JSC__JSValue__fromInt64NoTruncate(JSC__JSGlobalObject* arg0, int64_t arg1);
CPP_DECL JSC__JSValue JSC__JSValue__fromUInt64NoTruncate(JSC__JSGlobalObject* arg0, uint64_t arg1);
@@ -607,13 +566,13 @@ CPP_DECL JSC__JSValue JSC__Exception__value(JSC__Exception* arg0);
CPP_DECL void JSC__VM__clearExecutionTimeLimit(JSC__VM* arg0);
CPP_DECL JSC__VM* JSC__VM__create(unsigned char HeapType0);
-CPP_DECL void JSC__VM__deferGC(JSC__VM* arg0, void* arg1, void (*ArgFn2)(void* arg0));
+CPP_DECL void JSC__VM__deferGC(JSC__VM* arg0, void* arg1, void (* ArgFn2)(void* arg0));
CPP_DECL void JSC__VM__deinit(JSC__VM* arg0, JSC__JSGlobalObject* arg1);
CPP_DECL void JSC__VM__deleteAllCode(JSC__VM* arg0, JSC__JSGlobalObject* arg1);
CPP_DECL void JSC__VM__doWork(JSC__VM* arg0);
CPP_DECL void JSC__VM__drainMicrotasks(JSC__VM* arg0);
CPP_DECL bool JSC__VM__executionForbidden(JSC__VM* arg0);
-CPP_DECL void JSC__VM__holdAPILock(JSC__VM* arg0, void* arg1, void (*ArgFn2)(void* arg0));
+CPP_DECL void JSC__VM__holdAPILock(JSC__VM* arg0, void* arg1, void (* ArgFn2)(void* arg0));
CPP_DECL bool JSC__VM__isEntered(JSC__VM* arg0);
CPP_DECL bool JSC__VM__isJITEnabled();
CPP_DECL void JSC__VM__releaseWeakRefs(JSC__VM* arg0);
@@ -623,7 +582,7 @@ CPP_DECL void JSC__VM__setExecutionTimeLimit(JSC__VM* arg0, double arg1);
CPP_DECL void JSC__VM__shrinkFootprint(JSC__VM* arg0);
CPP_DECL void JSC__VM__throwError(JSC__VM* arg0, JSC__JSGlobalObject* arg1, JSC__JSValue JSValue2);
CPP_DECL void JSC__VM__throwError(JSC__VM* arg0, JSC__JSGlobalObject* arg1, JSC__JSValue JSValue2);
-CPP_DECL void JSC__VM__whenIdle(JSC__VM* arg0, void (*ArgFn1)());
+CPP_DECL void JSC__VM__whenIdle(JSC__VM* arg0, void (* ArgFn1)());
#pragma mark - JSC::ThrowScope
@@ -670,7 +629,7 @@ CPP_DECL size_t WTF__StringImpl__length(const WTF__StringImpl* arg0);
CPP_DECL const uint16_t* WTF__ExternalStringImpl__characters16(const WTF__ExternalStringImpl* arg0);
CPP_DECL const unsigned char* WTF__ExternalStringImpl__characters8(const WTF__ExternalStringImpl* arg0);
-CPP_DECL bWTF__ExternalStringImpl WTF__ExternalStringImpl__create(const unsigned char* arg0, size_t arg1, void (*ArgFn2)(void* arg0, unsigned char* arg1, size_t arg2));
+CPP_DECL bWTF__ExternalStringImpl WTF__ExternalStringImpl__create(const unsigned char* arg0, size_t arg1, void (* ArgFn2)(void* arg0, unsigned char* arg1, size_t arg2));
CPP_DECL bool WTF__ExternalStringImpl__is16Bit(const WTF__ExternalStringImpl* arg0);
CPP_DECL bool WTF__ExternalStringImpl__is8Bit(const WTF__ExternalStringImpl* arg0);
CPP_DECL bool WTF__ExternalStringImpl__isEmpty(const WTF__ExternalStringImpl* arg0);
@@ -812,6 +771,28 @@ ZIG_DECL void Bun__WebSocketHTTPSClient__register(JSC__JSGlobalObject* arg0, voi
#ifdef __cplusplus
+ZIG_DECL void Bun__WebSocketClient__close(WebSocketClient* arg0, uint16_t arg1, const ZigString* arg2);
+ZIG_DECL void Bun__WebSocketClient__finalize(WebSocketClient* arg0);
+ZIG_DECL void* Bun__WebSocketClient__init(void* arg0, void* arg1, void* arg2, JSC__JSGlobalObject* arg3);
+ZIG_DECL void Bun__WebSocketClient__register(JSC__JSGlobalObject* arg0, void* arg1, void* arg2);
+ZIG_DECL void Bun__WebSocketClient__writeBinaryData(WebSocketClient* arg0, const unsigned char* arg1, size_t arg2);
+ZIG_DECL void Bun__WebSocketClient__writeString(WebSocketClient* arg0, const ZigString* arg1);
+
+#endif
+
+#ifdef __cplusplus
+
+ZIG_DECL void Bun__WebSocketClientTLS__close(WebSocketClientTLS* arg0, uint16_t arg1, const ZigString* arg2);
+ZIG_DECL void Bun__WebSocketClientTLS__finalize(WebSocketClientTLS* arg0);
+ZIG_DECL void* Bun__WebSocketClientTLS__init(void* arg0, void* arg1, void* arg2, JSC__JSGlobalObject* arg3);
+ZIG_DECL void Bun__WebSocketClientTLS__register(JSC__JSGlobalObject* arg0, void* arg1, void* arg2);
+ZIG_DECL void Bun__WebSocketClientTLS__writeBinaryData(WebSocketClientTLS* arg0, const unsigned char* arg1, size_t arg2);
+ZIG_DECL void Bun__WebSocketClientTLS__writeString(WebSocketClientTLS* arg0, const ZigString* arg1);
+
+#endif
+
+#ifdef __cplusplus
+
ZIG_DECL void Bun__Process__exit(JSC__JSGlobalObject* arg0, int32_t arg1);
ZIG_DECL JSC__JSValue Bun__Process__getArgv(JSC__JSGlobalObject* arg0);
ZIG_DECL JSC__JSValue Bun__Process__getCwd(JSC__JSGlobalObject* arg0);
@@ -824,6 +805,7 @@ CPP_DECL ZigException ZigException__fromException(JSC__Exception* arg0);
#pragma mark - Zig::ConsoleClient
+
#ifdef __cplusplus
ZIG_DECL void Zig__ConsoleClient__count(void* arg0, JSC__JSGlobalObject* arg1, const unsigned char* arg2, size_t arg3);
@@ -844,6 +826,7 @@ ZIG_DECL void Zig__ConsoleClient__timeStamp(void* arg0, JSC__JSGlobalObject* arg
#pragma mark - Bun__Timer
+
#ifdef __cplusplus
ZIG_DECL JSC__JSValue Bun__Timer__clearInterval(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1);
diff --git a/src/javascript/jsc/bindings/headers.zig b/src/javascript/jsc/bindings/headers.zig
index 4b05ed250..306ee44f2 100644
--- a/src/javascript/jsc/bindings/headers.zig
+++ b/src/javascript/jsc/bindings/headers.zig
@@ -68,6 +68,8 @@ pub const ArrayBufferSink = @import("../webcore/streams.zig").ArrayBufferSink;
pub const WebSocketHTTPClient = bindings.WebSocketHTTPClient;
pub const WebSocketHTTPSClient = bindings.WebSocketHTTPSClient;
+pub const WebSocketClient = bindings.WebSocketClient;
+pub const WebSocketClientTLS = bindings.WebSocketClientTLS;
// GENERATED CODE - DO NOT MODIFY BY HAND
pub const ptrdiff_t = c_long;
diff --git a/src/javascript/jsc/bindings/webcore/WebSocket.cpp b/src/javascript/jsc/bindings/webcore/WebSocket.cpp
index 3c9f3a373..7748f2ae8 100644
--- a/src/javascript/jsc/bindings/webcore/WebSocket.cpp
+++ b/src/javascript/jsc/bindings/webcore/WebSocket.cpp
@@ -176,11 +176,11 @@ WebSocket::~WebSocket()
switch (m_connectedWebSocketKind) {
case ConnectedWebSocketKind::Client: {
- this->m_connectedWebSocket.client->end(None);
+ Bun__WebSocketClient__finalize(this->m_connectedWebSocket.client);
break;
}
case ConnectedWebSocketKind::ClientSSL: {
- this->m_connectedWebSocket.clientSSL->end(None);
+ Bun__WebSocketClientTLS__finalize(this->m_connectedWebSocket.clientSSL);
break;
}
case ConnectedWebSocketKind::Server: {
@@ -408,17 +408,17 @@ ExceptionOr<void> WebSocket::send(const String& message)
LOG(Network, "WebSocket %p send() Sending String '%s'", this, message.utf8().data());
if (m_state == CONNECTING)
return Exception { InvalidStateError };
- auto utf8 = message.utf8(StrictConversionReplacingUnpairedSurrogatesWithFFFD);
// No exception is raised if the connection was once established but has subsequently been closed.
if (m_state == CLOSING || m_state == CLOSED) {
+ auto utf8 = message.utf8(StrictConversionReplacingUnpairedSurrogatesWithFFFD);
size_t payloadSize = utf8.length();
m_bufferedAmountAfterClose = saturateAdd(m_bufferedAmountAfterClose, payloadSize);
m_bufferedAmountAfterClose = saturateAdd(m_bufferedAmountAfterClose, getFramingOverhead(payloadSize));
return {};
}
- if (utf8.length() > 0)
- this->sendWebSocketData<false>(utf8.data(), utf8.length());
+ if (message.length() > 0)
+ this->sendWebSocketString(message);
return {};
}
@@ -437,7 +437,7 @@ ExceptionOr<void> WebSocket::send(ArrayBuffer& binaryData)
char* data = static_cast<char*>(binaryData.data());
size_t length = binaryData.byteLength();
if (length > 0)
- this->sendWebSocketData<true>(data, length);
+ this->sendWebSocketData(data, length);
return {};
}
@@ -458,7 +458,7 @@ ExceptionOr<void> WebSocket::send(ArrayBufferView& arrayBufferView)
char* baseAddress = reinterpret_cast<char*>(buffer->data()) + arrayBufferView.byteOffset();
size_t length = arrayBufferView.byteLength();
if (length > 0)
- this->sendWebSocketData<true>(baseAddress, length);
+ this->sendWebSocketData(baseAddress, length);
return {};
}
@@ -480,48 +480,74 @@ ExceptionOr<void> WebSocket::send(ArrayBufferView& arrayBufferView)
// return {};
// }
-template<bool isBinary>
void WebSocket::sendWebSocketData(const char* baseAddress, size_t length)
{
- uWS::OpCode opCode = uWS::OpCode::TEXT;
+ uWS::OpCode opCode = uWS::OpCode::BINARY;
- if constexpr (isBinary)
- opCode = uWS::OpCode::BINARY;
+ switch (m_connectedWebSocketKind) {
+ case ConnectedWebSocketKind::Client: {
+ Bun__WebSocketClient__writeBinaryData(this->m_connectedWebSocket.client, reinterpret_cast<const unsigned char*>(baseAddress), length);
+ // this->m_connectedWebSocket.client->send({ baseAddress, length }, opCode);
+ // this->m_bufferedAmount = this->m_connectedWebSocket.client->getBufferedAmount();
+ break;
+ }
+ case ConnectedWebSocketKind::ClientSSL: {
+ Bun__WebSocketClientTLS__writeBinaryData(this->m_connectedWebSocket.clientSSL, reinterpret_cast<const unsigned char*>(baseAddress), length);
+ break;
+ }
+ case ConnectedWebSocketKind::Server: {
+ this->m_connectedWebSocket.server->send({ baseAddress, length }, opCode);
+ this->m_bufferedAmount = this->m_connectedWebSocket.server->getBufferedAmount();
+ break;
+ }
+ case ConnectedWebSocketKind::ServerSSL: {
+ this->m_connectedWebSocket.serverSSL->send({ baseAddress, length }, opCode);
+ this->m_bufferedAmount = this->m_connectedWebSocket.serverSSL->getBufferedAmount();
+ break;
+ }
+ default: {
+ RELEASE_ASSERT_NOT_REACHED();
+ }
+ }
+}
- this->m_connectedWebSocket.client->cork(
- [&]() {
- switch (m_connectedWebSocketKind) {
- case ConnectedWebSocketKind::Client: {
- this->m_connectedWebSocket.client->send({ baseAddress, length }, opCode);
- this->m_bufferedAmount = this->m_connectedWebSocket.client->getBufferedAmount();
- break;
- }
- case ConnectedWebSocketKind::ClientSSL: {
- this->m_connectedWebSocket.clientSSL->send({ baseAddress, length }, opCode);
- this->m_bufferedAmount = this->m_connectedWebSocket.clientSSL->getBufferedAmount();
- break;
- }
- case ConnectedWebSocketKind::Server: {
- this->m_connectedWebSocket.server->send({ baseAddress, length }, opCode);
- this->m_bufferedAmount = this->m_connectedWebSocket.server->getBufferedAmount();
- break;
- }
- case ConnectedWebSocketKind::ServerSSL: {
- this->m_connectedWebSocket.serverSSL->send({ baseAddress, length }, opCode);
- this->m_bufferedAmount = this->m_connectedWebSocket.serverSSL->getBufferedAmount();
- break;
- }
- default: {
- RELEASE_ASSERT_NOT_REACHED();
- }
- }
- });
+void WebSocket::sendWebSocketString(const String& message)
+{
+
+ switch (m_connectedWebSocketKind) {
+ case ConnectedWebSocketKind::Client: {
+ auto zigStr = Zig::toZigString(message);
+ Bun__WebSocketClient__writeString(this->m_connectedWebSocket.client, &zigStr);
+ // this->m_connectedWebSocket.client->send({ baseAddress, length }, opCode);
+ // this->m_bufferedAmount = this->m_connectedWebSocket.client->getBufferedAmount();
+ break;
+ }
+ case ConnectedWebSocketKind::ClientSSL: {
+ auto zigStr = Zig::toZigString(message);
+ Bun__WebSocketClientTLS__writeString(this->m_connectedWebSocket.clientSSL, &zigStr);
+ break;
+ }
+ case ConnectedWebSocketKind::Server: {
+ auto utf8 = message.utf8(StrictConversionReplacingUnpairedSurrogatesWithFFFD);
+ this->m_connectedWebSocket.server->send({ utf8.data(), utf8.length() }, uWS::OpCode::TEXT);
+ this->m_bufferedAmount = this->m_connectedWebSocket.server->getBufferedAmount();
+ break;
+ }
+ case ConnectedWebSocketKind::ServerSSL: {
+ auto utf8 = message.utf8(StrictConversionReplacingUnpairedSurrogatesWithFFFD);
+ this->m_connectedWebSocket.serverSSL->send({ utf8.data(), utf8.length() }, uWS::OpCode::TEXT);
+ this->m_bufferedAmount = this->m_connectedWebSocket.serverSSL->getBufferedAmount();
+ break;
+ }
+ default: {
+ RELEASE_ASSERT_NOT_REACHED();
+ }
+ }
}
ExceptionOr<void> WebSocket::close(std::optional<unsigned short> optionalCode, const String& reason)
{
- CString utf8 = reason.utf8(StrictConversionReplacingUnpairedSurrogatesWithFFFD);
int code = optionalCode ? optionalCode.value() : static_cast<int>(0);
if (code == 0)
LOG(Network, "WebSocket %p close() without code and reason", this);
@@ -529,7 +555,7 @@ ExceptionOr<void> WebSocket::close(std::optional<unsigned short> optionalCode, c
LOG(Network, "WebSocket %p close() code=%d reason='%s'", this, code, reason.utf8().data());
// if (!(code == WebSocketChannel::CloseEventCodeNormalClosure || (WebSocketChannel::CloseEventCodeMinimumUserDefined <= code && code <= WebSocketChannel::CloseEventCodeMaximumUserDefined)))
// return Exception { InvalidAccessError };
- if (utf8.length() > maxReasonSizeInBytes) {
+ if (reason.length() > maxReasonSizeInBytes) {
// scriptExecutionContext()->addConsoleMessage(MessageSource::JS, MessageLevel::Error, "WebSocket close message is too long."_s);
return Exception { SyntaxError, "WebSocket close message is too long."_s };
}
@@ -553,23 +579,25 @@ ExceptionOr<void> WebSocket::close(std::optional<unsigned short> optionalCode, c
m_state = CLOSING;
switch (m_connectedWebSocketKind) {
case ConnectedWebSocketKind::Client: {
- this->m_connectedWebSocket.client->end(code, { utf8.data(), utf8.length() });
- this->m_bufferedAmount = this->m_connectedWebSocket.client->getBufferedAmount();
+ ZigString reasonZigStr = Zig::toZigString(reason);
+ Bun__WebSocketClient__close(this->m_connectedWebSocket.client, code, &reasonZigStr);
+ // this->m_bufferedAmount = this->m_connectedWebSocket.client->getBufferedAmount();
break;
}
case ConnectedWebSocketKind::ClientSSL: {
- this->m_connectedWebSocket.clientSSL->end(code, { utf8.data(), utf8.length() });
- this->m_bufferedAmount = this->m_connectedWebSocket.clientSSL->getBufferedAmount();
+ ZigString reasonZigStr = Zig::toZigString(reason);
+ Bun__WebSocketClientTLS__close(this->m_connectedWebSocket.clientSSL, code, &reasonZigStr);
+ // this->m_bufferedAmount = this->m_connectedWebSocket.clientSSL->getBufferedAmount();
break;
}
case ConnectedWebSocketKind::Server: {
- this->m_connectedWebSocket.server->end(code, { utf8.data(), utf8.length() });
- this->m_bufferedAmount = this->m_connectedWebSocket.server->getBufferedAmount();
+ // this->m_connectedWebSocket.server->end(code, { utf8.data(), utf8.length() });
+ // this->m_bufferedAmount = this->m_connectedWebSocket.server->getBufferedAmount();
break;
}
case ConnectedWebSocketKind::ServerSSL: {
- this->m_connectedWebSocket.serverSSL->end(code, { utf8.data(), utf8.length() });
- this->m_bufferedAmount = this->m_connectedWebSocket.serverSSL->getBufferedAmount();
+ // this->m_connectedWebSocket.serverSSL->end(code, { utf8.data(), utf8.length() });
+ // this->m_bufferedAmount = this->m_connectedWebSocket.serverSSL->getBufferedAmount();
break;
}
default: {
@@ -832,26 +860,12 @@ void WebSocket::didConnect(us_socket_t* socket, char* bufferedData, size_t buffe
{
this->m_upgradeClient = nullptr;
if (m_isSecure) {
- /* Adopting a socket invalidates it, do not rely on it directly to carry any data */
- uWS::WebSocket<true, false, WebSocket*>* webSocket = (uWS::WebSocket<true, false, WebSocket*>*)us_socket_context_adopt_socket(1,
- (us_socket_context_t*)this->scriptExecutionContext()->connnectedWebSocketContext<true, false>(), socket, sizeof(uWS::WebSocketData) + sizeof(WebSocket*));
-
- webSocket->AsyncSocket<true>::uncork();
-
- webSocket->init(0, uWS::CompressOptions::DISABLED, uWS::BackPressure());
- *webSocket->getUserData() = this;
- this->m_connectedWebSocket.clientSSL = webSocket;
+ us_socket_context_t* ctx = (us_socket_context_t*)this->scriptExecutionContext()->connnectedWebSocketContext<true, false>();
+ this->m_connectedWebSocket.clientSSL = Bun__WebSocketClientTLS__init(this, socket, ctx, this->scriptExecutionContext()->jsGlobalObject());
this->m_connectedWebSocketKind = ConnectedWebSocketKind::ClientSSL;
} else {
- /* Adopting a socket invalidates it, do not rely on it directly to carry any data */
- uWS::WebSocket<false, false, WebSocket*>* webSocket = (uWS::WebSocket<false, false, WebSocket*>*)us_socket_context_adopt_socket(1,
- (us_socket_context_t*)this->scriptExecutionContext()->connnectedWebSocketContext<false, false>(), socket, sizeof(uWS::WebSocketData) + sizeof(WebSocket*));
-
- webSocket->AsyncSocket<false>::uncork();
-
- webSocket->init(0, uWS::CompressOptions::DISABLED, uWS::BackPressure());
- *webSocket->getUserData() = this;
- this->m_connectedWebSocket.client = webSocket;
+ us_socket_context_t* ctx = (us_socket_context_t*)this->scriptExecutionContext()->connnectedWebSocketContext<false, false>();
+ this->m_connectedWebSocket.client = Bun__WebSocketClient__init(this, socket, ctx, this->scriptExecutionContext()->jsGlobalObject());
this->m_connectedWebSocketKind = ConnectedWebSocketKind::Client;
}
@@ -977,6 +991,61 @@ void WebSocket::didFailWithErrorCode(int32_t code)
didReceiveMessageError(message);
break;
}
+
+ // failed_to_allocate_memory
+ case 18: {
+ auto message = MAKE_STATIC_STRING_IMPL("Failed to allocate memory");
+ didReceiveMessageError(message);
+ break;
+ }
+ // control_frame_is_fragmented
+ case 19: {
+ auto message = MAKE_STATIC_STRING_IMPL("Protocol error - control frame is fragmented");
+ didReceiveMessageError(message);
+ break;
+ }
+ // invalid_control_frame
+ case 20: {
+ auto message = MAKE_STATIC_STRING_IMPL("Protocol error - invalid control frame");
+ didReceiveMessageError(message);
+ break;
+ }
+ // compression_unsupported
+ case 21: {
+ auto message = MAKE_STATIC_STRING_IMPL("Compression not implemented yet");
+ didReceiveMessageError(message);
+ break;
+ }
+ // unexpected_mask_from_server
+ case 22: {
+ auto message = MAKE_STATIC_STRING_IMPL("Protocol error - unexpected mask from server");
+ didReceiveMessageError(message);
+ break;
+ }
+ // expected_control_frame
+ case 23: {
+ auto message = MAKE_STATIC_STRING_IMPL("Protocol error - expected control frame");
+ didReceiveMessageError(message);
+ break;
+ }
+ // unsupported_control_frame
+ case 24: {
+ auto message = MAKE_STATIC_STRING_IMPL("Protocol error - unsupported control frame");
+ didReceiveMessageError(message);
+ break;
+ }
+ // unexpected_opcode
+ case 25: {
+ auto message = MAKE_STATIC_STRING_IMPL("Protocol error - unexpected opcode");
+ didReceiveMessageError(message);
+ break;
+ }
+ // invalid_utf8
+ case 26: {
+ auto message = MAKE_STATIC_STRING_IMPL("Server sent invalid UTF8");
+ didReceiveMessageError(message);
+ break;
+ }
}
}
} // namespace WebCore
@@ -985,7 +1054,21 @@ extern "C" void WebSocket__didConnect(WebCore::WebSocket* webSocket, us_socket_t
{
webSocket->didConnect(socket, bufferedData, len);
}
-extern "C" void WebSocket__didFailWithErrorCode(WebCore::WebSocket* webSocket, int32_t errorCode)
+extern "C" void WebSocket__didCloseWithErrorCode(WebCore::WebSocket* webSocket, int32_t errorCode)
{
webSocket->didFailWithErrorCode(errorCode);
+}
+
+extern "C" void WebSocket__didReceiveText(WebCore::WebSocket* webSocket, bool clone, const ZigString* str)
+{
+ WTF::String wtf_str = Zig::toString(*str);
+ if (clone) {
+ wtf_str = wtf_str.isolatedCopy();
+ }
+
+ webSocket->didReceiveMessage(WTFMove(wtf_str));
+}
+extern "C" void WebSocket__didReceiveBytes(WebCore::WebSocket* webSocket, uint8_t* bytes, size_t len)
+{
+ webSocket->didReceiveBinaryData({ bytes, len });
} \ No newline at end of file
diff --git a/src/javascript/jsc/bindings/webcore/WebSocket.h b/src/javascript/jsc/bindings/webcore/WebSocket.h
index 51b885c3b..03c0d7709 100644
--- a/src/javascript/jsc/bindings/webcore/WebSocket.h
+++ b/src/javascript/jsc/bindings/webcore/WebSocket.h
@@ -105,8 +105,8 @@ public:
private:
typedef union AnyWebSocket {
- uWS::WebSocket<false, false, WebCore::WebSocket*>* client;
- uWS::WebSocket<true, false, WebCore::WebSocket*>* clientSSL;
+ WebSocketClient* client;
+ WebSocketClientTLS* clientSSL;
uWS::WebSocket<false, true, WebCore::WebSocket*>* server;
uWS::WebSocket<true, true, WebCore::WebSocket*>* serverSSL;
} AnyWebSocket;
@@ -138,7 +138,7 @@ private:
void didUpdateBufferedAmount(unsigned bufferedAmount);
void didStartClosingHandshake();
- template<bool isBinary>
+ void sendWebSocketString(const String& message);
void sendWebSocketData(const char* data, size_t length);
void failAsynchronously();
diff --git a/src/javascript/jsc/event_loop.zig b/src/javascript/jsc/event_loop.zig
index 9cc6c835a..fbd14c270 100644
--- a/src/javascript/jsc/event_loop.zig
+++ b/src/javascript/jsc/event_loop.zig
@@ -287,7 +287,7 @@ pub const EventLoop = struct {
concurrent_lock: Lock = Lock.init(),
global: *JSGlobalObject = undefined,
virtual_machine: *VirtualMachine = undefined,
- pub const Queue = std.fifo.LinearFifo(Task, .Dynamic);
+ pub const Queue = bun.LinearFifo(Task, .Dynamic);
pub fn tickWithCount(this: *EventLoop) u32 {
var finished: u32 = 0;
diff --git a/src/linear_fifo.zig b/src/linear_fifo.zig
new file mode 100644
index 000000000..8dc633394
--- /dev/null
+++ b/src/linear_fifo.zig
@@ -0,0 +1,536 @@
+// clone of zig stdlib
+// except, this one vectorizes
+
+// FIFO of fixed size items
+// Usually used for e.g. byte buffers
+
+const std = @import("std");
+const math = std.math;
+const mem = std.mem;
+const Allocator = mem.Allocator;
+const debug = std.debug;
+const assert = debug.assert;
+const testing = std.testing;
+const bun = @import("./global.zig");
+
+pub const LinearFifoBufferType = union(enum) {
+ /// The buffer is internal to the fifo; it is of the specified size.
+ Static: usize,
+
+ /// The buffer is passed as a slice to the initialiser.
+ Slice,
+
+ /// The buffer is managed dynamically using a `mem.Allocator`.
+ Dynamic,
+};
+
+pub fn LinearFifo(
+ comptime T: type,
+ comptime buffer_type: LinearFifoBufferType,
+) type {
+ const powers_of_two = switch (buffer_type) {
+ .Static => std.math.isPowerOfTwo(buffer_type.Static),
+ .Slice => false, // Any size slice could be passed in
+ .Dynamic => true, // This could be configurable in future
+ };
+
+ return struct {
+ allocator: if (buffer_type == .Dynamic) Allocator else void,
+ buf: if (buffer_type == .Static) [buffer_type.Static]T else []T,
+ head: usize,
+ count: usize,
+
+ const Self = @This();
+ pub const Reader = std.io.Reader(*Self, error{}, readFn);
+ pub const Writer = std.io.Writer(*Self, error{OutOfMemory}, appendWrite);
+
+ // Type of Self argument for slice operations.
+ // If buffer is inline (Static) then we need to ensure we haven't
+ // returned a slice into a copy on the stack
+ const SliceSelfArg = if (buffer_type == .Static) *Self else Self;
+
+ pub usingnamespace switch (buffer_type) {
+ .Static => struct {
+ pub fn init() Self {
+ return .{
+ .allocator = {},
+ .buf = undefined,
+ .head = 0,
+ .count = 0,
+ };
+ }
+ },
+ .Slice => struct {
+ pub fn init(buf: []T) Self {
+ return .{
+ .allocator = {},
+ .buf = buf,
+ .head = 0,
+ .count = 0,
+ };
+ }
+ },
+ .Dynamic => struct {
+ pub fn init(allocator: Allocator) Self {
+ return .{
+ .allocator = allocator,
+ .buf = &[_]T{},
+ .head = 0,
+ .count = 0,
+ };
+ }
+ },
+ };
+
+ pub fn deinit(self: Self) void {
+ if (buffer_type == .Dynamic) self.allocator.free(self.buf);
+ }
+
+ pub fn realign(self: *Self) void {
+ if (self.buf.len - self.head >= self.count) {
+ // this copy overlaps
+ bun.copy(T, self.buf[0..self.count], self.buf[self.head..][0..self.count]);
+ self.head = 0;
+ } else {
+ var tmp: [mem.page_size / 2 / @sizeOf(T)]T = undefined;
+
+ while (self.head != 0) {
+ const n = math.min(self.head, tmp.len);
+ const m = self.buf.len - n;
+ bun.copy(T, tmp[0..n], self.buf[0..n]);
+ // this middle copy overlaps; the others here don't
+ bun.copy(T, self.buf[0..m], self.buf[n..][0..m]);
+ bun.copy(T, self.buf[m..], tmp[0..n]);
+ self.head -= n;
+ }
+ }
+ { // set unused area to undefined
+ const unused = mem.sliceAsBytes(self.buf[self.count..]);
+ @memset(unused.ptr, undefined, unused.len);
+ }
+ }
+
+ /// Reduce allocated capacity to `size`.
+ pub fn shrink(self: *Self, size: usize) void {
+ assert(size >= self.count);
+ if (buffer_type == .Dynamic) {
+ self.realign();
+ self.buf = self.allocator.realloc(self.buf, size) catch |e| switch (e) {
+ error.OutOfMemory => return, // no problem, capacity is still correct then.
+ };
+ }
+ }
+
+ pub const ensureCapacity = @compileError("deprecated; call `ensureUnusedCapacity` or `ensureTotalCapacity`");
+
+ /// Ensure that the buffer can fit at least `size` items
+ pub fn ensureTotalCapacity(self: *Self, size: usize) !void {
+ if (self.buf.len >= size) return;
+ if (buffer_type == .Dynamic) {
+ const new_size = if (powers_of_two) math.ceilPowerOfTwo(usize, size) catch return error.OutOfMemory else size;
+ var buf = try self.allocator.alloc(T, new_size);
+ var new_bytes = std.mem.sliceAsBytes(buf);
+ var old_bytes = std.mem.sliceAsBytes(self.readableSlice(0));
+ @memcpy(new_bytes.ptr, old_bytes.ptr, old_bytes.len);
+ self.head = 0;
+ self.allocator.free(self.buf);
+ self.buf = buf;
+ } else {
+ return error.OutOfMemory;
+ }
+ }
+
+ /// Makes sure at least `size` items are unused
+ pub fn ensureUnusedCapacity(self: *Self, size: usize) error{OutOfMemory}!void {
+ if (self.writableLength() >= size) return;
+
+ return try self.ensureTotalCapacity(math.add(usize, self.count, size) catch return error.OutOfMemory);
+ }
+
+ /// Returns number of items currently in fifo
+ pub fn readableLength(self: Self) usize {
+ return self.count;
+ }
+
+ /// Returns a writable slice from the 'read' end of the fifo
+ fn readableSliceMut(self: SliceSelfArg, offset: usize) []T {
+ if (offset > self.count) return &[_]T{};
+
+ var start = self.head + offset;
+ if (start >= self.buf.len) {
+ start -= self.buf.len;
+ return self.buf[start .. start + (self.count - offset)];
+ } else {
+ const end = math.min(self.head + self.count, self.buf.len);
+ return self.buf[start..end];
+ }
+ }
+
+ /// Returns a readable slice from `offset`
+ pub fn readableSlice(self: SliceSelfArg, offset: usize) []const T {
+ return self.readableSliceMut(offset);
+ }
+
+ /// Discard first `count` items in the fifo
+ pub fn discard(self: *Self, count: usize) void {
+ assert(count <= self.count);
+
+ if (comptime bun.Environment.allow_assert) {
+ // set old range to undefined. Note: may be wrapped around
+ const slice = self.readableSliceMut(0);
+ if (slice.len >= count) {
+ const unused = mem.sliceAsBytes(slice[0..count]);
+ @memset(unused.ptr, undefined, unused.len);
+ } else {
+ const unused = mem.sliceAsBytes(slice[0..]);
+ @memset(unused.ptr, undefined, unused.len);
+ const unused2 = mem.sliceAsBytes(self.readableSliceMut(slice.len)[0 .. count - slice.len]);
+ @memset(unused2.ptr, undefined, unused2.len);
+ }
+ }
+
+ if (self.count == count) {
+ self.head = 0;
+ self.count = 0;
+ } else {
+ var head = self.head + count;
+ if (powers_of_two) {
+ // Note it is safe to do a wrapping subtract as
+ // bitwise & with all 1s is a noop
+ head &= self.buf.len -% 1;
+ } else {
+ head %= self.buf.len;
+ }
+ self.head = head;
+ self.count -= count;
+ }
+ }
+
+ /// Read the next item from the fifo
+ pub fn readItem(self: *Self) ?T {
+ if (self.count == 0) return null;
+
+ const c = self.buf[self.head];
+ self.discard(1);
+ return c;
+ }
+
+ /// Read data from the fifo into `dst`, returns number of items copied.
+ pub fn read(self: *Self, dst: []T) usize {
+ var dst_left = dst;
+
+ while (dst_left.len > 0) {
+ const slice = self.readableSlice(0);
+ if (slice.len == 0) break;
+ const n = math.min(slice.len, dst_left.len);
+ bun.copy(T, dst_left, slice[0..n]);
+ self.discard(n);
+ dst_left = dst_left[n..];
+ }
+
+ return dst.len - dst_left.len;
+ }
+
+ /// Same as `read` except it returns an error union
+ /// The purpose of this function existing is to match `std.io.Reader` API.
+ fn readFn(self: *Self, dest: []u8) error{}!usize {
+ return self.read(dest);
+ }
+
+ pub fn reader(self: *Self) Reader {
+ return .{ .context = self };
+ }
+
+ /// Returns number of items available in fifo
+ pub fn writableLength(self: Self) usize {
+ return self.buf.len - self.count;
+ }
+
+ /// Returns the first section of writable buffer
+ /// Note that this may be of length 0
+ pub fn writableSlice(self: SliceSelfArg, offset: usize) []T {
+ if (offset > self.buf.len) return &[_]T{};
+
+ const tail = self.head + offset + self.count;
+ if (tail < self.buf.len) {
+ return self.buf[tail..];
+ } else {
+ return self.buf[tail - self.buf.len ..][0 .. self.writableLength() - offset];
+ }
+ }
+
+ /// Returns a writable buffer of at least `size` items, allocating memory as needed.
+ /// Use `fifo.update` once you've written data to it.
+ pub fn writableWithSize(self: *Self, size: usize) ![]T {
+ try self.ensureUnusedCapacity(size);
+
+ // try to avoid realigning buffer
+ var slice = self.writableSlice(0);
+
+ if (slice.len < size) {
+ self.realign();
+ slice = self.writableSlice(0);
+ }
+
+ std.debug.assert(slice.len >= size);
+ return slice;
+ }
+
+ /// Update the tail location of the buffer (usually follows use of writable/writableWithSize)
+ pub fn update(self: *Self, count: usize) void {
+ assert(self.count + count <= self.buf.len);
+ self.count += count;
+ }
+
+ /// Appends the data in `src` to the fifo.
+ /// You must have ensured there is enough space.
+ pub fn writeAssumeCapacity(self: *Self, src: []const T) void {
+ assert(self.writableLength() >= src.len);
+
+ var src_left = src;
+ while (src_left.len > 0) {
+ const writable_slice = self.writableSlice(0);
+ assert(writable_slice.len != 0);
+ const n = math.min(writable_slice.len, src_left.len);
+ bun.copy(T, writable_slice, src_left[0..n]);
+ self.update(n);
+ src_left = src_left[n..];
+ }
+ }
+
+ /// Write a single item to the fifo
+ pub fn writeItem(self: *Self, item: T) !void {
+ try self.ensureUnusedCapacity(1);
+ return self.writeItemAssumeCapacity(item);
+ }
+
+ pub fn writeItemAssumeCapacity(self: *Self, item: T) void {
+ var tail = self.head + self.count;
+ if (powers_of_two) {
+ tail &= self.buf.len - 1;
+ } else {
+ tail %= self.buf.len;
+ }
+ self.buf[tail] = item;
+ self.update(1);
+ }
+
+ /// Appends the data in `src` to the fifo.
+ /// Allocates more memory as necessary
+ pub fn write(self: *Self, src: []const T) !void {
+ try self.ensureUnusedCapacity(src.len);
+
+ return self.writeAssumeCapacity(src);
+ }
+
+ /// Same as `write` except it returns the number of bytes written, which is always the same
+ /// as `bytes.len`. The purpose of this function existing is to match `std.io.Writer` API.
+ fn appendWrite(self: *Self, bytes: []const u8) error{OutOfMemory}!usize {
+ try self.write(bytes);
+ return bytes.len;
+ }
+
+ pub fn writer(self: *Self) Writer {
+ return .{ .context = self };
+ }
+
+ /// Make `count` items available before the current read location
+ fn rewind(self: *Self, count: usize) void {
+ assert(self.writableLength() >= count);
+
+ var head = self.head + (self.buf.len - count);
+ if (powers_of_two) {
+ head &= self.buf.len - 1;
+ } else {
+ head %= self.buf.len;
+ }
+ self.head = head;
+ self.count += count;
+ }
+
+ /// Place data back into the read stream
+ pub fn unget(self: *Self, src: []const T) !void {
+ try self.ensureUnusedCapacity(src.len);
+
+ self.rewind(src.len);
+
+ const slice = self.readableSliceMut(0);
+ if (src.len < slice.len) {
+ bun.copy(T, slice, src);
+ } else {
+ bun.copy(T, slice, src[0..slice.len]);
+ const slice2 = self.readableSliceMut(slice.len);
+ bun.copy(T, slice2, src[slice.len..]);
+ }
+ }
+
+ /// Returns the item at `offset`.
+ /// Asserts offset is within bounds.
+ pub fn peekItem(self: Self, offset: usize) T {
+ assert(offset < self.count);
+
+ var index = self.head + offset;
+ if (powers_of_two) {
+ index &= self.buf.len - 1;
+ } else {
+ index %= self.buf.len;
+ }
+ return self.buf[index];
+ }
+
+ /// Pump data from a reader into a writer
+ /// stops when reader returns 0 bytes (EOF)
+ /// Buffer size must be set before calling; a buffer length of 0 is invalid.
+ pub fn pump(self: *Self, src_reader: anytype, dest_writer: anytype) !void {
+ assert(self.buf.len > 0);
+ while (true) {
+ if (self.writableLength() > 0) {
+ const n = try src_reader.read(self.writableSlice(0));
+ if (n == 0) break; // EOF
+ self.update(n);
+ }
+ self.discard(try dest_writer.write(self.readableSlice(0)));
+ }
+ // flush remaining data
+ while (self.readableLength() > 0) {
+ self.discard(try dest_writer.write(self.readableSlice(0)));
+ }
+ }
+ };
+}
+
+test "LinearFifo(u8, .Dynamic) discard(0) from empty buffer should not error on overflow" {
+ var fifo = LinearFifo(u8, .Dynamic).init(testing.allocator);
+ defer fifo.deinit();
+
+ // If overflow is not explicitly allowed this will crash in debug / safe mode
+ fifo.discard(0);
+}
+
+test "LinearFifo(u8, .Dynamic)" {
+ var fifo = LinearFifo(u8, .Dynamic).init(testing.allocator);
+ defer fifo.deinit();
+
+ try fifo.write("HELLO");
+ try testing.expectEqual(@as(usize, 5), fifo.readableLength());
+ try testing.expectEqualSlices(u8, "HELLO", fifo.readableSlice(0));
+
+ {
+ var i: usize = 0;
+ while (i < 5) : (i += 1) {
+ try fifo.write(&[_]u8{fifo.peekItem(i)});
+ }
+ try testing.expectEqual(@as(usize, 10), fifo.readableLength());
+ try testing.expectEqualSlices(u8, "HELLOHELLO", fifo.readableSlice(0));
+ }
+
+ {
+ try testing.expectEqual(@as(u8, 'H'), fifo.readItem().?);
+ try testing.expectEqual(@as(u8, 'E'), fifo.readItem().?);
+ try testing.expectEqual(@as(u8, 'L'), fifo.readItem().?);
+ try testing.expectEqual(@as(u8, 'L'), fifo.readItem().?);
+ try testing.expectEqual(@as(u8, 'O'), fifo.readItem().?);
+ }
+ try testing.expectEqual(@as(usize, 5), fifo.readableLength());
+
+ { // Writes that wrap around
+ try testing.expectEqual(@as(usize, 11), fifo.writableLength());
+ try testing.expectEqual(@as(usize, 6), fifo.writableSlice(0).len);
+ fifo.writeAssumeCapacity("6<chars<11");
+ try testing.expectEqualSlices(u8, "HELLO6<char", fifo.readableSlice(0));
+ try testing.expectEqualSlices(u8, "s<11", fifo.readableSlice(11));
+ try testing.expectEqualSlices(u8, "11", fifo.readableSlice(13));
+ try testing.expectEqualSlices(u8, "", fifo.readableSlice(15));
+ fifo.discard(11);
+ try testing.expectEqualSlices(u8, "s<11", fifo.readableSlice(0));
+ fifo.discard(4);
+ try testing.expectEqual(@as(usize, 0), fifo.readableLength());
+ }
+
+ {
+ const buf = try fifo.writableWithSize(12);
+ try testing.expectEqual(@as(usize, 12), buf.len);
+ var i: u8 = 0;
+ while (i < 10) : (i += 1) {
+ buf[i] = i + 'a';
+ }
+ fifo.update(10);
+ try testing.expectEqualSlices(u8, "abcdefghij", fifo.readableSlice(0));
+ }
+
+ {
+ try fifo.unget("prependedstring");
+ var result: [30]u8 = undefined;
+ try testing.expectEqualSlices(u8, "prependedstringabcdefghij", result[0..fifo.read(&result)]);
+ try fifo.unget("b");
+ try fifo.unget("a");
+ try testing.expectEqualSlices(u8, "ab", result[0..fifo.read(&result)]);
+ }
+
+ fifo.shrink(0);
+
+ {
+ try fifo.writer().print("{s}, {s}!", .{ "Hello", "World" });
+ var result: [30]u8 = undefined;
+ try testing.expectEqualSlices(u8, "Hello, World!", result[0..fifo.read(&result)]);
+ try testing.expectEqual(@as(usize, 0), fifo.readableLength());
+ }
+
+ {
+ try fifo.writer().writeAll("This is a test");
+ var result: [30]u8 = undefined;
+ try testing.expectEqualSlices(u8, "This", (try fifo.reader().readUntilDelimiterOrEof(&result, ' ')).?);
+ try testing.expectEqualSlices(u8, "is", (try fifo.reader().readUntilDelimiterOrEof(&result, ' ')).?);
+ try testing.expectEqualSlices(u8, "a", (try fifo.reader().readUntilDelimiterOrEof(&result, ' ')).?);
+ try testing.expectEqualSlices(u8, "test", (try fifo.reader().readUntilDelimiterOrEof(&result, ' ')).?);
+ }
+
+ {
+ try fifo.ensureTotalCapacity(1);
+ var in_fbs = std.io.fixedBufferStream("pump test");
+ var out_buf: [50]u8 = undefined;
+ var out_fbs = std.io.fixedBufferStream(&out_buf);
+ try fifo.pump(in_fbs.reader(), out_fbs.writer());
+ try testing.expectEqualSlices(u8, in_fbs.buffer, out_fbs.getWritten());
+ }
+}
+
+test "LinearFifo" {
+ inline for ([_]type{ u1, u8, u16, u64 }) |T| {
+ inline for ([_]LinearFifoBufferType{ LinearFifoBufferType{ .Static = 32 }, .Slice, .Dynamic }) |bt| {
+ const FifoType = LinearFifo(T, bt);
+ var buf: if (bt == .Slice) [32]T else void = undefined;
+ var fifo = switch (bt) {
+ .Static => FifoType.init(),
+ .Slice => FifoType.init(buf[0..]),
+ .Dynamic => FifoType.init(testing.allocator),
+ };
+ defer fifo.deinit();
+
+ try fifo.write(&[_]T{ 0, 1, 1, 0, 1 });
+ try testing.expectEqual(@as(usize, 5), fifo.readableLength());
+
+ {
+ try testing.expectEqual(@as(T, 0), fifo.readItem().?);
+ try testing.expectEqual(@as(T, 1), fifo.readItem().?);
+ try testing.expectEqual(@as(T, 1), fifo.readItem().?);
+ try testing.expectEqual(@as(T, 0), fifo.readItem().?);
+ try testing.expectEqual(@as(T, 1), fifo.readItem().?);
+ try testing.expectEqual(@as(usize, 0), fifo.readableLength());
+ }
+
+ {
+ try fifo.writeItem(1);
+ try fifo.writeItem(1);
+ try fifo.writeItem(1);
+ try testing.expectEqual(@as(usize, 3), fifo.readableLength());
+ }
+
+ {
+ var readBuf: [3]T = undefined;
+ const n = fifo.read(&readBuf);
+ try testing.expectEqual(@as(usize, 3), n); // NOTE: It should be the number of items.
+ }
+ }
+ }
+}