aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/deps/picohttp.zig6
-rw-r--r--src/deps/uws.zig218
-rw-r--r--src/http/websocket_http_client.zig451
-rw-r--r--src/javascript/jsc/api/html_rewriter.zig159
-rw-r--r--src/javascript/jsc/bindings/BunJSCModule.cpp77
-rw-r--r--src/javascript/jsc/bindings/ScriptExecutionContext.cpp60
-rw-r--r--src/javascript/jsc/bindings/ScriptExecutionContext.h19
-rw-r--r--src/javascript/jsc/bindings/bindings.zig5
-rw-r--r--src/javascript/jsc/bindings/webcore/JSWebSocket.cpp705
-rw-r--r--src/javascript/jsc/bindings/webcore/JSWebSocket.dep2
-rw-r--r--src/javascript/jsc/bindings/webcore/JSWebSocket.h98
-rw-r--r--src/javascript/jsc/bindings/webcore/WebSocket.cpp894
-rw-r--r--src/javascript/jsc/bindings/webcore/WebSocket.h146
-rw-r--r--src/javascript/jsc/bindings/webcore/WebSocket.idl67
-rw-r--r--src/javascript/jsc/bindings/webcore/WebSocketHandshake.cpp485
-rw-r--r--src/javascript/jsc/bindings/webcore/WebSocketHandshake.h102
-rw-r--r--src/javascript/jsc/bindings/webcore/WebSocketIdentifier.h35
-rw-r--r--src/javascript/jsc/bindings/webcore/WebSocketStream.cpp83
-rw-r--r--src/javascript/jsc/bindings/webcore/WebSocketStream.h111
19 files changed, 3701 insertions, 22 deletions
diff --git a/src/deps/picohttp.zig b/src/deps/picohttp.zig
index d822b5d73..14e4c9f7c 100644
--- a/src/deps/picohttp.zig
+++ b/src/deps/picohttp.zig
@@ -171,6 +171,12 @@ test "pico_http: parse response" {
pub const Headers = struct {
headers: []const Header,
+ pub fn format(self: Headers, comptime _: []const u8, _: fmt.FormatOptions, writer: anytype) !void {
+ for (self.headers) |header| {
+ try fmt.format(writer, "{s}: {s}\r\n", .{ header.name, header.value });
+ }
+ }
+
pub fn parse(buf: []const u8, src: []Header) !Headers {
var num_headers: usize = src.len;
diff --git a/src/deps/uws.zig b/src/deps/uws.zig
index ae0efef79..1fbce62c5 100644
--- a/src/deps/uws.zig
+++ b/src/deps/uws.zig
@@ -8,6 +8,205 @@ pub const u_int64_t = c_ulonglong;
pub const LIBUS_LISTEN_DEFAULT: c_int = 0;
pub const LIBUS_LISTEN_EXCLUSIVE_PORT: c_int = 1;
pub const Socket = opaque {};
+const bun = @import("../global.zig");
+
+pub fn NewSocketHandler(comptime ssl: bool) type {
+ return struct {
+ const ssl_int: c_int = @boolToInt(ssl);
+ socket: *Socket,
+ const ThisSocket = @This();
+
+ pub fn isEstablished(this: ThisSocket) bool {
+ return us_socket_is_established(comptime ssl_int, this.socket);
+ }
+
+ pub fn timeout(this: ThisSocket, seconds: c_uint) void {
+ return us_socket_timeout(comptime ssl_int, this.socket, seconds);
+ }
+ pub fn ext(this: ThisSocket, comptime ContextType: type) ?*ContextType {
+ var ptr = us_socket_ext(
+ comptime ssl_int,
+ this.socket,
+ ) orelse return null;
+
+ return @ptrCast(*ContextType, @alignCast(std.meta.alignment(ContextType), ptr));
+ }
+ pub fn context(this: ThisSocket) *us_socket_context_t {
+ return us_socket_context(
+ comptime ssl_int,
+ this.socket,
+ );
+ }
+ pub fn flush(this: ThisSocket) void {
+ return us_socket_flush(
+ comptime ssl_int,
+ this.socket,
+ );
+ }
+ pub fn write(this: ThisSocket, data: []const u8, msg_more: bool) c_int {
+ return us_socket_write(
+ comptime ssl_int,
+ this.socket,
+ data.ptr,
+ data.len,
+ @as(c_int, @boolToInt(msg_more)),
+ );
+ }
+ pub fn shutdown(this: ThisSocket) void {
+ return us_socket_shutdown(
+ comptime ssl_int,
+ this.socket,
+ );
+ }
+ pub fn shutdownRead(this: ThisSocket) void {
+ return us_socket_shutdown_read(
+ comptime ssl_int,
+ this.socket,
+ );
+ }
+ pub fn isShutdown(this: ThisSocket) c_int {
+ return us_socket_is_shut_down(
+ comptime ssl_int,
+ this.socket,
+ );
+ }
+ pub fn isClosed(this: ThisSocket) c_int {
+ return us_socket_is_closed(
+ comptime ssl_int,
+ this.socket,
+ );
+ }
+ pub fn close(this: ThisSocket, code: c_int, reason: ?*anyopaque) void {
+ return us_socket_close(
+ comptime ssl_int,
+ this.socket,
+ code,
+ reason,
+ );
+ }
+ pub fn localPort(this: ThisSocket) c_int {
+ return us_socket_local_port(
+ comptime ssl_int,
+ this.socket,
+ );
+ }
+ pub fn remoteAddress(this: ThisSocket, buf: [*]u8, length: [*c]c_int) void {
+ return us_socket_remote_address(
+ comptime ssl_int,
+ this.socket,
+ buf,
+ length,
+ );
+ }
+
+ pub fn connect(
+ host: []const u8,
+ port: c_int,
+ socket_ctx: *us_socket_context_t,
+ comptime Context: type,
+ ctx: Context,
+ comptime socket_field_name: []const u8,
+ ) ?*Context {
+ var stack_fallback = std.heap.stackFallback(1024, bun.default_allocator);
+ var allocator = stack_fallback.get();
+ var host_ = allocator.dupeZ(u8, host) orelse return null;
+ defer allocator.free(host_);
+
+ var socket = us_socket_context_connect(comptime ssl_int, socket_ctx, host_, port, null, 0, @sizeOf(Context)) orelse return null;
+ const socket_ = ThisSocket{ .socket = socket };
+ var holder = socket_.ext(Context) orelse {
+ if (comptime bun.Environment.allow_assert) unreachable;
+ _ = us_socket_close_connecting(comptime ssl_int, socket);
+ return null;
+ };
+ holder.* = ctx;
+ @field(holder, socket_field_name) = socket_;
+ return holder;
+ }
+
+ pub fn configure(
+ ctx: *us_socket_context_t,
+ comptime ContextType: type,
+ comptime onOpen: anytype,
+ comptime onClose: anytype,
+ comptime onData: anytype,
+ comptime onWritable: anytype,
+ comptime onTimeout: anytype,
+ comptime onConnectError: anytype,
+ comptime onEnd: anytype,
+ ) void {
+ const SocketHandler = struct {
+ pub fn on_open(socket: *Socket, _: c_int, ip: [*:0]u8, port: c_int) callconv(.C) ?*Socket {
+ onOpen(
+ @ptrCast(*ContextType, @alignCast(std.meta.alignment(ContextType), us_socket_ext(comptime ssl_int, socket).?)),
+ socket,
+ bun.span(ip),
+ port,
+ );
+ return socket;
+ }
+ pub fn on_close(socket: *Socket, code: c_int, reason: ?*anyopaque) callconv(.C) ?*Socket {
+ onClose(
+ @ptrCast(*ContextType, @alignCast(std.meta.alignment(ContextType), us_socket_ext(comptime ssl_int, socket).?)),
+ socket,
+ code,
+ reason,
+ );
+ return socket;
+ }
+ pub fn on_data(socket: *Socket, buf: ?[*]u8, len: c_int) callconv(.C) ?*Socket {
+ onData(
+ @ptrCast(*ContextType, @alignCast(std.meta.alignment(ContextType), us_socket_ext(comptime ssl_int, socket).?)),
+ socket,
+ buf.?[0..len],
+ );
+ return socket;
+ }
+ pub fn on_writable(socket: *Socket) callconv(.C) ?*Socket {
+ onWritable(
+ @ptrCast(*ContextType, @alignCast(std.meta.alignment(ContextType), us_socket_ext(comptime ssl_int, socket).?)),
+ socket,
+ );
+ return socket;
+ }
+ pub fn on_timeout(socket: *Socket) callconv(.C) ?*Socket {
+ onTimeout(
+ @ptrCast(*ContextType, @alignCast(std.meta.alignment(ContextType), us_socket_ext(comptime ssl_int, socket).?)),
+ socket,
+ );
+ return socket;
+ }
+ pub fn on_connect_error(socket: *Socket, code: c_int) callconv(.C) ?*Socket {
+ onConnectError(
+ @ptrCast(*ContextType, @alignCast(std.meta.alignment(ContextType), us_socket_ext(comptime ssl_int, socket).?)),
+ socket,
+ code,
+ );
+ return socket;
+ }
+ pub fn on_end(socket: *Socket) callconv(.C) ?*Socket {
+ onEnd(
+ @ptrCast(*ContextType, @alignCast(std.meta.alignment(ContextType), us_socket_ext(comptime ssl_int, socket).?)),
+ socket,
+ );
+ return socket;
+ }
+ };
+
+ us_socket_context_on_open(ssl_int, ctx, SocketHandler.on_open);
+ us_socket_context_on_close(ssl_int, ctx, SocketHandler.on_close);
+ us_socket_context_on_data(ssl_int, ctx, SocketHandler.on_data);
+ us_socket_context_on_writable(ssl_int, ctx, SocketHandler.on_writable);
+ us_socket_context_on_timeout(ssl_int, ctx, SocketHandler.on_timeout);
+ us_socket_context_on_connect_error(ssl_int, ctx, SocketHandler.on_connect_error);
+ us_socket_context_on_end(ssl_int, ctx, SocketHandler.on_end);
+ }
+ };
+}
+
+pub const SocketTCP = NewSocketHandler(false);
+pub const SocketTLS = NewSocketHandler(true);
+
pub const us_timer_t = opaque {};
pub const us_socket_context_t = opaque {};
pub const Loop = opaque {
@@ -90,13 +289,13 @@ extern fn us_socket_context_on_server_name(ssl: c_int, context: ?*us_socket_cont
extern fn us_socket_context_get_native_handle(ssl: c_int, context: ?*us_socket_context_t) ?*anyopaque;
extern fn us_create_socket_context(ssl: c_int, loop: ?*Loop, ext_size: c_int, options: us_socket_context_options_t) ?*us_socket_context_t;
extern fn us_socket_context_free(ssl: c_int, context: ?*us_socket_context_t) void;
-extern fn us_socket_context_on_open(ssl: c_int, context: ?*us_socket_context_t, on_open: ?fn (?*Socket, c_int, [*c]u8, c_int) callconv(.C) ?*Socket) void;
-extern fn us_socket_context_on_close(ssl: c_int, context: ?*us_socket_context_t, on_close: ?fn (?*Socket, c_int, ?*anyopaque) callconv(.C) ?*Socket) void;
-extern fn us_socket_context_on_data(ssl: c_int, context: ?*us_socket_context_t, on_data: ?fn (?*Socket, [*c]u8, c_int) callconv(.C) ?*Socket) void;
-extern fn us_socket_context_on_writable(ssl: c_int, context: ?*us_socket_context_t, on_writable: ?fn (*Socket) callconv(.C) ?*Socket) void;
-extern fn us_socket_context_on_timeout(ssl: c_int, context: ?*us_socket_context_t, on_timeout: ?fn (?*Socket) callconv(.C) ?*Socket) void;
-extern fn us_socket_context_on_connect_error(ssl: c_int, context: ?*us_socket_context_t, on_connect_error: ?fn (?*Socket, c_int) callconv(.C) ?*Socket) void;
-extern fn us_socket_context_on_end(ssl: c_int, context: ?*us_socket_context_t, on_end: ?fn (?*Socket) callconv(.C) ?*Socket) void;
+extern fn us_socket_context_on_open(ssl: c_int, context: ?*us_socket_context_t, on_open: fn (?*Socket, c_int, [*c]u8, c_int) callconv(.C) ?*Socket) void;
+extern fn us_socket_context_on_close(ssl: c_int, context: ?*us_socket_context_t, on_close: fn (?*Socket, c_int, ?*anyopaque) callconv(.C) ?*Socket) void;
+extern fn us_socket_context_on_data(ssl: c_int, context: ?*us_socket_context_t, on_data: fn (?*Socket, [*c]u8, c_int) callconv(.C) ?*Socket) void;
+extern fn us_socket_context_on_writable(ssl: c_int, context: ?*us_socket_context_t, on_writable: fn (*Socket) callconv(.C) ?*Socket) void;
+extern fn us_socket_context_on_timeout(ssl: c_int, context: ?*us_socket_context_t, on_timeout: fn (?*Socket) callconv(.C) ?*Socket) void;
+extern fn us_socket_context_on_connect_error(ssl: c_int, context: ?*us_socket_context_t, on_connect_error: fn (?*Socket, c_int) callconv(.C) ?*Socket) void;
+extern fn us_socket_context_on_end(ssl: c_int, context: ?*us_socket_context_t, on_end: fn (?*Socket) callconv(.C) ?*Socket) void;
extern fn us_socket_context_ext(ssl: c_int, context: ?*us_socket_context_t) ?*anyopaque;
extern fn us_socket_context_listen(ssl: c_int, context: ?*us_socket_context_t, host: [*c]const u8, port: c_int, options: c_int, socket_ext_size: c_int) ?*listen_socket_t;
@@ -183,11 +382,12 @@ pub const Poll = opaque {
};
extern fn us_socket_get_native_handle(ssl: c_int, s: ?*Socket) ?*anyopaque;
-extern fn us_socket_write(ssl: c_int, s: ?*Socket, data: [*c]const u8, length: c_int, msg_more: c_int) c_int;
-extern fn Socketimeout(ssl: c_int, s: ?*Socket, seconds: c_uint) void;
+
+extern fn us_socket_timeout(ssl: c_int, s: ?*Socket, seconds: c_uint) void;
extern fn us_socket_ext(ssl: c_int, s: ?*Socket) ?*anyopaque;
extern fn us_socket_context(ssl: c_int, s: ?*Socket) ?*us_socket_context_t;
extern fn us_socket_flush(ssl: c_int, s: ?*Socket) void;
+extern fn us_socket_write(ssl: c_int, s: ?*Socket, data: [*c]const u8, length: c_int, msg_more: c_int) c_int;
extern fn us_socket_shutdown(ssl: c_int, s: ?*Socket) void;
extern fn us_socket_shutdown_read(ssl: c_int, s: ?*Socket) void;
extern fn us_socket_is_shut_down(ssl: c_int, s: ?*Socket) c_int;
diff --git a/src/http/websocket_http_client.zig b/src/http/websocket_http_client.zig
new file mode 100644
index 000000000..5e795bb4a
--- /dev/null
+++ b/src/http/websocket_http_client.zig
@@ -0,0 +1,451 @@
+// This code is based on https://github.com/frmdstryr/zhp/blob/a4b5700c289c3619647206144e10fb414113a888/src/websocket.zig
+// Thank you @frmdstryr.
+const std = @import("std");
+const native_endian = @import("builtin").target.cpu.arch.endian();
+
+const tcp = std.x.net.tcp;
+const ip = std.x.net.ip;
+
+const IPv4 = std.x.os.IPv4;
+const IPv6 = std.x.os.IPv6;
+const os = std.os;
+const bun = @import("../global.zig");
+const string = bun.string;
+const Output = bun.Output;
+const Global = bun.Global;
+const Environment = bun.Environment;
+const strings = bun.strings;
+const MutableString = bun.MutableString;
+const stringZ = bun.stringZ;
+const default_allocator = bun.default_allocator;
+const C = bun.C;
+
+const uws = @import("uws");
+const JSC = @import("javascript_core");
+const PicoHTTP = @import("picohttp");
+const ObjectPool = @import("../pool.zig").ObjectPool;
+
+fn buildRequestBody(vm: *JSC.VirtualMachine, pathname: *const JSC.ZigString, host: *const JSC.ZigString, client_protocol: *const JSC.ZigString, client_protocol_hash: *u64) ![]u8 {
+ const allocator = vm.allocator();
+ var input_rand_buf: [16]u8 = undefined;
+ std.crypto.random.bytes(&input_rand_buf);
+ const temp_buf_size = comptime std.base64.standard.Encoder.calcSize(16);
+ var encoded_buf: [temp_buf_size]u8 = undefined;
+ const accept_key = std.base64.standard.Encoder.encode(&encoded_buf, &input_rand_buf);
+
+ var headers = [_]PicoHTTP.Header{
+ .{
+ .name = "Sec-WebSocket-Key",
+ .value = accept_key,
+ },
+ .{
+ .name = "Sec-WebSocket-Protocol",
+ .value = client_protocol.slice(),
+ },
+ };
+
+ if (client_protocol.len > 0)
+ client_protocol_hash.* = std.hash.Wyhash.hash(0, headers[1].value);
+
+ var headers_: []PicoHTTP.Header = headers[0 .. 1 + @as(usize, @boolToInt(client_protocol.len > 0))];
+
+ return try std.fmt.allocPrint(allocator,
+ \\GET {} HTTP/1.1\r
+ \\Host: {}\r
+ \\Pragma: no-cache\r
+ \\Cache-Control: no-cache\r
+ \\Connection: Upgrade\r
+ \\Upgrade: websocket\r
+ \\Sec-WebSocket-Version: 13\r
+ \\{s}
+ \\\r
+ \\
+ , .{
+ pathname.*,
+ host.*,
+ PicoHTTP.Headers{ .headers = headers_ },
+ });
+}
+
+const ErrorCode = enum(i32) {
+ cancel,
+ invalid_response,
+ expected_101_status_code,
+ missing_upgrade_header,
+ missing_connection_header,
+ missing_websocket_accept_header,
+ invalid_upgrade_header,
+ invalid_connection_header,
+ invalid_websocket_version,
+ mismatch_websocket_accept_header,
+ missing_client_protocol,
+ mismatch_client_protocol,
+ timeout,
+ closed,
+ failed_to_write,
+ failed_to_connect,
+ headers_too_large,
+ ended,
+};
+extern fn WebSocket__didConnect(
+ websocket_context: *anyopaque,
+ socket: *uws.Socket,
+ buffered_data: ?[*]u8,
+ buffered_len: usize,
+) void;
+extern fn WebSocket__didFailToConnect(websocket_context: *anyopaque, reason: ErrorCode) void;
+
+const BodyBufBytes = [16384 - 16]u8;
+
+const BodyBufPool = ObjectPool(BodyBufBytes, null, true, 4);
+const BodyBuf = BodyBufPool.Node;
+
+pub fn NewHTTPUpgradeClient(comptime ssl: bool) type {
+ return struct {
+ pub const Socket = uws.NewSocketHandler(ssl);
+ socket: Socket,
+ outgoing_websocket: *anyopaque,
+ input_body_buf: []u8 = &[_]u8{},
+ client_protocol: []const u8 = "",
+ to_send: []const u8 = "",
+ read_length: usize = 0,
+ headers_buf: [128]PicoHTTP.Header = undefined,
+ body_buf: ?*BodyBuf = null,
+ body_written: usize = 0,
+ websocket_protocol: u64 = 0,
+
+ const basename = (if (ssl) "SecureWebSocket" else "WebSocket") ++ "UpgradeClient";
+
+ pub const shim = JSC.Shimmer("Bun", basename, @This());
+
+ const HTTPClient = @This();
+
+ pub fn register(global: *JSC.JSGlobalObject, loop_: *anyopaque, ctx_: *anyopaque) 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_);
+
+ if (vm.uws_event_loop) |other| {
+ std.debug.assert(other == loop);
+ }
+
+ vm.uws_event_loop = loop;
+
+ Socket.configure(ctx, HTTPClient, handleOpen, handleClose, handleData, handleWritable, handleTimeout, handleConnectError, handleEnd);
+ }
+
+ pub fn connect(
+ global: *JSC.JSGlobalObject,
+ socket_ctx: *anyopaque,
+ websocket: *anyopaque,
+ host: *const JSC.ZigString,
+ port: u16,
+ pathname: *const JSC.ZigString,
+ client_protocol: *const JSC.ZigString,
+ outgoing_usocket: **anyopaque,
+ ) ?*HTTPClient {
+ std.debug.assert(global.bunVM().uws_event_loop != null);
+
+ 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,
+ .outgoing_websocket = websocket,
+ .input_body_buf = body,
+ .websocket_protocol = client_protocol_hash,
+ };
+ var host_ = host.toSlice(bun.default_allocator);
+ defer host_.deinit();
+
+ if (Socket.connect(host_.slice(), port, @ptrCast(*uws.us_socket_context_t, socket_ctx), HTTPClient, client, "socket")) |out| {
+ outgoing_usocket.* = out.socket.socket;
+ out.socket.timeout(120);
+ return out;
+ }
+
+ client.clearData();
+
+ return null;
+ }
+
+ pub fn clearInput(this: *HTTPClient) void {
+ if (this.input_body_buf.len > 0) bun.default_allocator.free(this.input_body_buf);
+ this.input_body_buf.len = 0;
+ }
+ pub fn clearData(this: *HTTPClient) void {
+ this.clearInput();
+ if (this.body_buf) |buf| {
+ this.body_buf = null;
+ buf.release();
+ }
+ }
+ pub fn cancel(this: *HTTPClient) void {
+ this.clearData();
+
+ if (!this.socket.isEstablished()) {
+ _ = uws.us_socket_close_connecting(comptime @as(c_int, @boolToInt(ssl)), this.socket);
+ } else {
+ this.socket.close(0, null);
+ }
+ }
+
+ pub fn fail(this: *HTTPClient, code: ErrorCode) void {
+ WebSocket__didFailToConnect(this.outgoing_websocket, code);
+ this.cancel();
+ }
+
+ pub fn handleClose(this: *HTTPClient, _: Socket, _: c_int, _: ?*anyopaque) void {
+ this.clearData();
+ WebSocket__didFailToConnect(this.outgoing_websocket, ErrorCode.closed);
+ }
+
+ pub fn terminate(this: *HTTPClient, code: ErrorCode) void {
+ this.fail(code);
+ if (this.socket.isClosed() == 0)
+ this.socket.close(0, null);
+ }
+
+ pub fn handleOpen(this: *HTTPClient, socket: Socket) void {
+ std.debug.assert(socket.socket == this.socket.socket);
+
+ std.debug.assert(this.input_body_buf.len > 0);
+ std.debug.assert(this.to_send.len == 0);
+
+ const wrote = socket.write(this.input_body_buf, false);
+ if (wrote < 0) {
+ this.terminate(ErrorCode.failed_to_write);
+ return;
+ }
+
+ this.to_send = this.input_body_buf[@intCast(usize, wrote)..];
+ }
+
+ fn getBody(this: *HTTPClient) *BodyBufBytes {
+ if (this.body_buf == null) {
+ this.body_buf = BodyBufPool.get(bun.default_allocator);
+ }
+
+ return &this.body_buf.?.data;
+ }
+
+ pub fn handleData(this: *HTTPClient, socket: Socket, data: []const u8) void {
+ std.debug.assert(socket.socket == this.socket.socket);
+
+ if (comptime Environment.allow_assert)
+ std.debug.assert(socket.isShutdown() == 0);
+
+ var body = this.getBody();
+ var remain = body[this.body_written..];
+ const is_first = this.body_written == 0;
+ if (is_first and data.len >= "101 ".len) {
+ // fail early if we receive a non-101 status code
+ if (!strings.eqlComptimeIgnoreLen(data[0.."101 ".len], "101 ")) {
+ this.terminate(ErrorCode.expected_101_status_code);
+ return;
+ }
+ }
+
+ const to_write = remain[0..@minimum(remain.len, data.len)];
+ if (data.len > 0 and to_write.len > 0) {
+ @memcpy(remain.ptr, data, to_write.len);
+ this.body_written += to_write.len;
+ }
+
+ const overflow = data[to_write.len..];
+
+ const available_to_read = body[0..this.body_written];
+ const response = PicoHTTP.Response.parse(available_to_read, &this.headers_buf) catch |err| {
+ switch (err) {
+ error.Malformed_HTTP_Response => {
+ this.terminate(ErrorCode.invalid_response);
+ return;
+ },
+ error.ShortRead => {
+ if (overflow.len > 0) {
+ this.terminate(ErrorCode.headers_too_large);
+ return;
+ }
+ return;
+ },
+ }
+ };
+
+ var buffered_body_data = body[@minimum(@intCast(usize, response.bytes_read), body.len)..];
+ buffered_body_data = buffered_body_data[0..@minimum(buffered_body_data.len, this.body_written)];
+
+ this.processResponse(response, buffered_body_data, overflow);
+ }
+
+ pub fn handleEnd(this: *HTTPClient, socket: Socket) void {
+ std.debug.assert(socket.socket == this.socket.socket);
+ this.terminate(ErrorCode.ended);
+ }
+
+ pub fn processResponse(this: *HTTPClient, response: PicoHTTP.Response, remain_buf: []const u8, overflow_buf: []const u8) void {
+ std.debug.assert(this.body_written > 0);
+
+ var upgrade_header = PicoHTTP.Header{ .name = "", .value = "" };
+ var connection_header = PicoHTTP.Header{ .name = "", .value = "" };
+ var websocket_accept_header = PicoHTTP.Header{ .name = "", .value = "" };
+ var visited_protocol = this.websocket_protocol == 0;
+ var visited_version = false;
+
+ if (remain_buf.len > 0) {
+ std.debug.assert(overflow_buf.len == 0);
+ }
+
+ for (response.headers) |header| {
+ switch (header.name.len) {
+ "Connection".len => {
+ if (connection_header.name.len == 0 and strings.eqlCaseInsensitiveASCII(header.name, "Connection", false)) {
+ connection_header = header;
+ if (visited_protocol and upgrade_header.len > 0 and connection_header.len > 0 and websocket_accept_header.len > 0 and visited_version) {
+ break;
+ }
+ }
+ },
+ "Upgrade".len => {
+ if (upgrade_header.name.len == 0 and strings.eqlCaseInsensitiveASCII(header.name, "Upgrade", false)) {
+ upgrade_header = header;
+ if (visited_protocol and upgrade_header.len > 0 and connection_header.len > 0 and websocket_accept_header.len > 0 and visited_version) {
+ break;
+ }
+ }
+ },
+ "Sec-WebSocket-Version".len => {
+ if (!visited_version and strings.eqlCaseInsensitiveASCII(header.name, "Sec-WebSocket-Version", false)) {
+ visited_version = true;
+ if (!strings.eqlComptime(header.value, "13", false)) {
+ this.terminate(ErrorCode.invalid_websocket_version);
+ return;
+ }
+ }
+ },
+ "Sec-WebSocket-Accept".len => {
+ if (websocket_accept_header.name.len == 0 and strings.eqlCaseInsensitiveASCII(header.name, "Sec-WebSocket-Accept", false)) {
+ websocket_accept_header = header;
+ if (visited_protocol and upgrade_header.len > 0 and connection_header.len > 0 and websocket_accept_header.len > 0 and visited_version) {
+ break;
+ }
+ }
+ },
+ "Sec-WebSocket-Protocol".len => {
+ if (strings.eqlCaseInsensitiveASCII(header.name, "Sec-WebSocket-Protocol", false)) {
+ if (this.websocket_protocol == 0 or std.hash.Wyhash.hash(0, header.value) != this.websocket_protocol) {
+ this.terminate(ErrorCode.mismatch_client_protocol);
+ return;
+ }
+ visited_protocol = true;
+
+ if (visited_protocol and upgrade_header.len > 0 and connection_header.len > 0 and websocket_accept_header.len > 0 and visited_version) {
+ break;
+ }
+ }
+ },
+ else => {},
+ }
+ }
+
+ if (!visited_version) {
+ this.terminate(ErrorCode.invalid_websocket_version);
+ return;
+ }
+
+ if (@minimum(upgrade_header.name.len, upgrade_header.value.len) == 0) {
+ this.terminate(ErrorCode.missing_upgrade_header);
+ return;
+ }
+
+ if (@minimum(connection_header.name.len, connection_header.value.len) == 0) {
+ this.terminate(ErrorCode.missing_connection_header);
+ return;
+ }
+
+ if (@minimum(websocket_accept_header.name.len, websocket_accept_header.value.len) == 0) {
+ this.terminate(ErrorCode.missing_websocket_accept_header);
+ return;
+ }
+
+ if (!visited_protocol) {
+ this.terminate(ErrorCode.mismatch_client_protocol);
+ return;
+ }
+
+ if (strings.eqlComptime(connection_header.value, "Upgrade")) {
+ this.terminate(ErrorCode.invalid_connection_header);
+ return;
+ }
+
+ if (!strings.eqlComptime(upgrade_header.value, "websocket")) {
+ this.terminate(ErrorCode.invalid_upgrade_header);
+ return;
+ }
+
+ // TODO: check websocket_accept_header.value
+
+ const overflow_len = overflow_buf.len + remain_buf.len;
+ var overflow: []u8 = &.{};
+ if (overflow_len > 0) {
+ overflow = bun.default_allocator.alloc(u8, overflow_len) catch {
+ this.terminate(ErrorCode.invalid_response);
+ return;
+ };
+ if (remain_buf.len > 0) @memcpy(overflow.ptr, remain_buf.ptr, remain_buf.len);
+ if (overflow_buf.len > 0) @memcpy(overflow.ptr + remain_buf.len, overflow_buf.ptr, remain_buf.len);
+ }
+
+ this.clearData();
+ WebSocket__didConnect(this.outgoing_websocket, this.socket.socket, overflow.ptr, overflow.len);
+ }
+
+ pub fn handleWritable(
+ this: *HTTPClient,
+ socket: Socket,
+ ) void {
+ std.debug.assert(socket.socket == this.socket.socket);
+
+ if (this.to_send.len == 0)
+ return;
+
+ const wrote = socket.write(this.to_send, false);
+ if (wrote < 0) {
+ this.terminate(ErrorCode.failed_to_write);
+ return;
+ }
+ std.debug.assert(@intCast(usize, wrote) >= this.to_send.len);
+ this.to_send = this.to_send[@minimum(@intCast(usize, wrote), this.to_send.len)..];
+ }
+ pub fn handleTimeout(
+ this: *HTTPClient,
+ _: Socket,
+ ) void {
+ this.terminate(ErrorCode.timeout);
+ }
+ pub fn handleConnectError(this: *HTTPClient, _: Socket, _: c_int) void {
+ this.terminate(ErrorCode.failed_to_connect);
+ }
+
+ const Exports = shim.exportFunctions(.{
+ .connect = connect,
+ .cancel = cancel,
+ .register = register,
+ });
+
+ comptime {
+ if (!JSC.is_bindgen) {
+ @export(connect, .{
+ .name = Exports[0].symbol_name,
+ });
+ @export(cancel, .{
+ .name = Exports[1].symbol_name,
+ });
+ @export(register, .{
+ .name = Exports[2].symbol_name,
+ });
+ }
+ }
+ };
+}
+
+pub const WebSocketUpgradeClient = NewHTTPUpgradeClient(false);
+pub const SecureWebSocketUpgradeClient = NewHTTPUpgradeClient(true);
diff --git a/src/javascript/jsc/api/html_rewriter.zig b/src/javascript/jsc/api/html_rewriter.zig
index 7605cfef7..302af6aac 100644
--- a/src/javascript/jsc/api/html_rewriter.zig
+++ b/src/javascript/jsc/api/html_rewriter.zig
@@ -32,7 +32,7 @@ const Response = WebCore.Response;
const LOLHTML = @import("lolhtml");
const SelectorMap = std.ArrayListUnmanaged(*LOLHTML.HTMLSelector);
-const LOLHTMLContext = struct {
+pub const LOLHTMLContext = struct {
selectors: SelectorMap = .{},
element_handlers: std.ArrayListUnmanaged(*ElementHandler) = .{},
document_handlers: std.ArrayListUnmanaged(*DocumentHandler) = .{},
@@ -232,6 +232,163 @@ pub const HTMLRewriter = struct {
return this.beginTransform(global, response);
}
+ pub const HTMLRewriterLoader = struct {
+ rewriter: *LOLHTML.HTMLRewriter,
+ finalized: bool = false,
+ context: LOLHTMLContext,
+ chunk_size: usize = 0,
+ failed: bool = false,
+ output: JSC.WebCore.Sink,
+ signal: JSC.WebCore.Signal = .{},
+ backpressure: std.fifo.LinearFifo(u8, .Dynamic) = std.fifo.LinearFifo(u8, .Dynamic).init(bun.default_allocator),
+
+ pub fn finalize(this: *HTMLRewriterLoader) void {
+ if (this.finalized) return;
+ this.rewriter.deinit();
+ this.backpressure.deinit();
+ this.backpressure = std.fifo.LinearFifo(u8, .Dynamic).init(bun.default_allocator);
+ this.finalized = true;
+ }
+
+ pub fn fail(this: *HTMLRewriterLoader, err: JSC.Node.Syscall.Error) void {
+ this.signal.close(err);
+ this.output.end(err);
+ this.failed = true;
+ this.finalize();
+ }
+
+ pub fn connect(this: *HTMLRewriterLoader, signal: JSC.WebCore.Signal) void {
+ this.signal = signal;
+ }
+
+ pub fn writeToDestination(this: *HTMLRewriterLoader, bytes: []const u8) void {
+ if (this.backpressure.count > 0) {
+ this.backpressure.write(bytes) catch {
+ this.fail(JSC.Node.Syscall.Error.oom);
+ this.finalize();
+ };
+ return;
+ }
+
+ const write_result = this.output.write(.{ .temporary = bun.ByteList.init(bytes) });
+
+ switch (write_result) {
+ .err => |err| {
+ this.fail(err);
+ },
+ .owned_and_done, .temporary_and_done, .into_array_and_done => {
+ this.done();
+ },
+ .pending => |pending| {
+ pending.applyBackpressure(bun.default_allocator, &this.output, pending, bytes);
+ },
+ .into_array, .owned, .temporary => {
+ this.signal.ready(if (this.chunk_size > 0) this.chunk_size else null, null);
+ },
+ }
+ }
+
+ pub fn done(
+ this: *HTMLRewriterLoader,
+ ) void {
+ this.output.end(null);
+ this.signal.close(null);
+ this.finalize();
+ }
+
+ pub fn setup(
+ this: *HTMLRewriterLoader,
+ builder: *LOLHTML.HTMLRewriter.Builder,
+ context: LOLHTMLContext,
+ size_hint: ?usize,
+ output: JSC.WebCore.Sink,
+ ) ?[]const u8 {
+ for (context.document_handlers.items) |doc| {
+ doc.ctx = this;
+ }
+ for (context.element_handlers.items) |doc| {
+ doc.ctx = this;
+ }
+
+ const chunk_size = @maximum(size_hint orelse 16384, 1024);
+ this.rewriter = builder.build(
+ .UTF8,
+ .{
+ .preallocated_parsing_buffer_size = chunk_size,
+ .max_allowed_memory_usage = std.math.maxInt(u32),
+ },
+ false,
+ HTMLRewriterLoader,
+ this,
+ HTMLRewriterLoader.writeToDestination,
+ HTMLRewriterLoader.done,
+ ) catch {
+ output.end();
+ return LOLHTML.HTMLString.lastError().slice();
+ };
+
+ this.chunk_size = chunk_size;
+ this.context = context;
+ this.output = output;
+
+ return null;
+ }
+
+ pub fn sink(this: *HTMLRewriterLoader) JSC.WebCore.Sink {
+ return JSC.WebCore.Sink.init(this);
+ }
+
+ fn writeBytes(this: *HTMLRewriterLoader, bytes: bun.ByteList, comptime deinit_: bool) ?JSC.Node.Syscall.Error {
+ this.rewriter.write(bytes.slice()) catch {
+ return JSC.Node.Syscall.Error{
+ .errno = 1,
+ // TODO: make this a union
+ .path = bun.default_allocator.dupe(u8, LOLHTML.HTMLString.lastError().slice()) catch unreachable,
+ };
+ };
+ if (comptime deinit_) bytes.listManaged(bun.default_allocator).deinit();
+ return null;
+ }
+
+ pub fn write(this: *HTMLRewriterLoader, data: JSC.WebCore.StreamResult) JSC.WebCore.StreamResult.Writable {
+ switch (data) {
+ .owned => |bytes| {
+ if (this.writeBytes(bytes, true)) |err| {
+ return .{ .err = err };
+ }
+ return .{ .owned = bytes.len };
+ },
+ .owned_and_done => |bytes| {
+ if (this.writeBytes(bytes, true)) |err| {
+ return .{ .err = err };
+ }
+ return .{ .owned_and_done = bytes.len };
+ },
+ .temporary_and_done => |bytes| {
+ if (this.writeBytes(bytes, false)) |err| {
+ return .{ .err = err };
+ }
+ return .{ .temporary_and_done = bytes.len };
+ },
+ .temporary => |bytes| {
+ if (this.writeBytes(bytes, false)) |err| {
+ return .{ .err = err };
+ }
+ return .{ .temporary = bytes.len };
+ },
+ else => unreachable,
+ }
+ }
+
+ pub fn writeUTF16(this: *HTMLRewriterLoader, data: JSC.WebCore.StreamResult) JSC.WebCore.StreamResult.Writable {
+ return JSC.WebCore.Sink.UTF8Fallback.writeUTF16(HTMLRewriterLoader, this, data, write);
+ }
+
+ pub fn writeLatin1(this: *HTMLRewriterLoader, data: JSC.WebCore.StreamResult) JSC.WebCore.StreamResult.Writable {
+ return JSC.WebCore.Sink.UTF8Fallback.writeLatin1(HTMLRewriterLoader, this, data, write);
+ }
+ };
+
pub const BufferOutputSink = struct {
global: *JSGlobalObject,
bytes: bun.MutableString,
diff --git a/src/javascript/jsc/bindings/BunJSCModule.cpp b/src/javascript/jsc/bindings/BunJSCModule.cpp
index c9c2e6cfa..027cb3778 100644
--- a/src/javascript/jsc/bindings/BunJSCModule.cpp
+++ b/src/javascript/jsc/bindings/BunJSCModule.cpp
@@ -10,12 +10,66 @@
#include "JavaScriptCore/APICast.h"
#include "JavaScriptCore/JSBasePrivate.h"
#include "JavaScriptCore/ObjectConstructor.h"
-
+#include "JavaScriptCore/RemoteInspectorServer.h"
+#include "JavaScriptCore/AggregateError.h"
+#include "JavaScriptCore/BytecodeIndex.h"
+#include "JavaScriptCore/CallFrameInlines.h"
+#include "JavaScriptCore/ClassInfo.h"
+#include "JavaScriptCore/CodeBlock.h"
+#include "JavaScriptCore/CodeCache.h"
+#include "JavaScriptCore/Completion.h"
+#include "JavaScriptCore/Error.h"
+#include "JavaScriptCore/ErrorInstance.h"
#include "mimalloc.h"
using namespace JSC;
using namespace WTF;
+JSC_DECLARE_HOST_FUNCTION(functionStartRemoteDebugger);
+JSC_DEFINE_HOST_FUNCTION(functionStartRemoteDebugger, (JSGlobalObject * globalObject, CallFrame* callFrame))
+{
+
+ static const char* defaultHost = "127.0.0.1\0";
+ static uint16_t defaultPort = 9230; // node + 1
+ auto& vm = globalObject->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
+ JSC::JSValue hostValue = callFrame->argument(0);
+ JSC::JSValue portValue = callFrame->argument(1);
+ const char* host = defaultHost;
+ if (hostValue.isString()) {
+
+ auto str = hostValue.toWTFString(globalObject);
+ if (!str.isEmpty())
+ host = toCString(str).data();
+ } else if (!hostValue.isUndefined()) {
+ throwVMError(globalObject, scope, createTypeError(globalObject, "host must be a string"_s));
+ return JSC::JSValue::encode(JSC::jsUndefined());
+ }
+
+ uint16_t port = defaultPort;
+ if (portValue.isNumber()) {
+ auto port_int = portValue.toUInt32(globalObject);
+ if (!(port_int > 0 && port_int < 65536)) {
+ throwVMError(globalObject, scope, createRangeError(globalObject, "port must be between 0 and 65535"_s));
+ return JSC::JSValue::encode(JSC::jsUndefined());
+ }
+ port = port_int;
+ } else if (!portValue.isUndefined()) {
+ throwVMError(globalObject, scope, createTypeError(globalObject, "port must be a number between 0 and 65535"_s));
+ return JSC::JSValue::encode(JSC::jsUndefined());
+ }
+
+ globalObject->setRemoteDebuggingEnabled(true);
+ auto& server = Inspector::RemoteInspectorServer::singleton();
+ if (!server.start(reinterpret_cast<const char*>(host), port)) {
+ throwVMError(globalObject, scope, createError(globalObject, "Failed to start server \""_s + host + ":"_s + port + "\". Is port already in use?"_s));
+ return JSC::JSValue::encode(JSC::jsUndefined());
+ }
+
+ RELEASE_AND_RETURN(scope, JSC::JSValue::encode(JSC::jsUndefined()));
+}
+
JSC_DECLARE_HOST_FUNCTION(functionDescribe);
JSC_DEFINE_HOST_FUNCTION(functionDescribe, (JSGlobalObject * globalObject, CallFrame* callFrame))
{
@@ -266,27 +320,28 @@ JSC::JSObject* createJSCModule(JSC::JSGlobalObject* globalObject)
{
JSC::ObjectInitializationScope initializationScope(vm);
- object = JSC::constructEmptyObject(globalObject, globalObject->objectPrototype(), 20);
+ object = JSC::constructEmptyObject(globalObject, globalObject->objectPrototype(), 21);
+ object->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "callerSourceOrigin"_s), 1, functionCallerSourceOrigin, NoIntrinsic, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontDelete | 0);
object->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "describe"_s), 1, functionDescribe, NoIntrinsic, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontDelete | 0);
object->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "describeArray"_s), 1, functionDescribeArray, NoIntrinsic, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontDelete | 0);
- object->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "gcAndSweep"_s), 1, functionGCAndSweep, NoIntrinsic, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontDelete | 0);
- object->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "fullGC"_s), 1, functionFullGC, NoIntrinsic, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontDelete | 0);
+ object->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "drainMicrotasks"_s), 1, functionDrainMicrotasks, NoIntrinsic, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontDelete | 0);
object->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "edenGC"_s), 1, functionEdenGC, NoIntrinsic, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontDelete | 0);
+ object->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "fullGC"_s), 1, functionFullGC, NoIntrinsic, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontDelete | 0);
+ object->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "gcAndSweep"_s), 1, functionGCAndSweep, NoIntrinsic, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontDelete | 0);
+ object->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "getRandomSeed"_s), 1, functionGetRandomSeed, NoIntrinsic, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontDelete | 0);
object->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "heapSize"_s), 1, functionHeapSize, NoIntrinsic, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontDelete | 0);
object->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "heapStats"_s), 1, functionMemoryUsageStatistics, NoIntrinsic, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontDelete | 0);
- object->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "memoryUsage"_s), 1, functionCreateMemoryFootprint, NoIntrinsic, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontDelete | 0);
- object->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "getRandomSeed"_s), 1, functionGetRandomSeed, NoIntrinsic, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontDelete | 0);
- object->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "setRandomSeed"_s), 1, functionSetRandomSeed, NoIntrinsic, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontDelete | 0);
object->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "isRope"_s), 1, functionIsRope, NoIntrinsic, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontDelete | 0);
- object->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "callerSourceOrigin"_s), 1, functionCallerSourceOrigin, NoIntrinsic, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontDelete | 0);
+ object->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "memoryUsage"_s), 1, functionCreateMemoryFootprint, NoIntrinsic, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontDelete | 0);
object->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "noFTL"_s), 1, functionNoFTL, NoIntrinsic, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontDelete | 0);
object->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "noOSRExitFuzzing"_s), 1, functionNoOSRExitFuzzing, NoIntrinsic, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontDelete | 0);
- object->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "optimizeNextInvocation"_s), 1, functionOptimizeNextInvocation, NoIntrinsic, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontDelete | 0);
object->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "numberOfDFGCompiles"_s), 1, functionNumberOfDFGCompiles, NoIntrinsic, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontDelete | 0);
+ object->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "optimizeNextInvocation"_s), 1, functionOptimizeNextInvocation, NoIntrinsic, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontDelete | 0);
object->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "releaseWeakRefs"_s), 1, functionReleaseWeakRefs, NoIntrinsic, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontDelete | 0);
- object->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "totalCompileTime"_s), 1, functionTotalCompileTime, NoIntrinsic, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontDelete | 0);
object->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "reoptimizationRetryCount"_s), 1, functionReoptimizationRetryCount, NoIntrinsic, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontDelete | 0);
- object->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "drainMicrotasks"_s), 1, functionDrainMicrotasks, NoIntrinsic, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontDelete | 0);
+ object->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "setRandomSeed"_s), 1, functionSetRandomSeed, NoIntrinsic, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontDelete | 0);
+ object->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "startRemoteDebugger"_s), 2, functionStartRemoteDebugger, NoIntrinsic, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontDelete | 0);
+ object->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "totalCompileTime"_s), 1, functionTotalCompileTime, NoIntrinsic, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontDelete | 0);
}
return object;
diff --git a/src/javascript/jsc/bindings/ScriptExecutionContext.cpp b/src/javascript/jsc/bindings/ScriptExecutionContext.cpp
new file mode 100644
index 000000000..67dd30f6f
--- /dev/null
+++ b/src/javascript/jsc/bindings/ScriptExecutionContext.cpp
@@ -0,0 +1,60 @@
+
+#include "ScriptExecutionContext.h"
+#include <uws/uSockets/src/libusockets.h>
+#include <uws/src/Loop.h>
+
+extern "C" void Bun__startLoop(us_loop_t* loop);
+
+namespace WebCore {
+
+template<bool isSSL>
+us_socket_context_t* webSocketContext()
+{
+ if constexpr (isSSL) {
+ if (!m_ssl_client_websockets_ctx) {
+ us_loop_t* loop = (us_loop_t*)uWs::Loop::get();
+ us_socket_context_options_t opts;
+ memset(&opts, 0, sizeof(us_socket_context_t));
+ this->m_ssl_client_websockets_ctx = us_create_socket_context(1, loop, sizeof(*ScriptExecutionContext), opts);
+ *us_socket_context_ext(m_ssl_client_websockets_ctx) = this;
+ WebSocketStream::registerHTTPContext(this, m_ssl_client_websockets_ctx, loop);
+ }
+
+ return m_ssl_client_websockets_ctx;
+ } else {
+ if (!m_client_websockets_ctx) {
+ us_loop_t* loop = (us_loop_t*)uWs::Loop::get();
+ us_socket_context_options_t opts;
+ memset(&opts, 0, sizeof(us_socket_context_t));
+ this->m_client_websockets_ctx = us_create_socket_context(0, loop, sizeof(*ScriptExecutionContext), opts);
+ *us_socket_context_ext(m_client_websockets_ctx) = this;
+ SecureWebSocketStream::registerHTTPContext(this, m_client_websockets_ctx, loop);
+ }
+
+ return m_client_websockets_ctx;
+ }
+}
+
+template<bool isSSL, bool isServer>
+uWS::WebSocketContext<isSSL, isServer, ScriptExecutionContext*>*
+{
+ if constexpr (isSSL) {
+ if (!m_connected_ssl_client_websockets_ctx) {
+ // should be the parent
+ RELEASE_ASSERT(m_ssl_client_websockets_ctx);
+ m_connected_client_websockets_ctx = SecureWebSocketStream::registerClientContext(this, webSocketContext<isSSL>(), loop);
+ }
+
+ return m_connected_ssl_client_websockets_ctx;
+ } else {
+ if (!m_connected_client_websockets_ctx) {
+ // should be the parent
+ RELEASE_ASSERT(m_client_websockets_ctx);
+ m_connected_client_websockets_ctx = WebSocketStream::registerClientContext(this, webSocketContext<isSSL>(), loop);
+ }
+
+ return m_connected_client_websockets_ctx;
+ }
+}
+
+} \ No newline at end of file
diff --git a/src/javascript/jsc/bindings/ScriptExecutionContext.h b/src/javascript/jsc/bindings/ScriptExecutionContext.h
index 4265ffccd..8f4e2edfd 100644
--- a/src/javascript/jsc/bindings/ScriptExecutionContext.h
+++ b/src/javascript/jsc/bindings/ScriptExecutionContext.h
@@ -10,6 +10,10 @@
#include <wtf/text/WTFString.h>
#include "CachedScript.h"
#include "wtf/URL.h"
+#include <uws/src/WebSocketContext.h>
+struct us_socket_t;
+struct us_socket_context_t;
+
namespace WebCore {
class ScriptExecutionContext : public CanMakeWeakPtr<ScriptExecutionContext> {
@@ -58,6 +62,13 @@ public:
{
return m_globalObject;
}
+
+ template<bool isSSL>
+ us_socket_context_t* webSocketContext();
+
+ template<bool isSSL, bool isServer>
+ uWS::WebSocketContext<isSSL, isServer, ScriptExecutionContext*>* connnectedWebSocketContext();
+
const WTF::URL& url() const { return m_url; }
bool activeDOMObjectsAreSuspended() { return false; }
bool activeDOMObjectsAreStopped() { return false; }
@@ -72,7 +83,7 @@ public:
// {
// }
- void postTask(Task&&)
+ void postTask(Task&& task)
{
} // Executes the task on context's thread asynchronously.
@@ -91,5 +102,11 @@ private:
JSC::VM* m_vm = nullptr;
JSC::JSGlobalObject* m_globalObject = nullptr;
WTF::URL m_url = WTF::URL();
+
+ us_socket_context_t* m_ssl_client_websockets_ctx = nullptr;
+ us_socket_context_t* m_client_websockets_ctx = nullptr;
+
+ uWS::WebSocketContext<true, false, ScriptExecutionContext*>* m_ssl_client_websockets_ctx = nullptr;
+ uWS::WebSocketContext<true, true, ScriptExecutionContext*>* m_client_websockets_ctx = nullptr;
};
} \ No newline at end of file
diff --git a/src/javascript/jsc/bindings/bindings.zig b/src/javascript/jsc/bindings/bindings.zig
index f061e50b5..841fa7a38 100644
--- a/src/javascript/jsc/bindings/bindings.zig
+++ b/src/javascript/jsc/bindings/bindings.zig
@@ -1620,6 +1620,10 @@ pub const JSGlobalObject = extern struct {
return @ptrCast(*JSC.VirtualMachine, @alignCast(std.meta.alignment(JSC.VirtualMachine), this.bunVM_()));
}
+ pub fn startRemoteInspector(this: *JSGlobalObject, host: [:0]const u8, port: u16) bool {
+ return cppFn("startRemoteInspector", .{ this, host, port });
+ }
+
extern fn ZigGlobalObject__readableStreamToArrayBuffer(*JSGlobalObject, JSValue) JSValue;
extern fn ZigGlobalObject__readableStreamToText(*JSGlobalObject, JSValue) JSValue;
extern fn ZigGlobalObject__readableStreamToJSON(*JSGlobalObject, JSValue) JSValue;
@@ -1677,6 +1681,7 @@ pub const JSGlobalObject = extern struct {
"asyncGeneratorFunctionPrototype",
"vm",
"generateHeapSnapshot",
+ "startRemoteInspector",
// "createError",
// "throwError",
};
diff --git a/src/javascript/jsc/bindings/webcore/JSWebSocket.cpp b/src/javascript/jsc/bindings/webcore/JSWebSocket.cpp
new file mode 100644
index 000000000..aa351fba3
--- /dev/null
+++ b/src/javascript/jsc/bindings/webcore/JSWebSocket.cpp
@@ -0,0 +1,705 @@
+/*
+ This file is part of the WebKit open source project.
+ This file has been generated by generate-bindings.pl. DO NOT MODIFY!
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "config.h"
+#include "JSWebSocket.h"
+
+#include "ActiveDOMObject.h"
+#include "EventNames.h"
+#include "ExtendedDOMClientIsoSubspaces.h"
+#include "ExtendedDOMIsoSubspaces.h"
+#include "IDLTypes.h"
+// #include "JSBlob.h"
+#include "JSDOMAttribute.h"
+#include "JSDOMBinding.h"
+#include "JSDOMConstructor.h"
+#include "JSDOMConvertBase.h"
+#include "JSDOMConvertBufferSource.h"
+#include "JSDOMConvertInterface.h"
+#include "JSDOMConvertNullable.h"
+#include "JSDOMConvertNumbers.h"
+#include "JSDOMConvertSequences.h"
+#include "JSDOMConvertStrings.h"
+#include "JSDOMExceptionHandling.h"
+#include "JSDOMGlobalObjectInlines.h"
+#include "JSDOMOperation.h"
+#include "JSDOMWrapperCache.h"
+#include "JSEventListener.h"
+#include "ScriptExecutionContext.h"
+#include "WebCoreJSClientData.h"
+#include <JavaScriptCore/HeapAnalyzer.h>
+#include <JavaScriptCore/IteratorOperations.h>
+#include <JavaScriptCore/JSArray.h>
+#include <JavaScriptCore/JSCInlines.h>
+#include <JavaScriptCore/JSDestructibleObjectHeapCellType.h>
+#include <JavaScriptCore/SlotVisitorMacros.h>
+#include <JavaScriptCore/SubspaceInlines.h>
+#include <wtf/GetPtr.h>
+#include <wtf/PointerPreparations.h>
+#include <wtf/URL.h>
+
+namespace WebCore {
+using namespace JSC;
+
+// Functions
+
+static JSC_DECLARE_HOST_FUNCTION(jsWebSocketPrototypeFunction_send);
+static JSC_DECLARE_HOST_FUNCTION(jsWebSocketPrototypeFunction_close);
+
+// Attributes
+
+static JSC_DECLARE_CUSTOM_GETTER(jsWebSocketConstructor);
+static JSC_DECLARE_CUSTOM_GETTER(jsWebSocket_URL);
+static JSC_DECLARE_CUSTOM_GETTER(jsWebSocket_url);
+static JSC_DECLARE_CUSTOM_GETTER(jsWebSocket_readyState);
+static JSC_DECLARE_CUSTOM_GETTER(jsWebSocket_bufferedAmount);
+static JSC_DECLARE_CUSTOM_GETTER(jsWebSocket_onopen);
+static JSC_DECLARE_CUSTOM_SETTER(setJSWebSocket_onopen);
+static JSC_DECLARE_CUSTOM_GETTER(jsWebSocket_onmessage);
+static JSC_DECLARE_CUSTOM_SETTER(setJSWebSocket_onmessage);
+static JSC_DECLARE_CUSTOM_GETTER(jsWebSocket_onerror);
+static JSC_DECLARE_CUSTOM_SETTER(setJSWebSocket_onerror);
+static JSC_DECLARE_CUSTOM_GETTER(jsWebSocket_onclose);
+static JSC_DECLARE_CUSTOM_SETTER(setJSWebSocket_onclose);
+static JSC_DECLARE_CUSTOM_GETTER(jsWebSocket_protocol);
+static JSC_DECLARE_CUSTOM_GETTER(jsWebSocket_extensions);
+static JSC_DECLARE_CUSTOM_GETTER(jsWebSocket_binaryType);
+static JSC_DECLARE_CUSTOM_SETTER(setJSWebSocket_binaryType);
+
+class JSWebSocketPrototype final : public JSC::JSNonFinalObject {
+public:
+ using Base = JSC::JSNonFinalObject;
+ static JSWebSocketPrototype* create(JSC::VM& vm, JSDOMGlobalObject* globalObject, JSC::Structure* structure)
+ {
+ JSWebSocketPrototype* ptr = new (NotNull, JSC::allocateCell<JSWebSocketPrototype>(vm)) JSWebSocketPrototype(vm, globalObject, structure);
+ ptr->finishCreation(vm);
+ return ptr;
+ }
+
+ DECLARE_INFO;
+ template<typename CellType, JSC::SubspaceAccess>
+ static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
+ {
+ STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSWebSocketPrototype, Base);
+ return &vm.plainObjectSpace();
+ }
+ static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
+ {
+ return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info());
+ }
+
+private:
+ JSWebSocketPrototype(JSC::VM& vm, JSC::JSGlobalObject*, JSC::Structure* structure)
+ : JSC::JSNonFinalObject(vm, structure)
+ {
+ }
+
+ void finishCreation(JSC::VM&);
+};
+STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSWebSocketPrototype, JSWebSocketPrototype::Base);
+
+using JSWebSocketDOMConstructor = JSDOMConstructor<JSWebSocket>;
+
+/* Hash table for constructor */
+
+static const HashTableValue JSWebSocketConstructorTableValues[] = {
+ { "CONNECTING"_s, JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::ConstantInteger, NoIntrinsic, { (long long)(0) } },
+ { "OPEN"_s, JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::ConstantInteger, NoIntrinsic, { (long long)(1) } },
+ { "CLOSING"_s, JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::ConstantInteger, NoIntrinsic, { (long long)(2) } },
+ { "CLOSED"_s, JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::ConstantInteger, NoIntrinsic, { (long long)(3) } },
+};
+
+static_assert(WebSocket::CONNECTING == 0, "CONNECTING in WebSocket does not match value from IDL");
+static_assert(WebSocket::OPEN == 1, "OPEN in WebSocket does not match value from IDL");
+static_assert(WebSocket::CLOSING == 2, "CLOSING in WebSocket does not match value from IDL");
+static_assert(WebSocket::CLOSED == 3, "CLOSED in WebSocket does not match value from IDL");
+
+static inline EncodedJSValue constructJSWebSocket1(JSGlobalObject* lexicalGlobalObject, CallFrame* callFrame)
+{
+ VM& vm = lexicalGlobalObject->vm();
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ auto* castedThis = jsCast<JSWebSocketDOMConstructor*>(callFrame->jsCallee());
+ ASSERT(castedThis);
+ auto* context = castedThis->scriptExecutionContext();
+ if (UNLIKELY(!context))
+ return throwConstructorScriptExecutionContextUnavailableError(*lexicalGlobalObject, throwScope, "WebSocket");
+ EnsureStillAliveScope argument0 = callFrame->uncheckedArgument(0);
+ auto url = convert<IDLUSVString>(*lexicalGlobalObject, argument0.value());
+ RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
+ EnsureStillAliveScope argument1 = callFrame->argument(1);
+ auto protocols = argument1.value().isUndefined() ? Converter<IDLSequence<IDLDOMString>>::ReturnType {} : convert<IDLSequence<IDLDOMString>>(*lexicalGlobalObject, argument1.value());
+ RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
+ auto object = WebSocket::create(*context, WTFMove(url), WTFMove(protocols));
+ if constexpr (IsExceptionOr<decltype(object)>)
+ RETURN_IF_EXCEPTION(throwScope, {});
+ static_assert(TypeOrExceptionOrUnderlyingType<decltype(object)>::isRef);
+ auto jsValue = toJSNewlyCreated<IDLInterface<WebSocket>>(*lexicalGlobalObject, *castedThis->globalObject(), throwScope, WTFMove(object));
+ if constexpr (IsExceptionOr<decltype(object)>)
+ RETURN_IF_EXCEPTION(throwScope, {});
+ setSubclassStructureIfNeeded<WebSocket>(lexicalGlobalObject, callFrame, asObject(jsValue));
+ RETURN_IF_EXCEPTION(throwScope, {});
+ return JSValue::encode(jsValue);
+}
+
+static inline EncodedJSValue constructJSWebSocket2(JSGlobalObject* lexicalGlobalObject, CallFrame* callFrame)
+{
+ VM& vm = lexicalGlobalObject->vm();
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ auto* castedThis = jsCast<JSWebSocketDOMConstructor*>(callFrame->jsCallee());
+ ASSERT(castedThis);
+ auto* context = castedThis->scriptExecutionContext();
+ if (UNLIKELY(!context))
+ return throwConstructorScriptExecutionContextUnavailableError(*lexicalGlobalObject, throwScope, "WebSocket");
+ EnsureStillAliveScope argument0 = callFrame->uncheckedArgument(0);
+ auto url = convert<IDLUSVString>(*lexicalGlobalObject, argument0.value());
+ RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
+ EnsureStillAliveScope argument1 = callFrame->uncheckedArgument(1);
+ auto protocol = convert<IDLDOMString>(*lexicalGlobalObject, argument1.value());
+ RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
+ auto object = WebSocket::create(*context, WTFMove(url), WTFMove(protocol));
+ if constexpr (IsExceptionOr<decltype(object)>)
+ RETURN_IF_EXCEPTION(throwScope, {});
+ static_assert(TypeOrExceptionOrUnderlyingType<decltype(object)>::isRef);
+ auto jsValue = toJSNewlyCreated<IDLInterface<WebSocket>>(*lexicalGlobalObject, *castedThis->globalObject(), throwScope, WTFMove(object));
+ if constexpr (IsExceptionOr<decltype(object)>)
+ RETURN_IF_EXCEPTION(throwScope, {});
+ setSubclassStructureIfNeeded<WebSocket>(lexicalGlobalObject, callFrame, asObject(jsValue));
+ RETURN_IF_EXCEPTION(throwScope, {});
+ return JSValue::encode(jsValue);
+}
+
+template<> EncodedJSValue JSC_HOST_CALL_ATTRIBUTES JSWebSocketDOMConstructor::construct(JSGlobalObject* lexicalGlobalObject, CallFrame* callFrame)
+{
+ VM& vm = lexicalGlobalObject->vm();
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ UNUSED_PARAM(throwScope);
+ size_t argsCount = std::min<size_t>(2, callFrame->argumentCount());
+ if (argsCount == 1) {
+ RELEASE_AND_RETURN(throwScope, (constructJSWebSocket1(lexicalGlobalObject, callFrame)));
+ }
+ if (argsCount == 2) {
+ JSValue distinguishingArg = callFrame->uncheckedArgument(1);
+ if (distinguishingArg.isUndefined())
+ RELEASE_AND_RETURN(throwScope, (constructJSWebSocket1(lexicalGlobalObject, callFrame)));
+ {
+ bool success = hasIteratorMethod(lexicalGlobalObject, distinguishingArg);
+ RETURN_IF_EXCEPTION(throwScope, {});
+ if (success)
+ RELEASE_AND_RETURN(throwScope, (constructJSWebSocket1(lexicalGlobalObject, callFrame)));
+ }
+ RELEASE_AND_RETURN(throwScope, (constructJSWebSocket2(lexicalGlobalObject, callFrame)));
+ }
+ return argsCount < 1 ? throwVMError(lexicalGlobalObject, throwScope, createNotEnoughArgumentsError(lexicalGlobalObject)) : throwVMTypeError(lexicalGlobalObject, throwScope);
+}
+JSC_ANNOTATE_HOST_FUNCTION(JSWebSocketConstructorConstruct, JSWebSocketDOMConstructor::construct);
+
+template<> const ClassInfo JSWebSocketDOMConstructor::s_info = { "WebSocket"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSWebSocketDOMConstructor) };
+
+template<> JSValue JSWebSocketDOMConstructor::prototypeForStructure(JSC::VM& vm, const JSDOMGlobalObject& globalObject)
+{
+ return JSEventTarget::getConstructor(vm, &globalObject);
+}
+
+template<> void JSWebSocketDOMConstructor::initializeProperties(VM& vm, JSDOMGlobalObject& globalObject)
+{
+ putDirect(vm, vm.propertyNames->length, jsNumber(1), JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum);
+ JSString* nameString = jsNontrivialString(vm, "WebSocket"_s);
+ m_originalName.set(vm, this, nameString);
+ putDirect(vm, vm.propertyNames->name, nameString, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum);
+ putDirect(vm, vm.propertyNames->prototype, JSWebSocket::prototype(vm, globalObject), JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete);
+ reifyStaticProperties(vm, JSWebSocket::info(), JSWebSocketConstructorTableValues, *this);
+}
+
+/* Hash table for prototype */
+
+static const HashTableValue JSWebSocketPrototypeTableValues[] = {
+ { "constructor"_s, static_cast<unsigned>(JSC::PropertyAttribute::DontEnum), NoIntrinsic, { (intptr_t) static_cast<PropertySlot::GetValueFunc>(jsWebSocketConstructor), (intptr_t) static_cast<PutPropertySlot::PutValueFunc>(0) } },
+ { "URL"_s, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { (intptr_t) static_cast<PropertySlot::GetValueFunc>(jsWebSocket_URL), (intptr_t) static_cast<PutPropertySlot::PutValueFunc>(0) } },
+ { "url"_s, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { (intptr_t) static_cast<PropertySlot::GetValueFunc>(jsWebSocket_url), (intptr_t) static_cast<PutPropertySlot::PutValueFunc>(0) } },
+ { "readyState"_s, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { (intptr_t) static_cast<PropertySlot::GetValueFunc>(jsWebSocket_readyState), (intptr_t) static_cast<PutPropertySlot::PutValueFunc>(0) } },
+ { "bufferedAmount"_s, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { (intptr_t) static_cast<PropertySlot::GetValueFunc>(jsWebSocket_bufferedAmount), (intptr_t) static_cast<PutPropertySlot::PutValueFunc>(0) } },
+ { "onopen"_s, static_cast<unsigned>(JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { (intptr_t) static_cast<PropertySlot::GetValueFunc>(jsWebSocket_onopen), (intptr_t) static_cast<PutPropertySlot::PutValueFunc>(setJSWebSocket_onopen) } },
+ { "onmessage"_s, static_cast<unsigned>(JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { (intptr_t) static_cast<PropertySlot::GetValueFunc>(jsWebSocket_onmessage), (intptr_t) static_cast<PutPropertySlot::PutValueFunc>(setJSWebSocket_onmessage) } },
+ { "onerror"_s, static_cast<unsigned>(JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { (intptr_t) static_cast<PropertySlot::GetValueFunc>(jsWebSocket_onerror), (intptr_t) static_cast<PutPropertySlot::PutValueFunc>(setJSWebSocket_onerror) } },
+ { "onclose"_s, static_cast<unsigned>(JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { (intptr_t) static_cast<PropertySlot::GetValueFunc>(jsWebSocket_onclose), (intptr_t) static_cast<PutPropertySlot::PutValueFunc>(setJSWebSocket_onclose) } },
+ { "protocol"_s, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { (intptr_t) static_cast<PropertySlot::GetValueFunc>(jsWebSocket_protocol), (intptr_t) static_cast<PutPropertySlot::PutValueFunc>(0) } },
+ { "extensions"_s, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { (intptr_t) static_cast<PropertySlot::GetValueFunc>(jsWebSocket_extensions), (intptr_t) static_cast<PutPropertySlot::PutValueFunc>(0) } },
+ { "binaryType"_s, static_cast<unsigned>(JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { (intptr_t) static_cast<PropertySlot::GetValueFunc>(jsWebSocket_binaryType), (intptr_t) static_cast<PutPropertySlot::PutValueFunc>(setJSWebSocket_binaryType) } },
+ { "send"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { (intptr_t) static_cast<RawNativeFunction>(jsWebSocketPrototypeFunction_send), (intptr_t)(1) } },
+ { "close"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { (intptr_t) static_cast<RawNativeFunction>(jsWebSocketPrototypeFunction_close), (intptr_t)(0) } },
+ { "CONNECTING"_s, JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::ConstantInteger, NoIntrinsic, { (long long)(0) } },
+ { "OPEN"_s, JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::ConstantInteger, NoIntrinsic, { (long long)(1) } },
+ { "CLOSING"_s, JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::ConstantInteger, NoIntrinsic, { (long long)(2) } },
+ { "CLOSED"_s, JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::ConstantInteger, NoIntrinsic, { (long long)(3) } },
+};
+
+const ClassInfo JSWebSocketPrototype::s_info = { "WebSocket"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSWebSocketPrototype) };
+
+void JSWebSocketPrototype::finishCreation(VM& vm)
+{
+ Base::finishCreation(vm);
+ reifyStaticProperties(vm, JSWebSocket::info(), JSWebSocketPrototypeTableValues, *this);
+ JSC_TO_STRING_TAG_WITHOUT_TRANSITION();
+}
+
+const ClassInfo JSWebSocket::s_info = { "WebSocket"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSWebSocket) };
+
+JSWebSocket::JSWebSocket(Structure* structure, JSDOMGlobalObject& globalObject, Ref<WebSocket>&& impl)
+ : JSEventTarget(structure, globalObject, WTFMove(impl))
+{
+}
+
+void JSWebSocket::finishCreation(VM& vm)
+{
+ Base::finishCreation(vm);
+ ASSERT(inherits(info()));
+
+ // static_assert(std::is_base_of<ActiveDOMObject, WebSocket>::value, "Interface is marked as [ActiveDOMObject] but implementation class does not subclass ActiveDOMObject.");
+}
+
+JSObject* JSWebSocket::createPrototype(VM& vm, JSDOMGlobalObject& globalObject)
+{
+ return JSWebSocketPrototype::create(vm, &globalObject, JSWebSocketPrototype::createStructure(vm, &globalObject, JSEventTarget::prototype(vm, globalObject)));
+}
+
+JSObject* JSWebSocket::prototype(VM& vm, JSDOMGlobalObject& globalObject)
+{
+ return getDOMPrototype<JSWebSocket>(vm, globalObject);
+}
+
+JSValue JSWebSocket::getConstructor(VM& vm, const JSGlobalObject* globalObject)
+{
+ return getDOMConstructor<JSWebSocketDOMConstructor, DOMConstructorID::WebSocket>(vm, *jsCast<const JSDOMGlobalObject*>(globalObject));
+}
+
+JSC_DEFINE_CUSTOM_GETTER(jsWebSocketConstructor, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName))
+{
+ VM& vm = JSC::getVM(lexicalGlobalObject);
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ auto* prototype = jsDynamicCast<JSWebSocketPrototype*>(JSValue::decode(thisValue));
+ if (UNLIKELY(!prototype))
+ return throwVMTypeError(lexicalGlobalObject, throwScope);
+ return JSValue::encode(JSWebSocket::getConstructor(JSC::getVM(lexicalGlobalObject), prototype->globalObject()));
+}
+
+static inline JSValue jsWebSocket_URLGetter(JSGlobalObject& lexicalGlobalObject, JSWebSocket& thisObject)
+{
+ auto& vm = JSC::getVM(&lexicalGlobalObject);
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ auto& impl = thisObject.wrapped();
+ RELEASE_AND_RETURN(throwScope, (toJS<IDLUSVString>(lexicalGlobalObject, throwScope, impl.url())));
+}
+
+JSC_DEFINE_CUSTOM_GETTER(jsWebSocket_URL, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName))
+{
+ return IDLAttribute<JSWebSocket>::get<jsWebSocket_URLGetter, CastedThisErrorBehavior::Assert>(*lexicalGlobalObject, thisValue, attributeName);
+}
+
+static inline JSValue jsWebSocket_urlGetter(JSGlobalObject& lexicalGlobalObject, JSWebSocket& thisObject)
+{
+ auto& vm = JSC::getVM(&lexicalGlobalObject);
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ auto& impl = thisObject.wrapped();
+ RELEASE_AND_RETURN(throwScope, (toJS<IDLUSVString>(lexicalGlobalObject, throwScope, impl.url())));
+}
+
+JSC_DEFINE_CUSTOM_GETTER(jsWebSocket_url, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName))
+{
+ return IDLAttribute<JSWebSocket>::get<jsWebSocket_urlGetter, CastedThisErrorBehavior::Assert>(*lexicalGlobalObject, thisValue, attributeName);
+}
+
+static inline JSValue jsWebSocket_readyStateGetter(JSGlobalObject& lexicalGlobalObject, JSWebSocket& thisObject)
+{
+ auto& vm = JSC::getVM(&lexicalGlobalObject);
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ auto& impl = thisObject.wrapped();
+ RELEASE_AND_RETURN(throwScope, (toJS<IDLUnsignedShort>(lexicalGlobalObject, throwScope, impl.readyState())));
+}
+
+JSC_DEFINE_CUSTOM_GETTER(jsWebSocket_readyState, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName))
+{
+ return IDLAttribute<JSWebSocket>::get<jsWebSocket_readyStateGetter, CastedThisErrorBehavior::Assert>(*lexicalGlobalObject, thisValue, attributeName);
+}
+
+static inline JSValue jsWebSocket_bufferedAmountGetter(JSGlobalObject& lexicalGlobalObject, JSWebSocket& thisObject)
+{
+ auto& vm = JSC::getVM(&lexicalGlobalObject);
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ auto& impl = thisObject.wrapped();
+ RELEASE_AND_RETURN(throwScope, (toJS<IDLUnsignedLong>(lexicalGlobalObject, throwScope, impl.bufferedAmount())));
+}
+
+JSC_DEFINE_CUSTOM_GETTER(jsWebSocket_bufferedAmount, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName))
+{
+ return IDLAttribute<JSWebSocket>::get<jsWebSocket_bufferedAmountGetter, CastedThisErrorBehavior::Assert>(*lexicalGlobalObject, thisValue, attributeName);
+}
+
+static inline JSValue jsWebSocket_onopenGetter(JSGlobalObject& lexicalGlobalObject, JSWebSocket& thisObject)
+{
+ UNUSED_PARAM(lexicalGlobalObject);
+ return eventHandlerAttribute(thisObject.wrapped(), eventNames().openEvent, worldForDOMObject(thisObject));
+}
+
+JSC_DEFINE_CUSTOM_GETTER(jsWebSocket_onopen, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName))
+{
+ return IDLAttribute<JSWebSocket>::get<jsWebSocket_onopenGetter, CastedThisErrorBehavior::Assert>(*lexicalGlobalObject, thisValue, attributeName);
+}
+
+static inline bool setJSWebSocket_onopenSetter(JSGlobalObject& lexicalGlobalObject, JSWebSocket& thisObject, JSValue value)
+{
+ auto& vm = JSC::getVM(&lexicalGlobalObject);
+ setEventHandlerAttribute<JSEventListener>(thisObject.wrapped(), eventNames().openEvent, value, thisObject);
+ vm.writeBarrier(&thisObject, value);
+ ensureStillAliveHere(value);
+
+ return true;
+}
+
+JSC_DEFINE_CUSTOM_SETTER(setJSWebSocket_onopen, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, EncodedJSValue encodedValue, PropertyName attributeName))
+{
+ return IDLAttribute<JSWebSocket>::set<setJSWebSocket_onopenSetter>(*lexicalGlobalObject, thisValue, encodedValue, attributeName);
+}
+
+static inline JSValue jsWebSocket_onmessageGetter(JSGlobalObject& lexicalGlobalObject, JSWebSocket& thisObject)
+{
+ UNUSED_PARAM(lexicalGlobalObject);
+ return eventHandlerAttribute(thisObject.wrapped(), eventNames().messageEvent, worldForDOMObject(thisObject));
+}
+
+JSC_DEFINE_CUSTOM_GETTER(jsWebSocket_onmessage, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName))
+{
+ return IDLAttribute<JSWebSocket>::get<jsWebSocket_onmessageGetter, CastedThisErrorBehavior::Assert>(*lexicalGlobalObject, thisValue, attributeName);
+}
+
+static inline bool setJSWebSocket_onmessageSetter(JSGlobalObject& lexicalGlobalObject, JSWebSocket& thisObject, JSValue value)
+{
+ auto& vm = JSC::getVM(&lexicalGlobalObject);
+ setEventHandlerAttribute<JSEventListener>(thisObject.wrapped(), eventNames().messageEvent, value, thisObject);
+ vm.writeBarrier(&thisObject, value);
+ ensureStillAliveHere(value);
+
+ return true;
+}
+
+JSC_DEFINE_CUSTOM_SETTER(setJSWebSocket_onmessage, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, EncodedJSValue encodedValue, PropertyName attributeName))
+{
+ return IDLAttribute<JSWebSocket>::set<setJSWebSocket_onmessageSetter>(*lexicalGlobalObject, thisValue, encodedValue, attributeName);
+}
+
+static inline JSValue jsWebSocket_onerrorGetter(JSGlobalObject& lexicalGlobalObject, JSWebSocket& thisObject)
+{
+ UNUSED_PARAM(lexicalGlobalObject);
+ return eventHandlerAttribute(thisObject.wrapped(), eventNames().errorEvent, worldForDOMObject(thisObject));
+}
+
+JSC_DEFINE_CUSTOM_GETTER(jsWebSocket_onerror, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName))
+{
+ return IDLAttribute<JSWebSocket>::get<jsWebSocket_onerrorGetter, CastedThisErrorBehavior::Assert>(*lexicalGlobalObject, thisValue, attributeName);
+}
+
+static inline bool setJSWebSocket_onerrorSetter(JSGlobalObject& lexicalGlobalObject, JSWebSocket& thisObject, JSValue value)
+{
+ auto& vm = JSC::getVM(&lexicalGlobalObject);
+ setEventHandlerAttribute<JSEventListener>(thisObject.wrapped(), eventNames().errorEvent, value, thisObject);
+ vm.writeBarrier(&thisObject, value);
+ ensureStillAliveHere(value);
+
+ return true;
+}
+
+JSC_DEFINE_CUSTOM_SETTER(setJSWebSocket_onerror, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, EncodedJSValue encodedValue, PropertyName attributeName))
+{
+ return IDLAttribute<JSWebSocket>::set<setJSWebSocket_onerrorSetter>(*lexicalGlobalObject, thisValue, encodedValue, attributeName);
+}
+
+static inline JSValue jsWebSocket_oncloseGetter(JSGlobalObject& lexicalGlobalObject, JSWebSocket& thisObject)
+{
+ UNUSED_PARAM(lexicalGlobalObject);
+ return eventHandlerAttribute(thisObject.wrapped(), eventNames().closeEvent, worldForDOMObject(thisObject));
+}
+
+JSC_DEFINE_CUSTOM_GETTER(jsWebSocket_onclose, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName))
+{
+ return IDLAttribute<JSWebSocket>::get<jsWebSocket_oncloseGetter, CastedThisErrorBehavior::Assert>(*lexicalGlobalObject, thisValue, attributeName);
+}
+
+static inline bool setJSWebSocket_oncloseSetter(JSGlobalObject& lexicalGlobalObject, JSWebSocket& thisObject, JSValue value)
+{
+ auto& vm = JSC::getVM(&lexicalGlobalObject);
+ setEventHandlerAttribute<JSEventListener>(thisObject.wrapped(), eventNames().closeEvent, value, thisObject);
+ vm.writeBarrier(&thisObject, value);
+ ensureStillAliveHere(value);
+
+ return true;
+}
+
+JSC_DEFINE_CUSTOM_SETTER(setJSWebSocket_onclose, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, EncodedJSValue encodedValue, PropertyName attributeName))
+{
+ return IDLAttribute<JSWebSocket>::set<setJSWebSocket_oncloseSetter>(*lexicalGlobalObject, thisValue, encodedValue, attributeName);
+}
+
+static inline JSValue jsWebSocket_protocolGetter(JSGlobalObject& lexicalGlobalObject, JSWebSocket& thisObject)
+{
+ auto& vm = JSC::getVM(&lexicalGlobalObject);
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ auto& impl = thisObject.wrapped();
+ RELEASE_AND_RETURN(throwScope, (toJS<IDLNullable<IDLDOMString>>(lexicalGlobalObject, throwScope, impl.protocol())));
+}
+
+JSC_DEFINE_CUSTOM_GETTER(jsWebSocket_protocol, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName))
+{
+ return IDLAttribute<JSWebSocket>::get<jsWebSocket_protocolGetter, CastedThisErrorBehavior::Assert>(*lexicalGlobalObject, thisValue, attributeName);
+}
+
+static inline JSValue jsWebSocket_extensionsGetter(JSGlobalObject& lexicalGlobalObject, JSWebSocket& thisObject)
+{
+ auto& vm = JSC::getVM(&lexicalGlobalObject);
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ auto& impl = thisObject.wrapped();
+ RELEASE_AND_RETURN(throwScope, (toJS<IDLNullable<IDLDOMString>>(lexicalGlobalObject, throwScope, impl.extensions())));
+}
+
+JSC_DEFINE_CUSTOM_GETTER(jsWebSocket_extensions, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName))
+{
+ return IDLAttribute<JSWebSocket>::get<jsWebSocket_extensionsGetter, CastedThisErrorBehavior::Assert>(*lexicalGlobalObject, thisValue, attributeName);
+}
+
+static inline JSValue jsWebSocket_binaryTypeGetter(JSGlobalObject& lexicalGlobalObject, JSWebSocket& thisObject)
+{
+ auto& vm = JSC::getVM(&lexicalGlobalObject);
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ auto& impl = thisObject.wrapped();
+ RELEASE_AND_RETURN(throwScope, (toJS<IDLDOMString>(lexicalGlobalObject, throwScope, impl.binaryType())));
+}
+
+JSC_DEFINE_CUSTOM_GETTER(jsWebSocket_binaryType, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName))
+{
+ return IDLAttribute<JSWebSocket>::get<jsWebSocket_binaryTypeGetter, CastedThisErrorBehavior::Assert>(*lexicalGlobalObject, thisValue, attributeName);
+}
+
+static inline bool setJSWebSocket_binaryTypeSetter(JSGlobalObject& lexicalGlobalObject, JSWebSocket& thisObject, JSValue value)
+{
+ auto& vm = JSC::getVM(&lexicalGlobalObject);
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ auto& impl = thisObject.wrapped();
+ auto nativeValue = convert<IDLDOMString>(lexicalGlobalObject, value);
+ RETURN_IF_EXCEPTION(throwScope, false);
+ invokeFunctorPropagatingExceptionIfNecessary(lexicalGlobalObject, throwScope, [&] {
+ return impl.setBinaryType(WTFMove(nativeValue));
+ });
+ return true;
+}
+
+JSC_DEFINE_CUSTOM_SETTER(setJSWebSocket_binaryType, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, EncodedJSValue encodedValue, PropertyName attributeName))
+{
+ return IDLAttribute<JSWebSocket>::set<setJSWebSocket_binaryTypeSetter>(*lexicalGlobalObject, thisValue, encodedValue, attributeName);
+}
+
+static inline JSC::EncodedJSValue jsWebSocketPrototypeFunction_send1Body(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSWebSocket>::ClassParameter castedThis)
+{
+ auto& vm = JSC::getVM(lexicalGlobalObject);
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ UNUSED_PARAM(throwScope);
+ UNUSED_PARAM(callFrame);
+ auto& impl = castedThis->wrapped();
+ EnsureStillAliveScope argument0 = callFrame->uncheckedArgument(0);
+ auto data = convert<IDLArrayBuffer>(*lexicalGlobalObject, argument0.value(), [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentTypeError(lexicalGlobalObject, scope, 0, "data", "WebSocket", "send", "ArrayBuffer"); });
+ RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
+ RELEASE_AND_RETURN(throwScope, JSValue::encode(toJS<IDLUndefined>(*lexicalGlobalObject, throwScope, [&]() -> decltype(auto) { return impl.send(*data); })));
+}
+
+static inline JSC::EncodedJSValue jsWebSocketPrototypeFunction_send2Body(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSWebSocket>::ClassParameter castedThis)
+{
+ auto& vm = JSC::getVM(lexicalGlobalObject);
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ UNUSED_PARAM(throwScope);
+ UNUSED_PARAM(callFrame);
+ auto& impl = castedThis->wrapped();
+ EnsureStillAliveScope argument0 = callFrame->uncheckedArgument(0);
+ auto data = convert<IDLArrayBufferView>(*lexicalGlobalObject, argument0.value(), [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentTypeError(lexicalGlobalObject, scope, 0, "data", "WebSocket", "send", "ArrayBufferView"); });
+ RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
+ RELEASE_AND_RETURN(throwScope, JSValue::encode(toJS<IDLUndefined>(*lexicalGlobalObject, throwScope, [&]() -> decltype(auto) { return impl.send(data.releaseNonNull()); })));
+}
+
+// static inline JSC::EncodedJSValue jsWebSocketPrototypeFunction_send3Body(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSWebSocket>::ClassParameter castedThis)
+// {
+// auto& vm = JSC::getVM(lexicalGlobalObject);
+// auto throwScope = DECLARE_THROW_SCOPE(vm);
+// UNUSED_PARAM(throwScope);
+// UNUSED_PARAM(callFrame);
+// auto& impl = castedThis->wrapped();
+// EnsureStillAliveScope argument0 = callFrame->uncheckedArgument(0);
+// auto data = convert<IDLInterface<Blob>>(*lexicalGlobalObject, argument0.value(), [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentTypeError(lexicalGlobalObject, scope, 0, "data", "WebSocket", "send", "Blob"); });
+// RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
+// RELEASE_AND_RETURN(throwScope, JSValue::encode(toJS<IDLUndefined>(*lexicalGlobalObject, throwScope, [&]() -> decltype(auto) { return impl.send(*data); })));
+// }
+
+static inline JSC::EncodedJSValue jsWebSocketPrototypeFunction_send4Body(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSWebSocket>::ClassParameter castedThis)
+{
+ auto& vm = JSC::getVM(lexicalGlobalObject);
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ UNUSED_PARAM(throwScope);
+ UNUSED_PARAM(callFrame);
+ auto& impl = castedThis->wrapped();
+ EnsureStillAliveScope argument0 = callFrame->uncheckedArgument(0);
+ auto data = convert<IDLUSVString>(*lexicalGlobalObject, argument0.value());
+ RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
+ RELEASE_AND_RETURN(throwScope, JSValue::encode(toJS<IDLUndefined>(*lexicalGlobalObject, throwScope, [&]() -> decltype(auto) { return impl.send(WTFMove(data)); })));
+}
+
+static inline JSC::EncodedJSValue jsWebSocketPrototypeFunction_sendOverloadDispatcher(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSWebSocket>::ClassParameter castedThis)
+{
+ auto& vm = JSC::getVM(lexicalGlobalObject);
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ UNUSED_PARAM(throwScope);
+ UNUSED_PARAM(callFrame);
+ size_t argsCount = std::min<size_t>(1, callFrame->argumentCount());
+ if (argsCount == 1) {
+ JSValue distinguishingArg = callFrame->uncheckedArgument(0);
+ if (distinguishingArg.isObject() && asObject(distinguishingArg)->inherits<JSArrayBuffer>())
+ RELEASE_AND_RETURN(throwScope, (jsWebSocketPrototypeFunction_send1Body(lexicalGlobalObject, callFrame, castedThis)));
+ if (distinguishingArg.isObject() && asObject(distinguishingArg)->inherits<JSArrayBufferView>())
+ RELEASE_AND_RETURN(throwScope, (jsWebSocketPrototypeFunction_send2Body(lexicalGlobalObject, callFrame, castedThis)));
+ // if (distinguishingArg.isObject() && asObject(distinguishingArg)->inherits<JSBlob>())
+ // RELEASE_AND_RETURN(throwScope, (jsWebSocketPrototypeFunction_send3Body(lexicalGlobalObject, callFrame, castedThis)));
+ RELEASE_AND_RETURN(throwScope, (jsWebSocketPrototypeFunction_send4Body(lexicalGlobalObject, callFrame, castedThis)));
+ }
+ return argsCount < 1 ? throwVMError(lexicalGlobalObject, throwScope, createNotEnoughArgumentsError(lexicalGlobalObject)) : throwVMTypeError(lexicalGlobalObject, throwScope);
+}
+
+JSC_DEFINE_HOST_FUNCTION(jsWebSocketPrototypeFunction_send, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame))
+{
+ return IDLOperation<JSWebSocket>::call<jsWebSocketPrototypeFunction_sendOverloadDispatcher>(*lexicalGlobalObject, *callFrame, "send");
+}
+
+static inline JSC::EncodedJSValue jsWebSocketPrototypeFunction_closeBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSWebSocket>::ClassParameter castedThis)
+{
+ auto& vm = JSC::getVM(lexicalGlobalObject);
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ UNUSED_PARAM(throwScope);
+ UNUSED_PARAM(callFrame);
+ auto& impl = castedThis->wrapped();
+ EnsureStillAliveScope argument0 = callFrame->argument(0);
+ auto code = argument0.value().isUndefined() ? std::optional<Converter<IDLClampAdaptor<IDLUnsignedShort>>::ReturnType>() : std::optional<Converter<IDLClampAdaptor<IDLUnsignedShort>>::ReturnType>(convert<IDLClampAdaptor<IDLUnsignedShort>>(*lexicalGlobalObject, argument0.value()));
+ RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
+ EnsureStillAliveScope argument1 = callFrame->argument(1);
+ auto reason = argument1.value().isUndefined() ? String() : convert<IDLUSVString>(*lexicalGlobalObject, argument1.value());
+ RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
+ RELEASE_AND_RETURN(throwScope, JSValue::encode(toJS<IDLUndefined>(*lexicalGlobalObject, throwScope, [&]() -> decltype(auto) { return impl.close(WTFMove(code), WTFMove(reason)); })));
+}
+
+JSC_DEFINE_HOST_FUNCTION(jsWebSocketPrototypeFunction_close, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame))
+{
+ return IDLOperation<JSWebSocket>::call<jsWebSocketPrototypeFunction_closeBody>(*lexicalGlobalObject, *callFrame, "close");
+}
+
+JSC::GCClient::IsoSubspace* JSWebSocket::subspaceForImpl(JSC::VM& vm)
+{
+ return WebCore::subspaceForImpl<JSWebSocket, UseCustomHeapCellType::No>(
+ vm,
+ [](auto& spaces) { return spaces.m_clientSubspaceForWebSocket.get(); },
+ [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForWebSocket = WTFMove(space); },
+ [](auto& spaces) { return spaces.m_subspaceForWebSocket.get(); },
+ [](auto& spaces, auto&& space) { spaces.m_subspaceForWebSocket = WTFMove(space); });
+}
+
+void JSWebSocket::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer)
+{
+ auto* thisObject = jsCast<JSWebSocket*>(cell);
+ analyzer.setWrappedObjectForCell(cell, &thisObject->wrapped());
+ if (thisObject->scriptExecutionContext())
+ analyzer.setLabelForCell(cell, "url " + thisObject->scriptExecutionContext()->url().string());
+ Base::analyzeHeap(cell, analyzer);
+}
+
+bool JSWebSocketOwner::isReachableFromOpaqueRoots(JSC::Handle<JSC::Unknown> handle, void*, AbstractSlotVisitor& visitor, const char** reason)
+{
+ auto* jsWebSocket = jsCast<JSWebSocket*>(handle.slot()->asCell());
+ auto& wrapped = jsWebSocket->wrapped();
+ // if (!wrapped.isContextStopped() && wrapped.hasPendingActivity()) {
+ // if (UNLIKELY(reason))
+ // *reason = "ActiveDOMObject with pending activity";
+ // return true;
+ // }
+ if (jsWebSocket->wrapped().isFiringEventListeners()) {
+ if (UNLIKELY(reason))
+ *reason = "EventTarget firing event listeners";
+ return true;
+ }
+ UNUSED_PARAM(visitor);
+ UNUSED_PARAM(reason);
+ return false;
+}
+
+void JSWebSocketOwner::finalize(JSC::Handle<JSC::Unknown> handle, void* context)
+{
+ auto* jsWebSocket = static_cast<JSWebSocket*>(handle.slot()->asCell());
+ auto& world = *static_cast<DOMWrapperWorld*>(context);
+ uncacheWrapper(world, &jsWebSocket->wrapped(), jsWebSocket);
+}
+
+#if ENABLE(BINDING_INTEGRITY)
+#if PLATFORM(WIN)
+#pragma warning(disable : 4483)
+extern "C" {
+extern void (*const __identifier("??_7WebSocket@WebCore@@6B@")[])();
+}
+#else
+extern "C" {
+extern void* _ZTVN7WebCore9WebSocketE[];
+}
+#endif
+#endif
+
+JSC::JSValue toJSNewlyCreated(JSC::JSGlobalObject*, JSDOMGlobalObject* globalObject, Ref<WebSocket>&& impl)
+{
+
+ if constexpr (std::is_polymorphic_v<WebSocket>) {
+#if ENABLE(BINDING_INTEGRITY)
+ const void* actualVTablePointer = getVTablePointer(impl.ptr());
+#if PLATFORM(WIN)
+ void* expectedVTablePointer = __identifier("??_7WebSocket@WebCore@@6B@");
+#else
+ void* expectedVTablePointer = &_ZTVN7WebCore9WebSocketE[2];
+#endif
+
+ // If you hit this assertion you either have a use after free bug, or
+ // WebSocket has subclasses. If WebSocket has subclasses that get passed
+ // to toJS() we currently require WebSocket you to opt out of binding hardening
+ // by adding the SkipVTableValidation attribute to the interface IDL definition
+ RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer);
+#endif
+ }
+ return createWrapper<WebSocket>(globalObject, WTFMove(impl));
+}
+
+JSC::JSValue toJS(JSC::JSGlobalObject* lexicalGlobalObject, JSDOMGlobalObject* globalObject, WebSocket& impl)
+{
+ return wrap(lexicalGlobalObject, globalObject, impl);
+}
+
+WebSocket* JSWebSocket::toWrapped(JSC::VM&, JSC::JSValue value)
+{
+ if (auto* wrapper = jsDynamicCast<JSWebSocket*>(value))
+ return &wrapper->wrapped();
+ return nullptr;
+}
+
+}
diff --git a/src/javascript/jsc/bindings/webcore/JSWebSocket.dep b/src/javascript/jsc/bindings/webcore/JSWebSocket.dep
new file mode 100644
index 000000000..69bc1775b
--- /dev/null
+++ b/src/javascript/jsc/bindings/webcore/JSWebSocket.dep
@@ -0,0 +1,2 @@
+JSWebSocket.h : EventTarget.idl
+EventTarget.idl :
diff --git a/src/javascript/jsc/bindings/webcore/JSWebSocket.h b/src/javascript/jsc/bindings/webcore/JSWebSocket.h
new file mode 100644
index 000000000..ae99a9887
--- /dev/null
+++ b/src/javascript/jsc/bindings/webcore/JSWebSocket.h
@@ -0,0 +1,98 @@
+/*
+ This file is part of the WebKit open source project.
+ This file has been generated by generate-bindings.pl. DO NOT MODIFY!
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#pragma once
+
+#include "JSDOMWrapper.h"
+#include "JSEventTarget.h"
+#include "WebSocket.h"
+#include <wtf/NeverDestroyed.h>
+
+namespace WebCore {
+
+class JSWebSocket : public JSEventTarget {
+public:
+ using Base = JSEventTarget;
+ using DOMWrapped = WebSocket;
+ static JSWebSocket* create(JSC::Structure* structure, JSDOMGlobalObject* globalObject, Ref<WebSocket>&& impl)
+ {
+ JSWebSocket* ptr = new (NotNull, JSC::allocateCell<JSWebSocket>(globalObject->vm())) JSWebSocket(structure, *globalObject, WTFMove(impl));
+ ptr->finishCreation(globalObject->vm());
+ return ptr;
+ }
+
+ static JSC::JSObject* createPrototype(JSC::VM&, JSDOMGlobalObject&);
+ static JSC::JSObject* prototype(JSC::VM&, JSDOMGlobalObject&);
+ static WebSocket* toWrapped(JSC::VM&, JSC::JSValue);
+
+ DECLARE_INFO;
+
+ static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
+ {
+ return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info(), JSC::NonArray);
+ }
+
+ static JSC::JSValue getConstructor(JSC::VM&, const JSC::JSGlobalObject*);
+ template<typename, JSC::SubspaceAccess mode> static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
+ {
+ if constexpr (mode == JSC::SubspaceAccess::Concurrently)
+ return nullptr;
+ return subspaceForImpl(vm);
+ }
+ static JSC::GCClient::IsoSubspace* subspaceForImpl(JSC::VM& vm);
+ static void analyzeHeap(JSCell*, JSC::HeapAnalyzer&);
+ WebSocket& wrapped() const
+ {
+ return static_cast<WebSocket&>(Base::wrapped());
+ }
+protected:
+ JSWebSocket(JSC::Structure*, JSDOMGlobalObject&, Ref<WebSocket>&&);
+
+ void finishCreation(JSC::VM&);
+};
+
+class JSWebSocketOwner final : public JSC::WeakHandleOwner {
+public:
+ bool isReachableFromOpaqueRoots(JSC::Handle<JSC::Unknown>, void* context, JSC::AbstractSlotVisitor&, const char**) final;
+ void finalize(JSC::Handle<JSC::Unknown>, void* context) final;
+};
+
+inline JSC::WeakHandleOwner* wrapperOwner(DOMWrapperWorld&, WebSocket*)
+{
+ static NeverDestroyed<JSWebSocketOwner> owner;
+ return &owner.get();
+}
+
+inline void* wrapperKey(WebSocket* wrappableObject)
+{
+ return wrappableObject;
+}
+
+JSC::JSValue toJS(JSC::JSGlobalObject*, JSDOMGlobalObject*, WebSocket&);
+inline JSC::JSValue toJS(JSC::JSGlobalObject* lexicalGlobalObject, JSDOMGlobalObject* globalObject, WebSocket* impl) { return impl ? toJS(lexicalGlobalObject, globalObject, *impl) : JSC::jsNull(); }
+JSC::JSValue toJSNewlyCreated(JSC::JSGlobalObject*, JSDOMGlobalObject*, Ref<WebSocket>&&);
+inline JSC::JSValue toJSNewlyCreated(JSC::JSGlobalObject* lexicalGlobalObject, JSDOMGlobalObject* globalObject, RefPtr<WebSocket>&& impl) { return impl ? toJSNewlyCreated(lexicalGlobalObject, globalObject, impl.releaseNonNull()) : JSC::jsNull(); }
+
+template<> struct JSDOMWrapperConverterTraits<WebSocket> {
+ using WrapperClass = JSWebSocket;
+ using ToWrappedReturnType = WebSocket*;
+};
+
+} // namespace WebCore
diff --git a/src/javascript/jsc/bindings/webcore/WebSocket.cpp b/src/javascript/jsc/bindings/webcore/WebSocket.cpp
new file mode 100644
index 000000000..6d03a52b7
--- /dev/null
+++ b/src/javascript/jsc/bindings/webcore/WebSocket.cpp
@@ -0,0 +1,894 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ * Copyright (C) 2015-2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "WebSocket.h"
+
+// #include "Blob.h"
+#include "CloseEvent.h"
+// #include "ContentSecurityPolicy.h"
+// #include "DOMWindow.h"
+// #include "Document.h"
+#include "Event.h"
+#include "EventListener.h"
+#include "EventNames.h"
+// #include "Frame.h"
+// #include "FrameLoader.h"
+// #include "FrameLoaderClient.h"
+// #include "InspectorInstrumentation.h"
+// #include "Logging.h"
+#include "MessageEvent.h"
+// #include "MixedContentChecker.h"
+// #include "ResourceLoadObserver.h"
+// #include "ScriptController.h"
+#include "ScriptExecutionContext.h"
+// #include "SecurityOrigin.h"
+// #include "SocketProvider.h"
+// #include "ThreadableWebSocketChannel.h"
+// #include "WebSocketChannel.h"
+// #include "WorkerGlobalScope.h"
+// #include "WorkerLoaderProxy.h"
+// #include "WorkerThread.h"
+#include <JavaScriptCore/ArrayBuffer.h>
+#include <JavaScriptCore/ArrayBufferView.h>
+#include <JavaScriptCore/ScriptCallStack.h>
+#include <wtf/HashSet.h>
+#include <wtf/HexNumber.h>
+// #include <wtf/IsoMallocInlines.h>
+#include <wtf/NeverDestroyed.h>
+// #include <wtf/RunLoop.h>
+#include <wtf/StdLibExtras.h>
+#include <wtf/text/CString.h>
+#include <wtf/text/StringBuilder.h>
+
+// #if USE(WEB_THREAD)
+// #include "WebCoreThreadRun.h"
+// #endif
+namespace WebCore {
+using WebSocketChannel = WebSocketStream;
+using ThreadableWebSocketChannel = WebSocketStream;
+using WebSocketChannelClient = WebSocketStream;
+WTF_MAKE_ISO_ALLOCATED_IMPL(WebSocket);
+
+Lock WebSocket::s_allActiveWebSocketsLock;
+
+const size_t maxReasonSizeInBytes = 123;
+
+static inline bool isValidProtocolCharacter(UChar character)
+{
+ // Hybi-10 says "(Subprotocol string must consist of) characters in the range U+0021 to U+007E not including
+ // separator characters as defined in [RFC2616]."
+ const UChar minimumProtocolCharacter = '!'; // U+0021.
+ const UChar maximumProtocolCharacter = '~'; // U+007E.
+ return character >= minimumProtocolCharacter && character <= maximumProtocolCharacter
+ && character != '"' && character != '(' && character != ')' && character != ',' && character != '/'
+ && !(character >= ':' && character <= '@') // U+003A - U+0040 (':', ';', '<', '=', '>', '?', '@').
+ && !(character >= '[' && character <= ']') // U+005B - U+005D ('[', '\\', ']').
+ && character != '{' && character != '}';
+}
+
+static bool isValidProtocolString(StringView protocol)
+{
+ if (protocol.isEmpty())
+ return false;
+ for (auto codeUnit : protocol.codeUnits()) {
+ if (!isValidProtocolCharacter(codeUnit))
+ return false;
+ }
+ return true;
+}
+
+static String encodeProtocolString(const String& protocol)
+{
+ StringBuilder builder;
+ for (size_t i = 0; i < protocol.length(); i++) {
+ if (protocol[i] < 0x20 || protocol[i] > 0x7E)
+ builder.append("\\u", hex(protocol[i], 4));
+ else if (protocol[i] == 0x5c)
+ builder.append("\\\\");
+ else
+ builder.append(protocol[i]);
+ }
+ return builder.toString();
+}
+
+static String joinStrings(const Vector<String>& strings, const char* separator)
+{
+ StringBuilder builder;
+ for (size_t i = 0; i < strings.size(); ++i) {
+ if (i)
+ builder.append(separator);
+ builder.append(strings[i]);
+ }
+ return builder.toString();
+}
+
+static unsigned saturateAdd(unsigned a, unsigned b)
+{
+ if (std::numeric_limits<unsigned>::max() - a < b)
+ return std::numeric_limits<unsigned>::max();
+ return a + b;
+}
+
+ASCIILiteral WebSocket::subprotocolSeparator()
+{
+ return ", "_s;
+}
+
+WebSocket::WebSocket(ScriptExecutionContext& context)
+ : ContextDestructionObserver(&context)
+ , m_subprotocol(emptyString())
+ , m_extensions(emptyString())
+ , m_handshake(url, )
+
+{
+ Locker locker { allActiveWebSocketsLock() };
+ allActiveWebSockets().add(this);
+}
+
+WebSocket::~WebSocket()
+{
+ {
+ Locker locker { allActiveWebSocketsLock() };
+ allActiveWebSockets().remove(this);
+ }
+
+ if (m_channel)
+ m_channel->disconnect();
+}
+
+ExceptionOr<Ref<WebSocket>> WebSocket::create(ScriptExecutionContext& context, const String& url)
+{
+ return create(context, url, Vector<String> {});
+}
+
+ExceptionOr<Ref<WebSocket>> WebSocket::create(ScriptExecutionContext& context, const String& url, const Vector<String>& protocols)
+{
+ if (url.isNull())
+ return Exception { SyntaxError };
+
+ auto socket = adoptRef(*new WebSocket(context, url));
+ // socket->suspendIfNeeded();
+
+ auto result = socket->connect(url.string(), protocols);
+ // auto result = socket->connect(url, protocols);
+
+ if (result.hasException())
+ return result.releaseException();
+
+ return socket;
+}
+
+ExceptionOr<Ref<WebSocket>> WebSocket::create(ScriptExecutionContext& context, const String& url, const String& protocol)
+{
+ return create(context, url, Vector<String> { 1, protocol });
+}
+
+HashSet<WebSocket*>& WebSocket::allActiveWebSockets()
+{
+ static NeverDestroyed<HashSet<WebSocket*>> activeWebSockets;
+ return activeWebSockets;
+}
+
+Lock& WebSocket::allActiveWebSocketsLock()
+{
+ return s_allActiveWebSocketsLock;
+}
+
+ExceptionOr<void> WebSocket::connect(const String& url)
+{
+ return connect(url, Vector<String> {});
+}
+
+ExceptionOr<void> WebSocket::connect(const String& url, const String& protocol)
+{
+ return connect(url, Vector<String> { 1, protocol });
+}
+
+void WebSocket::failAsynchronously()
+{
+ // queueTaskKeepingObjectAlive(*this, TaskSource::WebSocket, [this] {
+ // We must block this connection. Instead of throwing an exception, we indicate this
+ // using the error event. But since this code executes as part of the WebSocket's
+ // constructor, we have to wait until the constructor has completed before firing the
+ // event; otherwise, users can't connect to the event.
+ this->dispatchErrorEventIfNeeded();
+ // this->();
+ // this->stop();
+ // });
+}
+
+static String resourceName(const URL& url)
+{
+ auto path = url.path();
+ auto result = makeString(
+ path,
+ path.isEmpty() ? "/" : "",
+ url.queryWithLeadingQuestionMark());
+ ASSERT(!result.isEmpty());
+ ASSERT(!result.contains(' '));
+ return result;
+}
+
+static String hostName(const URL& url, bool secure)
+{
+ ASSERT(url.protocolIs("wss") == secure);
+ if (url.port() && ((!secure && url.port().value() != 80) || (secure && url.port().value() != 443)))
+ return makeString(asASCIILowercase(url.host()), ':', url.port().value());
+ return url.host().convertToASCIILowercase();
+}
+
+ExceptionOr<void> WebSocket::connect(const String& url, const Vector<String>& protocols)
+{
+ LOG(Network, "WebSocket %p connect() url='%s'", this, url.utf8().data());
+ m_url = URL { url };
+
+ ASSERT(scriptExecutionContext());
+ auto& context = *scriptExecutionContext();
+
+ if (!m_url.isValid()) {
+ // context.addConsoleMessage(MessageSource::JS, MessageLevel::Error, );
+ m_state = CLOSED;
+ return Exception { SyntaxError, "Invalid url for WebSocket " + m_url.stringCenterEllipsizedToLength() };
+ }
+
+ bool is_secure = m_url.protocolIs("wss");
+
+ if (!m_url.protocolIs("ws") && !is_secure) {
+ // context.addConsoleMessage(MessageSource::JS, MessageLevel::Error, );
+ m_state = CLOSED;
+ return Exception { SyntaxError, "Wrong url scheme for WebSocket " + m_url.stringCenterEllipsizedToLength() };
+ }
+ if (m_url.hasFragmentIdentifier()) {
+ // context.addConsoleMessage(MessageSource::JS, MessageLevel::Error, );
+ m_state = CLOSED;
+ return Exception { SyntaxError, "URL has fragment component " + m_url.stringCenterEllipsizedToLength() };
+ }
+
+ // ASSERT(context.contentSecurityPolicy());
+ // auto& contentSecurityPolicy = *context.contentSecurityPolicy();
+
+ // contentSecurityPolicy.upgradeInsecureRequestIfNeeded(m_url, ContentSecurityPolicy::InsecureRequestType::Load);
+
+ // if (!portAllowed(m_url)) {
+ // String message;
+ // if (m_url.port())
+ // message = makeString("WebSocket port ", m_url.port().value(), " blocked");
+ // else
+ // message = "WebSocket without port blocked"_s;
+ // context.addConsoleMessage(MessageSource::JS, MessageLevel::Error, message);
+ // failAsynchronously();
+ // return {};
+ // }
+
+ // FIXME: Convert this to check the isolated world's Content Security Policy once webkit.org/b/104520 is solved.
+ // if (!context.shouldBypassMainWorldContentSecurityPolicy() && !contentSecurityPolicy.allowConnectToSource(m_url)) {
+ // m_state = CLOSED;
+
+ // // FIXME: Should this be throwing an exception?
+ // return Exception { SecurityError };
+ // }
+
+ // FIXME: There is a disagreement about restriction of subprotocols between WebSocket API and hybi-10 protocol
+ // draft. The former simply says "only characters in the range U+0021 to U+007E are allowed," while the latter
+ // imposes a stricter rule: "the elements MUST be non-empty strings with characters as defined in [RFC2616],
+ // and MUST all be unique strings."
+ //
+ // Here, we throw SyntaxError if the given protocols do not meet the latter criteria. This behavior does not
+ // comply with WebSocket API specification, but it seems to be the only reasonable way to handle this conflict.
+ for (auto& protocol : protocols) {
+ if (!isValidProtocolString(protocol)) {
+ // context.addConsoleMessage(MessageSource::JS, MessageLevel::Error, );
+ m_state = CLOSED;
+ return Exception { SyntaxError, "Wrong protocol for WebSocket '" + encodeProtocolString(protocol) + "'" };
+ }
+ }
+ HashSet<String> visited;
+ for (auto& protocol : protocols) {
+ if (!visited.add(protocol).isNewEntry) {
+ // context.addConsoleMessage(MessageSource::JS, MessageLevel::Error, );
+ m_state = CLOSED;
+ return Exception { SyntaxError, "WebSocket protocols contain duplicates: '" + encodeProtocolString(protocol) + "'" };
+ }
+ }
+
+ // RunLoop::main().dispatch([targetURL = m_url.isolatedCopy(), mainFrameURL = context.url().isolatedCopy()]() {
+ // ResourceLoadObserver::shared().logWebSocketLoading(targetURL, mainFrameURL);
+ // });
+
+ // if (is<Document>(context)) {
+ // Document& document = downcast<Document>(context);
+ // RefPtr<Frame> frame = document.frame();
+ // // FIXME: make the mixed content check equivalent to the non-document mixed content check currently in WorkerThreadableWebSocketChannel::Bridge::connect()
+ // if (!frame || !MixedContentChecker::canRunInsecureContent(*frame, document.securityOrigin(), m_url)) {
+ // failAsynchronously();
+ // return { };
+ // }
+ // }
+
+ String protocolString;
+ if (!protocols.isEmpty())
+ protocolString = joinStrings(protocols, subprotocolSeparator());
+
+ ZigString host = Zig::toZigString(m_url.host());
+ auto resource = resourceName(m_url);
+ ZigString path = Zig::toZigString(resource);
+ ZigString clientProtocolString = Zig::toZigString(protocolString);
+ uint16_t port = m_url.port();
+
+ if (is_secure) {
+ us_socket_context_t* ctx = scriptExecutionContext->webSocketContext<true>();
+ RELEASE_ASSERT(ctx);
+ this->m_upgradeClient = Bun_SecureWebSocketUpgradeClient__connect(scriptExecutionContext->jsGlobalObject(), ctx, this, &host, port, &path, &clientProtocolString);
+ } else {
+ us_socket_context_t* ctx = scriptExecutionContext->webSocketContext<false>();
+ RELEASE_ASSERT(ctx);
+ this->m_upgradeClient = Bun_WebSocketUpgradeClient__connect(scriptExecutionContext->jsGlobalObject(), ctx, this, &host, port, &path, &clientProtocolString);
+ }
+
+ if (this->m_upgradeClient == nullptr) {
+ // context.addConsoleMessage(MessageSource::JS, MessageLevel::Error, );
+ m_state = CLOSED;
+ return Exception { SyntaxError, "WebSocket connection failed" };
+ }
+
+ m_state = CONNECTING;
+
+ // #if ENABLE(INTELLIGENT_TRACKING_PREVENTION)
+ // auto reportRegistrableDomain = [domain = RegistrableDomain(m_url).isolatedCopy()](auto& context) mutable {
+ // if (auto* frame = downcast<Document>(context).frame())
+ // frame->loader().client().didLoadFromRegistrableDomain(WTFMove(domain));
+ // };
+ // if (is<Document>(context))
+ // reportRegistrableDomain(context);
+ // else
+ // downcast<WorkerGlobalScope>(context).thread().workerLoaderProxy().postTaskToLoader(WTFMove(reportRegistrableDomain));
+ // #endif
+
+ // m_pendingActivity = makePendingActivity(*this);
+
+ return {};
+}
+
+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) {
+ size_t payloadSize = utf8.length();
+ m_bufferedAmountAfterClose = saturateAdd(m_bufferedAmountAfterClose, payloadSize);
+ m_bufferedAmountAfterClose = saturateAdd(m_bufferedAmountAfterClose, getFramingOverhead(payloadSize));
+ return {};
+ }
+ // FIXME: WebSocketChannel also has a m_bufferedAmount. Remove that one. This one is the correct one accessed by JS.
+ m_bufferedAmount = saturateAdd(m_bufferedAmount, utf8.length());
+ ASSERT(m_channel);
+ m_channel->send(WTFMove(utf8));
+ return {};
+}
+
+ExceptionOr<void> WebSocket::send(ArrayBuffer& binaryData)
+{
+ LOG(Network, "WebSocket %p send() Sending ArrayBuffer %p", this, &binaryData);
+ if (m_state == CONNECTING)
+ return Exception { InvalidStateError };
+ if (m_state == CLOSING || m_state == CLOSED) {
+ unsigned payloadSize = binaryData.byteLength();
+ m_bufferedAmountAfterClose = saturateAdd(m_bufferedAmountAfterClose, payloadSize);
+ m_bufferedAmountAfterClose = saturateAdd(m_bufferedAmountAfterClose, getFramingOverhead(payloadSize));
+ return {};
+ }
+ m_bufferedAmount = saturateAdd(m_bufferedAmount, binaryData.byteLength());
+ ASSERT(m_channel);
+ m_channel->send(binaryData, 0, binaryData.byteLength());
+ return {};
+}
+
+ExceptionOr<void> WebSocket::send(ArrayBufferView& arrayBufferView)
+{
+ LOG(Network, "WebSocket %p send() Sending ArrayBufferView %p", this, &arrayBufferView);
+
+ if (m_state == CONNECTING)
+ return Exception { InvalidStateError };
+ if (m_state == CLOSING || m_state == CLOSED) {
+ unsigned payloadSize = arrayBufferView.byteLength();
+ m_bufferedAmountAfterClose = saturateAdd(m_bufferedAmountAfterClose, payloadSize);
+ m_bufferedAmountAfterClose = saturateAdd(m_bufferedAmountAfterClose, getFramingOverhead(payloadSize));
+ return {};
+ }
+ m_bufferedAmount = saturateAdd(m_bufferedAmount, arrayBufferView.byteLength());
+ ASSERT(m_channel);
+ m_channel->send(*arrayBufferView.unsharedBuffer(), arrayBufferView.byteOffset(), arrayBufferView.byteLength());
+ return {};
+}
+
+// ExceptionOr<void> WebSocket::send(Blob& binaryData)
+// {
+// LOG(Network, "WebSocket %p send() Sending Blob '%s'", this, binaryData.url().stringCenterEllipsizedToLength().utf8().data());
+// if (m_state == CONNECTING)
+// return Exception { InvalidStateError };
+// if (m_state == CLOSING || m_state == CLOSED) {
+// unsigned payloadSize = static_cast<unsigned>(binaryData.size());
+// m_bufferedAmountAfterClose = saturateAdd(m_bufferedAmountAfterClose, payloadSize);
+// m_bufferedAmountAfterClose = saturateAdd(m_bufferedAmountAfterClose, getFramingOverhead(payloadSize));
+// return {};
+// }
+// m_bufferedAmount = saturateAdd(m_bufferedAmount, binaryData.size());
+// ASSERT(m_channel);
+// m_channel->send(binaryData);
+// return {};
+// }
+
+ExceptionOr<void> WebSocket::close(std::optional<unsigned short> optionalCode, const String& reason)
+{
+ int code = optionalCode ? optionalCode.value() : static_cast<int>(WebSocketChannel::CloseEventCodeNotSpecified);
+ if (code == WebSocketChannel::CloseEventCodeNotSpecified)
+ LOG(Network, "WebSocket %p close() without code and reason", this);
+ else {
+ 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 };
+ CString utf8 = reason.utf8(StrictConversionReplacingUnpairedSurrogatesWithFFFD);
+ if (utf8.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 };
+ }
+ }
+
+ if (m_state == CLOSING || m_state == CLOSED)
+ return {};
+ if (m_state == CONNECTING) {
+ m_state = CLOSING;
+ m_channel->fail("WebSocket is closed before the connection is established."_s);
+ return {};
+ }
+ m_state = CLOSING;
+ if (m_channel)
+ m_channel->close(code, reason);
+ return {};
+}
+
+WebSocketChannelClient* WebSocket::channel() const
+{
+ return m_channel;
+}
+
+const URL& WebSocket::url() const
+{
+ return m_url;
+}
+
+WebSocket::State WebSocket::readyState() const
+{
+ return m_state;
+}
+
+unsigned WebSocket::bufferedAmount() const
+{
+ return saturateAdd(m_bufferedAmount, m_bufferedAmountAfterClose);
+}
+
+String WebSocket::protocol() const
+{
+ return m_subprotocol;
+}
+
+String WebSocket::extensions() const
+{
+ return m_extensions;
+}
+
+String WebSocket::binaryType() const
+{
+ switch (m_binaryType) {
+ // case BinaryType::Blob:
+ // return "blob"_s;
+ case BinaryType::ArrayBuffer:
+ return "arraybuffer"_s;
+ }
+ ASSERT_NOT_REACHED();
+ return String();
+}
+
+ExceptionOr<void> WebSocket::setBinaryType(const String& binaryType)
+{
+ // if (binaryType == "blob") {
+ // m_binaryType = BinaryType::Blob;
+ // return {};
+ // }
+ if (binaryType == "arraybuffer") {
+ m_binaryType = BinaryType::ArrayBuffer;
+ return {};
+ }
+ // scriptExecutionContext()->addConsoleMessage(MessageSource::JS, MessageLevel::Error, "'" + binaryType + "' is not a valid value for binaryType; binaryType remains unchanged.");
+ return Exception { SyntaxError, "'" + binaryType + "' is not a valid value for binaryType; binaryType remains unchanged." };
+}
+
+EventTargetInterface WebSocket::eventTargetInterface() const
+{
+ return WebSocketEventTargetInterfaceType;
+}
+
+ScriptExecutionContext* WebSocket::scriptExecutionContext() const
+{
+ return ContextDestructionObserver::scriptExecutionContext();
+}
+
+// void WebSocket::contextDestroyed()
+// {
+// LOG(Network, "WebSocket %p contextDestroyed()", this);
+// ASSERT(!m_channel);
+// ASSERT(m_state == CLOSED);
+// // ActiveDOMObject::contextDestroyed();
+// }
+
+// void WebSocket::suspend(ReasonForSuspension reason)
+// {
+// // if (!m_channel)
+// // return;
+
+// // if (reason == ReasonForSuspension::BackForwardCache) {
+// // // This will cause didClose() to be called.
+// // m_channel->fail("WebSocket is closed due to suspension."_s);
+// // return;
+// // }
+
+// // m_channel->suspend();
+// }
+
+// void WebSocket::resume()
+// {
+// // if (m_channel)
+// // m_channel->resume();
+// }
+
+// void WebSocket::stop()
+// {
+// if (m_channel)
+// m_channel->disconnect();
+// m_channel = nullptr;
+// m_state = CLOSED;
+// // ActiveDOMObject::stop();
+// // m_pendingActivity = nullptr;
+// }
+
+// const char* WebSocket::activeDOMObjectName() const
+// {
+// return "WebSocket";
+// }
+
+void WebSocket::didConnect()
+{
+ LOG(Network, "WebSocket %p didConnect()", this);
+ // queueTaskKeepingObjectAlive(*this, TaskSource::WebSocket, [this] {
+ if (m_state == CLOSED)
+ return;
+ if (m_state != CONNECTING) {
+ didClose(0, ClosingHandshakeIncomplete, WebSocketChannel::CloseEventCodeAbnormalClosure, emptyString());
+ return;
+ }
+ ASSERT(scriptExecutionContext());
+ m_state = OPEN;
+ // m_subprotocol = m_channel->subprotocol();
+ // m_extensions = m_channel->extensions();
+ dispatchEvent(Event::create(eventNames().openEvent, Event::CanBubble::No, Event::IsCancelable::No));
+ // });
+}
+
+void WebSocket::didReceiveMessage(String&& message)
+{
+ LOG(Network, "WebSocket %p didReceiveMessage() Text message '%s'", this, message.utf8().data());
+ // queueTaskKeepingObjectAlive(*this, TaskSource::WebSocket, [this, message = WTFMove(message)]() mutable {
+ if (m_state != OPEN)
+ return;
+
+ // if (UNLIKELY(InspectorInstrumentation::hasFrontends())) {
+ // if (auto* inspector = m_channel->channelInspector()) {
+ // auto utf8Message = message.utf8();
+ // inspector->didReceiveWebSocketFrame(WebSocketChannelInspector::createFrame(utf8Message.dataAsUInt8Ptr(), utf8Message.length(), WebSocketFrame::OpCode::OpCodeText));
+ // }
+ // }
+ ASSERT(scriptExecutionContext());
+ dispatchEvent(MessageEvent::create(WTFMove(message), m_url.string()));
+ // });
+}
+
+void WebSocket::didReceiveBinaryData(Vector<uint8_t>&& binaryData)
+{
+ LOG(Network, "WebSocket %p didReceiveBinaryData() %u byte binary message", this, static_cast<unsigned>(binaryData.size()));
+ // queueTaskKeepingObjectAlive(*this, TaskSource::WebSocket, [this, binaryData = WTFMove(binaryData)]() mutable {
+ if (m_state != OPEN)
+ return;
+
+ // if (UNLIKELY(InspectorInstrumentation::hasFrontends())) {
+ // if (auto* inspector = m_channel->channelInspector())
+ // inspector->didReceiveWebSocketFrame(WebSocketChannelInspector::createFrame(binaryData.data(), binaryData.size(), WebSocketFrame::OpCode::OpCodeBinary));
+ // }
+
+ switch (m_binaryType) {
+ // case BinaryType::Blob:
+ // // FIXME: We just received the data from NetworkProcess, and are sending it back. This is inefficient.
+ // dispatchEvent(MessageEvent::create(Blob::create(scriptExecutionContext(), WTFMove(binaryData), emptyString()), SecurityOrigin::create(m_url)->toString()));
+ // break;
+ case BinaryType::ArrayBuffer:
+ dispatchEvent(MessageEvent::create(ArrayBuffer::create(binaryData.data(), binaryData.size()), m_url.string()));
+ break;
+ }
+ // });
+}
+
+void WebSocket::didReceiveMessageError(String&& reason)
+{
+ LOG(Network, "WebSocket %p didReceiveErrorMessage()", this);
+ // queueTaskKeepingObjectAlive(*this, TaskSource::WebSocket, [this, reason = WTFMove(reason)] {
+ if (m_state == CLOSED)
+ return;
+ m_state = CLOSED;
+ ASSERT(scriptExecutionContext());
+
+ // if (UNLIKELY(InspectorInstrumentation::hasFrontends())) {
+ // if (auto* inspector = m_channel->channelInspector())
+ // inspector->didReceiveWebSocketFrameError(reason);
+ // }
+
+ // FIXME: As per https://html.spec.whatwg.org/multipage/web-sockets.html#feedback-from-the-protocol:concept-websocket-closed, we should synchronously fire a close event.
+ dispatchErrorEventIfNeeded();
+ // });
+}
+
+void WebSocket::didReceiveMessageError(String& reason)
+{
+ LOG(Network, "WebSocket %p didReceiveErrorMessage()", this);
+ // queueTaskKeepingObjectAlive(*this, TaskSource::WebSocket, [this, reason = WTFMove(reason)] {
+ if (m_state == CLOSED)
+ return;
+ m_state = CLOSED;
+ ASSERT(scriptExecutionContext());
+
+ // if (UNLIKELY(InspectorInstrumentation::hasFrontends())) {
+ // if (auto* inspector = m_channel->channelInspector())
+ // inspector->didReceiveWebSocketFrameError(reason);
+ // }
+
+ // FIXME: As per https://html.spec.whatwg.org/multipage/web-sockets.html#feedback-from-the-protocol:concept-websocket-closed, we should synchronously fire a close event.
+ dispatchEvent(CloseEvent::create(false, 0, reason));
+ // });
+}
+
+void WebSocket::didUpdateBufferedAmount(unsigned bufferedAmount)
+{
+ LOG(Network, "WebSocket %p didUpdateBufferedAmount() New bufferedAmount is %u", this, bufferedAmount);
+ if (m_state == CLOSED)
+ return;
+ m_bufferedAmount = bufferedAmount;
+}
+
+void WebSocket::didStartClosingHandshake()
+{
+ LOG(Network, "WebSocket %p didStartClosingHandshake()", this);
+ // queueTaskKeepingObjectAlive(*this, TaskSource::WebSocket, [this] {
+ if (m_state == CLOSED)
+ return;
+ m_state = CLOSING;
+ // });
+}
+
+void WebSocket::didClose(unsigned unhandledBufferedAmount, ClosingHandshakeCompletionStatus closingHandshakeCompletion, unsigned short code, const String& reason)
+{
+ LOG(Network, "WebSocket %p didClose()", this);
+ // queueTaskKeepingObjectAlive(*this, TaskSource::WebSocket, [this, unhandledBufferedAmount, closingHandshakeCompletion, code, reason] {
+ if (!m_channel)
+ return;
+
+ // if (UNLIKELY(InspectorInstrumentation::hasFrontends())) {
+ // if (auto* inspector = m_channel->channelInspector()) {
+ // WebSocketFrame closingFrame(WebSocketFrame::OpCodeClose, true, false, false);
+ // inspector->didReceiveWebSocketFrame(closingFrame);
+ // inspector->didCloseWebSocket();
+ // }
+ // }
+
+ bool wasClean = m_state == CLOSING && !unhandledBufferedAmount && closingHandshakeCompletion == ClosingHandshakeComplete && code != WebSocketChannel::CloseEventCodeAbnormalClosure;
+ m_state = CLOSED;
+ m_bufferedAmount = unhandledBufferedAmount;
+ ASSERT(scriptExecutionContext());
+
+ dispatchEvent(CloseEvent::create(wasClean, code, reason));
+
+ if (m_channel) {
+ m_channel->disconnect();
+ m_channel = nullptr;
+ }
+ // m_pendingActivity = nullptr;
+ // });
+}
+
+void WebSocket::didUpgradeURL()
+{
+ ASSERT(m_url.protocolIs("ws"));
+ m_url.setProtocol("wss");
+}
+
+size_t WebSocket::getFramingOverhead(size_t payloadSize)
+{
+ static const size_t hybiBaseFramingOverhead = 2; // Every frame has at least two-byte header.
+ static const size_t hybiMaskingKeyLength = 4; // Every frame from client must have masking key.
+ static const size_t minimumPayloadSizeWithTwoByteExtendedPayloadLength = 126;
+ static const size_t minimumPayloadSizeWithEightByteExtendedPayloadLength = 0x10000;
+ size_t overhead = hybiBaseFramingOverhead + hybiMaskingKeyLength;
+ if (payloadSize >= minimumPayloadSizeWithEightByteExtendedPayloadLength)
+ overhead += 8;
+ else if (payloadSize >= minimumPayloadSizeWithTwoByteExtendedPayloadLength)
+ overhead += 2;
+ return overhead;
+}
+
+void WebSocket::dispatchErrorEventIfNeeded()
+{
+ if (m_dispatchedErrorEvent)
+ return;
+
+ m_dispatchedErrorEvent = true;
+ dispatchEvent(Event::create(eventNames().errorEvent, Event::CanBubble::No, Event::IsCancelable::No));
+}
+
+void WebSocket::didConnect(us_socket_t* socket, char* bufferedData, size_t bufferedDataSize)
+{
+ m_state = CONNECTED;
+ this->m_upgradeClient = nullptr;
+}
+void WebSocket::didFailToConnect(int32_t code)
+{
+ m_state = CLOSED;
+ this->m_upgradeClient = nullptr;
+
+ switch (code) {
+ // cancel
+ case 0: {
+ break;
+ }
+ // invalid_response
+ case 1: {
+ auto message = MAKE_STATIC_STRING_IMPL("Invalid response");
+ didReceiveMessageError(message);
+ break;
+ }
+ // expected_101_status_code
+ case 2: {
+ auto message = MAKE_STATIC_STRING_IMPL("Expected 101 status code");
+ didReceiveMessageError(message);
+ break;
+ }
+ // missing_upgrade_header
+ case 3: {
+ auto message = MAKE_STATIC_STRING_IMPL("Missing upgrade header");
+ didReceiveMessageError(message);
+ break;
+ }
+ // missing_connection_header
+ case 4: {
+ auto message = MAKE_STATIC_STRING_IMPL("Missing connection header");
+ didReceiveMessageError(message);
+ break;
+ }
+ // missing_websocket_accept_header
+ case 5: {
+ auto message = MAKE_STATIC_STRING_IMPL("Missing websocket accept header");
+ didReceiveMessageError(message);
+ break;
+ }
+ // invalid_upgrade_header
+ case 6: {
+ auto message = MAKE_STATIC_STRING_IMPL("Invalid upgrade header");
+ didReceiveMessageError(message);
+ break;
+ }
+ // invalid_connection_header
+ case 7: {
+ auto message = MAKE_STATIC_STRING_IMPL("Invalid connection header");
+ didReceiveMessageError(message);
+ break;
+ }
+ // invalid_websocket_version
+ case 8: {
+ auto message = MAKE_STATIC_STRING_IMPL("Invalid websocket version");
+ didReceiveMessageError(message);
+ break;
+ }
+ // mismatch_websocket_accept_header
+ case 9: {
+ auto message = MAKE_STATIC_STRING_IMPL("Mismatch websocket accept header");
+ didReceiveMessageError(message);
+ break;
+ }
+ // missing_client_protocol
+ case 10: {
+ auto message = MAKE_STATIC_STRING_IMPL("Missing client protocol");
+ didReceiveMessageError(message);
+ break;
+ }
+ // mismatch_client_protocol
+ case 11: {
+ auto message = MAKE_STATIC_STRING_IMPL("Mismatch client protocol");
+ didReceiveMessageError(message);
+ break;
+ }
+ // timeout
+ case 12: {
+ auto message = MAKE_STATIC_STRING_IMPL("Timeout");
+ didReceiveMessageError(message);
+ break;
+ }
+ // closed
+ case 13: {
+ auto message = MAKE_STATIC_STRING_IMPL("Closed by client");
+ didReceiveMessageError(message);
+ break;
+ }
+ // failed_to_write
+ case 14: {
+ auto message = MAKE_STATIC_STRING_IMPL("Failed to write");
+ didReceiveMessageError(message);
+ break;
+ }
+ // failed_to_connect
+ case 15: {
+ auto message = MAKE_STATIC_STRING_IMPL("Failed to connect");
+ didReceiveMessageError(message);
+ break;
+ }
+ // headers_too_large
+ case 16: {
+ auto message = MAKE_STATIC_STRING_IMPL("Headers too large");
+ didReceiveMessageError(message);
+ break;
+ }
+ // ended
+ case 17: {
+ auto message = MAKE_STATIC_STRING_IMPL("Closed by server");
+ didReceiveMessageError(message);
+ break;
+ }
+ }
+}
+} // namespace WebCore
+
+extern "C" WebSocket__didConnect(WebSocket* webSocket, us_socket_t* socket, char* bufferedData, size_t len)
+{
+ webSocket->didConnect(socket, bufferedData, len);
+}
+extern "C" WebSocket__didFailToConnect(WebSocket* webSocket, int32_t errorCode)
+{
+ webSocket->didFailToConnect(socket, errorCode);
+} \ No newline at end of file
diff --git a/src/javascript/jsc/bindings/webcore/WebSocket.h b/src/javascript/jsc/bindings/webcore/WebSocket.h
new file mode 100644
index 000000000..ed108705c
--- /dev/null
+++ b/src/javascript/jsc/bindings/webcore/WebSocket.h
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "ContextDestructionObserver.h"
+#include "EventTarget.h"
+#include "ExceptionOr.h"
+#include <wtf/URL.h>
+#include <wtf/HashSet.h>
+#include <wtf/Lock.h>
+
+#include "WebSocketStream.h"
+
+namespace JSC {
+class ArrayBuffer;
+class ArrayBufferView;
+}
+
+namespace WebCore {
+
+// class Blob;
+class WebSocket final : public RefCounted<WebSocket>, public EventTargetWithInlineData, public ContextDestructionObserver {
+ WTF_MAKE_ISO_ALLOCATED(WebSocket);
+
+public:
+ static ASCIILiteral subprotocolSeparator();
+
+ static ExceptionOr<Ref<WebSocket>> create(ScriptExecutionContext&, const String& url);
+ static ExceptionOr<Ref<WebSocket>> create(ScriptExecutionContext&, const String& url, const String& protocol);
+ static ExceptionOr<Ref<WebSocket>> create(ScriptExecutionContext&, const String& url, const Vector<String>& protocols);
+ ~WebSocket();
+
+ static HashSet<WebSocket*>& allActiveWebSockets() WTF_REQUIRES_LOCK(s_allActiveWebSocketsLock);
+ static Lock& allActiveWebSocketsLock() WTF_RETURNS_LOCK(s_allActiveWebSocketsLock);
+
+ enum State {
+ CONNECTING = 0,
+ OPEN = 1,
+ CLOSING = 2,
+ CLOSED = 3
+ };
+
+ ExceptionOr<void> connect(const String& url);
+ ExceptionOr<void> connect(const String& url, const String& protocol);
+ ExceptionOr<void> connect(const String& url, const Vector<String>& protocols);
+
+ ExceptionOr<void> send(const String& message);
+ ExceptionOr<void> send(JSC::ArrayBuffer&);
+ ExceptionOr<void> send(JSC::ArrayBufferView&);
+ // ExceptionOr<void> send(Blob&);
+
+ ExceptionOr<void> close(std::optional<unsigned short> code, const String& reason);
+
+ WebSocketStream* channel() const;
+
+ const URL& url() const;
+ State readyState() const;
+ unsigned bufferedAmount() const;
+
+ String protocol() const;
+ String extensions() const;
+
+ String binaryType() const;
+ ExceptionOr<void> setBinaryType(const String&);
+
+ ScriptExecutionContext* scriptExecutionContext() const final;
+
+ using RefCounted::deref;
+ using RefCounted::ref;
+
+private:
+ explicit WebSocket(ScriptExecutionContext&);
+
+ void dispatchErrorEventIfNeeded();
+
+ // void contextDestroyed() final;
+ // void suspend(ReasonForSuspension) final;
+ // void resume() final;
+ // void stop() final;
+ // const char* activeDOMObjectName() const final;
+
+ EventTargetInterface eventTargetInterface() const final;
+
+ void refEventTarget() final { ref(); }
+ void derefEventTarget() final { deref(); }
+
+ void didConnect();
+ void didReceiveMessage(String&& message);
+ void didReceiveBinaryData(Vector<uint8_t>&&);
+ void didReceiveMessageError(String&& reason);
+ void didUpdateBufferedAmount(unsigned bufferedAmount);
+ void didStartClosingHandshake();
+ void didClose(unsigned unhandledBufferedAmount, ClosingHandshakeCompletionStatus, unsigned short code, const String& reason);
+ void didConnect(us_socket_t* socket, char* bufferedData, size_t bufferedDataSize);
+ void didFailToConnect(int32_t code);
+
+ void failAsynchronously();
+
+ enum class BinaryType { Blob,
+ ArrayBuffer };
+
+ static Lock s_allActiveWebSocketsLock;
+ WebSocketStream* m_channel { nullptr };
+
+ State m_state { CONNECTING };
+ URL m_url;
+ unsigned m_bufferedAmount { 0 };
+ unsigned m_bufferedAmountAfterClose { 0 };
+ BinaryType m_binaryType { BinaryType::ArrayBuffer };
+ String m_subprotocol;
+ String m_extensions;
+ void* m_upgradeClient { nullptr };
+
+ bool m_dispatchedErrorEvent { false };
+ // RefPtr<PendingActivity<WebSocket>> m_pendingActivity;
+};
+
+} // namespace WebCore
diff --git a/src/javascript/jsc/bindings/webcore/WebSocket.idl b/src/javascript/jsc/bindings/webcore/WebSocket.idl
new file mode 100644
index 000000000..4f65c90f3
--- /dev/null
+++ b/src/javascript/jsc/bindings/webcore/WebSocket.idl
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ * Copyright (C) 2010, 2011 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+[
+ ActiveDOMObject,
+ Exposed=(Window,Worker),
+ EnabledAtRuntime=WebSocketEnabled
+] interface WebSocket : EventTarget {
+ [CallWith=CurrentScriptExecutionContext] constructor(USVString url, optional sequence<DOMString> protocols = []);
+ [CallWith=CurrentScriptExecutionContext] constructor(USVString url, DOMString protocol);
+
+ readonly attribute USVString URL; // Lowercased .url is the one in the spec, but leaving .URL for compatibility reasons.
+ readonly attribute USVString url;
+
+ const unsigned short CONNECTING = 0;
+ const unsigned short OPEN = 1;
+ const unsigned short CLOSING = 2;
+ const unsigned short CLOSED = 3;
+ readonly attribute unsigned short readyState;
+
+ readonly attribute unsigned long bufferedAmount;
+
+ attribute EventHandler onopen;
+ attribute EventHandler onmessage;
+ attribute EventHandler onerror;
+ attribute EventHandler onclose;
+
+ readonly attribute DOMString? protocol;
+ readonly attribute DOMString? extensions;
+
+ attribute DOMString binaryType;
+
+ undefined send(ArrayBuffer data);
+ undefined send(ArrayBufferView data);
+ undefined send(Blob data);
+ undefined send(USVString data);
+
+ undefined close(optional [Clamp] unsigned short code, optional USVString reason);
+};
diff --git a/src/javascript/jsc/bindings/webcore/WebSocketHandshake.cpp b/src/javascript/jsc/bindings/webcore/WebSocketHandshake.cpp
new file mode 100644
index 000000000..90482d668
--- /dev/null
+++ b/src/javascript/jsc/bindings/webcore/WebSocketHandshake.cpp
@@ -0,0 +1,485 @@
+// /*
+// * Copyright (C) 2011 Google Inc. All rights reserved.
+// * Copyright (C) Research In Motion Limited 2011. All rights reserved.
+// * Copyright (C) 2018-2021 Apple Inc. All rights reserved.
+// *
+// * Redistribution and use in source and binary forms, with or without
+// * modification, are permitted provided that the following conditions are
+// * met:
+// *
+// * * Redistributions of source code must retain the above copyright
+// * notice, this list of conditions and the following disclaimer.
+// * * Redistributions in binary form must reproduce the above
+// * copyright notice, this list of conditions and the following disclaimer
+// * in the documentation and/or other materials provided with the
+// * distribution.
+// * * Neither the name of Google Inc. nor the names of its
+// * contributors may be used to endorse or promote products derived from
+// * this software without specific prior written permission.
+// *
+// * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// */
+
+// #include "config.h"
+// #include "WebSocketHandshake.h"
+
+// #include "HTTPHeaderMap.h"
+// #include "HTTPHeaderNames.h"
+// #include "HTTPHeaderValues.h"
+// #include "HTTPParsers.h"
+// #include "ScriptExecutionContext.h"
+// #include <wtf/URL.h>
+// #include "WebSocket.h"
+// #include <wtf/ASCIICType.h>
+// #include <wtf/CryptographicallyRandomNumber.h>
+// #include <wtf/SHA1.h>
+// #include <wtf/StdLibExtras.h>
+// #include <wtf/StringExtras.h>
+// #include <wtf/Vector.h>
+// #include <wtf/text/Base64.h>
+// #include <wtf/text/CString.h>
+// #include <wtf/text/StringToIntegerConversion.h>
+// #include <wtf/text/StringView.h>
+// #include <wtf/text/WTFString.h>
+// #include <wtf/unicode/CharacterNames.h>
+
+// namespace WebCore {
+
+// static String resourceName(const URL& url)
+// {
+// auto path = url.path();
+// auto result = makeString(
+// path,
+// path.isEmpty() ? "/" : "",
+// url.queryWithLeadingQuestionMark());
+// ASSERT(!result.isEmpty());
+// ASSERT(!result.contains(' '));
+// return result;
+// }
+
+// static String hostName(const URL& url, bool secure)
+// {
+// ASSERT(url.protocolIs("wss") == secure);
+// if (url.port() && ((!secure && url.port().value() != 80) || (secure && url.port().value() != 443)))
+// return makeString(asASCIILowercase(url.host()), ':', url.port().value());
+// return url.host().convertToASCIILowercase();
+// }
+
+// static constexpr size_t maxInputSampleSize = 128;
+// static String trimInputSample(const uint8_t* p, size_t length)
+// {
+// if (length <= maxInputSampleSize)
+// return String(p, length);
+// return makeString(StringView(p, length).left(maxInputSampleSize), horizontalEllipsis);
+// }
+
+// static String generateSecWebSocketKey()
+// {
+// static const size_t nonceSize = 16;
+// unsigned char key[nonceSize];
+// cryptographicallyRandomValues(key, nonceSize);
+// return base64EncodeToString(key, nonceSize);
+// }
+
+// String WebSocketHandshake::getExpectedWebSocketAccept(const String& secWebSocketKey)
+// {
+// constexpr uint8_t webSocketKeyGUID[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
+// SHA1 sha1;
+// CString keyData = secWebSocketKey.ascii();
+// sha1.addBytes(keyData.dataAsUInt8Ptr(), keyData.length());
+// sha1.addBytes(webSocketKeyGUID, std::size(webSocketKeyGUID) - 1);
+// SHA1::Digest hash;
+// sha1.computeHash(hash);
+// return base64EncodeToString(hash.data(), SHA1::hashSize);
+// }
+
+// WebSocketHandshake::WebSocketHandshake(const URL& url, const String& protocol, const String& userAgent, const String& clientOrigin, bool allowCookies, bool isAppInitiated)
+// : m_url(url)
+// , m_clientProtocol(protocol)
+// , m_secure(m_url.protocolIs("wss"))
+// , m_mode(Incomplete)
+// , m_userAgent(userAgent)
+// , m_clientOrigin(clientOrigin)
+// , m_allowCookies(allowCookies)
+// , m_isAppInitiated(isAppInitiated)
+// {
+// m_secWebSocketKey = generateSecWebSocketKey();
+// m_expectedAccept = getExpectedWebSocketAccept(m_secWebSocketKey);
+// }
+
+// WebSocketHandshake::~WebSocketHandshake() = default;
+
+// const URL& WebSocketHandshake::url() const
+// {
+// return m_url;
+// }
+
+// // FIXME: Return type should just be String, not const String.
+// const String WebSocketHandshake::host() const
+// {
+// return m_url.host().convertToASCIILowercase();
+// }
+
+// const String& WebSocketHandshake::clientProtocol() const
+// {
+// return m_clientProtocol;
+// }
+
+// void WebSocketHandshake::setClientProtocol(const String& protocol)
+// {
+// m_clientProtocol = protocol;
+// }
+
+// bool WebSocketHandshake::secure() const
+// {
+// return m_secure;
+// }
+
+// String WebSocketHandshake::clientLocation() const
+// {
+// return makeString(m_secure ? "wss" : "ws", "://", hostName(m_url, m_secure), resourceName(m_url));
+// }
+
+// CString WebSocketHandshake::clientHandshakeMessage() const
+// {
+// // Keep the following consistent with clientHandshakeRequest just below.
+
+// // Cookies are not retrieved in the WebContent process. Instead, a proxy object is
+// // added in the handshake, and is exchanged for actual cookies in the Network process.
+
+// // Add no-cache headers to avoid a compatibility issue. There are some proxies that
+// // rewrite "Connection: upgrade" to "Connection: close" in the response if a request
+// // doesn't contain these headers.
+
+// auto extensions = m_extensionDispatcher.createHeaderValue();
+// }
+
+// void WebSocketHandshake::reset()
+// {
+// m_mode = Incomplete;
+// }
+
+// int WebSocketHandshake::readServerHandshake(const uint8_t* header, size_t len)
+// {
+// m_mode = Incomplete;
+// int statusCode;
+// AtomString statusText;
+// int lineLength = readStatusLine(header, len, statusCode, statusText);
+// if (lineLength == -1)
+// return -1;
+// if (statusCode == -1) {
+// m_mode = Failed; // m_failureReason is set inside readStatusLine().
+// return len;
+// }
+// // LOG(Network, "WebSocketHandshake %p readServerHandshake() Status code is %d", this, statusCode);
+
+// m_serverHandshakeResponse = ResourceResponse();
+// m_serverHandshakeResponse.setHTTPStatusCode(statusCode);
+// m_serverHandshakeResponse.setHTTPStatusText(statusText);
+
+// if (statusCode != 101) {
+// m_mode = Failed;
+// m_failureReason = makeString("Unexpected response code: ", statusCode);
+// return len;
+// }
+// m_mode = Normal;
+// if (!memmem(header, len, "\r\n\r\n", 4)) {
+// // Just hasn't been received fully yet.
+// m_mode = Incomplete;
+// return -1;
+// }
+// auto p = readHTTPHeaders(header + lineLength, header + len);
+// if (!p) {
+// // LOG(Network, "WebSocketHandshake %p readServerHandshake() readHTTPHeaders() failed", this);
+// m_mode = Failed; // m_failureReason is set inside readHTTPHeaders().
+// return len;
+// }
+// if (!checkResponseHeaders()) {
+// // LOG(Network, "WebSocketHandshake %p readServerHandshake() checkResponseHeaders() failed", this);
+// m_mode = Failed;
+// return p - header;
+// }
+
+// m_mode = Connected;
+// return p - header;
+// }
+
+// WebSocketHandshake::Mode WebSocketHandshake::mode() const
+// {
+// return m_mode;
+// }
+
+// String WebSocketHandshake::failureReason() const
+// {
+// return m_failureReason;
+// }
+
+// String WebSocketHandshake::serverWebSocketProtocol() const
+// {
+// return m_serverHandshakeResponse.httpHeaderFields().get(HTTPHeaderName::SecWebSocketProtocol);
+// }
+
+// String WebSocketHandshake::serverSetCookie() const
+// {
+// return m_serverHandshakeResponse.httpHeaderFields().get(HTTPHeaderName::SetCookie);
+// }
+
+// String WebSocketHandshake::serverUpgrade() const
+// {
+// return m_serverHandshakeResponse.httpHeaderFields().get(HTTPHeaderName::Upgrade);
+// }
+
+// String WebSocketHandshake::serverConnection() const
+// {
+// return m_serverHandshakeResponse.httpHeaderFields().get(HTTPHeaderName::Connection);
+// }
+
+// String WebSocketHandshake::serverWebSocketAccept() const
+// {
+// return m_serverHandshakeResponse.httpHeaderFields().get(HTTPHeaderName::SecWebSocketAccept);
+// }
+
+// void WebSocketHandshake::addExtensionProcessor(std::unique_ptr<WebSocketExtensionProcessor> processor)
+// {
+// m_extensionDispatcher.addProcessor(WTFMove(processor));
+// }
+
+// URL WebSocketHandshake::httpURLForAuthenticationAndCookies() const
+// {
+// URL url = m_url.isolatedCopy();
+// bool couldSetProtocol = url.setProtocol(m_secure ? "https" : "http");
+// ASSERT_UNUSED(couldSetProtocol, couldSetProtocol);
+// return url;
+// }
+
+// // https://tools.ietf.org/html/rfc6455#section-4.1
+// // "The HTTP version MUST be at least 1.1."
+// static inline bool headerHasValidHTTPVersion(StringView httpStatusLine)
+// {
+// constexpr char preamble[] = "HTTP/";
+// if (!httpStatusLine.startsWith(preamble))
+// return false;
+
+// // Check that there is a version number which should be at least three characters after "HTTP/"
+// unsigned preambleLength = strlen(preamble);
+// if (httpStatusLine.length() < preambleLength + 3)
+// return false;
+
+// auto dotPosition = httpStatusLine.find('.', preambleLength);
+// if (dotPosition == notFound)
+// return false;
+
+// auto majorVersion = parseInteger<int>(httpStatusLine.substring(preambleLength, dotPosition - preambleLength));
+// if (!majorVersion)
+// return false;
+
+// unsigned minorVersionLength;
+// unsigned charactersLeftAfterDotPosition = httpStatusLine.length() - dotPosition;
+// for (minorVersionLength = 1; minorVersionLength < charactersLeftAfterDotPosition; minorVersionLength++) {
+// if (!isASCIIDigit(httpStatusLine[dotPosition + minorVersionLength]))
+// break;
+// }
+// auto minorVersion = parseInteger<int>(httpStatusLine.substring(dotPosition + 1, minorVersionLength));
+// if (!minorVersion)
+// return false;
+
+// return (*majorVersion >= 1 && *minorVersion >= 1) || *majorVersion >= 2;
+// }
+
+// // Returns the header length (including "\r\n"), or -1 if we have not received enough data yet.
+// // If the line is malformed or the status code is not a 3-digit number,
+// // statusCode and statusText will be set to -1 and a null string, respectively.
+// int WebSocketHandshake::readStatusLine(const uint8_t* header, size_t headerLength, int& statusCode, AtomString& statusText)
+// {
+// // Arbitrary size limit to prevent the server from sending an unbounded
+// // amount of data with no newlines and forcing us to buffer it all.
+// static const int maximumLength = 1024;
+
+// statusCode = -1;
+// statusText = nullAtom();
+
+// const uint8_t* space1 = nullptr;
+// const uint8_t* space2 = nullptr;
+// const uint8_t* p;
+// size_t consumedLength;
+
+// for (p = header, consumedLength = 0; consumedLength < headerLength; p++, consumedLength++) {
+// if (*p == ' ') {
+// if (!space1)
+// space1 = p;
+// else if (!space2)
+// space2 = p;
+// } else if (*p == '\0') {
+// // The caller isn't prepared to deal with null bytes in status
+// // line. WebSockets specification doesn't prohibit this, but HTTP
+// // does, so we'll just treat this as an error.
+// m_failureReason = "Status line contains embedded null"_s;
+// return p + 1 - header;
+// } else if (!isASCII(*p)) {
+// m_failureReason = "Status line contains non-ASCII character"_s;
+// return p + 1 - header;
+// } else if (*p == '\n')
+// break;
+// }
+// if (consumedLength == headerLength)
+// return -1; // We have not received '\n' yet.
+
+// auto end = p + 1;
+// int lineLength = end - header;
+// if (lineLength > maximumLength) {
+// m_failureReason = "Status line is too long"_s;
+// return maximumLength;
+// }
+
+// // The line must end with "\r\n".
+// if (lineLength < 2 || *(end - 2) != '\r') {
+// m_failureReason = "Status line does not end with CRLF"_s;
+// return lineLength;
+// }
+
+// if (!space1 || !space2) {
+// m_failureReason = makeString("No response code found: ", trimInputSample(header, lineLength - 2));
+// return lineLength;
+// }
+
+// StringView httpStatusLine(header, space1 - header);
+// if (!headerHasValidHTTPVersion(httpStatusLine)) {
+// m_failureReason = makeString("Invalid HTTP version string: ", httpStatusLine);
+// return lineLength;
+// }
+
+// StringView statusCodeString(space1 + 1, space2 - space1 - 1);
+// if (statusCodeString.length() != 3) // Status code must consist of three digits.
+// return lineLength;
+// for (int i = 0; i < 3; ++i) {
+// if (!isASCIIDigit(statusCodeString[i])) {
+// m_failureReason = makeString("Invalid status code: ", statusCodeString);
+// return lineLength;
+// }
+// }
+
+// statusCode = parseInteger<int>(statusCodeString).value();
+// statusText = AtomString(space2 + 1, end - space2 - 3); // Exclude "\r\n".
+// return lineLength;
+// }
+
+// const uint8_t* WebSocketHandshake::readHTTPHeaders(const uint8_t* start, const uint8_t* end)
+// {
+// StringView name;
+// String value;
+// bool sawSecWebSocketExtensionsHeaderField = false;
+// bool sawSecWebSocketAcceptHeaderField = false;
+// bool sawSecWebSocketProtocolHeaderField = false;
+// auto p = start;
+// for (; p < end; p++) {
+// size_t consumedLength = parseHTTPHeader(p, end - p, m_failureReason, name, value);
+// if (!consumedLength)
+// return nullptr;
+// p += consumedLength;
+
+// // Stop once we consumed an empty line.
+// if (name.isEmpty())
+// break;
+
+// HTTPHeaderName headerName;
+// if (!findHTTPHeaderName(name, headerName)) {
+// // Evidence in the wild shows that services make use of custom headers in the handshake
+// m_serverHandshakeResponse.addUncommonHTTPHeaderField(name.toString(), value);
+// continue;
+// }
+
+// // https://tools.ietf.org/html/rfc7230#section-3.2.4
+// // "Newly defined header fields SHOULD limit their field values to US-ASCII octets."
+// if ((headerName == HTTPHeaderName::SecWebSocketExtensions
+// || headerName == HTTPHeaderName::SecWebSocketAccept
+// || headerName == HTTPHeaderName::SecWebSocketProtocol)
+// && !value.isAllASCII()) {
+// m_failureReason = makeString(name, " header value should only contain ASCII characters");
+// return nullptr;
+// }
+
+// if (headerName == HTTPHeaderName::SecWebSocketExtensions) {
+// if (sawSecWebSocketExtensionsHeaderField) {
+// m_failureReason = "The Sec-WebSocket-Extensions header must not appear more than once in an HTTP response"_s;
+// return nullptr;
+// }
+// sawSecWebSocketExtensionsHeaderField = true;
+// } else {
+// if (headerName == HTTPHeaderName::SecWebSocketAccept) {
+// if (sawSecWebSocketAcceptHeaderField) {
+// m_failureReason = "The Sec-WebSocket-Accept header must not appear more than once in an HTTP response"_s;
+// return nullptr;
+// }
+// sawSecWebSocketAcceptHeaderField = true;
+// } else if (headerName == HTTPHeaderName::SecWebSocketProtocol) {
+// if (sawSecWebSocketProtocolHeaderField) {
+// m_failureReason = "The Sec-WebSocket-Protocol header must not appear more than once in an HTTP response"_s;
+// return nullptr;
+// }
+// sawSecWebSocketProtocolHeaderField = true;
+// }
+
+// m_serverHandshakeResponse.addHTTPHeaderField(headerName, value);
+// }
+// }
+// return p;
+// }
+
+// bool WebSocketHandshake::checkResponseHeaders()
+// {
+// const String& serverWebSocketProtocol = this->serverWebSocketProtocol();
+// const String& serverUpgrade = this->serverUpgrade();
+// const String& serverConnection = this->serverConnection();
+// const String& serverWebSocketAccept = this->serverWebSocketAccept();
+
+// if (serverUpgrade.isNull()) {
+// m_failureReason = "Error during WebSocket handshake: 'Upgrade' header is missing"_s;
+// return false;
+// }
+// if (serverConnection.isNull()) {
+// m_failureReason = "Error during WebSocket handshake: 'Connection' header is missing"_s;
+// return false;
+// }
+// if (serverWebSocketAccept.isNull()) {
+// m_failureReason = "Error during WebSocket handshake: 'Sec-WebSocket-Accept' header is missing"_s;
+// return false;
+// }
+
+// if (!equalLettersIgnoringASCIICase(serverUpgrade, "websocket"_s)) {
+// m_failureReason = "Error during WebSocket handshake: 'Upgrade' header value is not 'WebSocket'"_s;
+// return false;
+// }
+// if (!equalLettersIgnoringASCIICase(serverConnection, "upgrade"_s)) {
+// m_failureReason = "Error during WebSocket handshake: 'Connection' header value is not 'Upgrade'"_s;
+// return false;
+// }
+
+// if (serverWebSocketAccept != m_expectedAccept) {
+// m_failureReason = "Error during WebSocket handshake: Sec-WebSocket-Accept mismatch"_s;
+// return false;
+// }
+// if (!serverWebSocketProtocol.isNull()) {
+// if (m_clientProtocol.isEmpty()) {
+// m_failureReason = "Error during WebSocket handshake: Sec-WebSocket-Protocol mismatch"_s;
+// return false;
+// }
+// Vector<String> result = m_clientProtocol.split(StringView { WebSocket::subprotocolSeparator() });
+// if (!result.contains(serverWebSocketProtocol)) {
+// m_failureReason = "Error during WebSocket handshake: Sec-WebSocket-Protocol mismatch"_s;
+// return false;
+// }
+// }
+// return true;
+// }
+
+// } // namespace WebCore
diff --git a/src/javascript/jsc/bindings/webcore/WebSocketHandshake.h b/src/javascript/jsc/bindings/webcore/WebSocketHandshake.h
new file mode 100644
index 000000000..1cefe2e60
--- /dev/null
+++ b/src/javascript/jsc/bindings/webcore/WebSocketHandshake.h
@@ -0,0 +1,102 @@
+// /*
+// * Copyright (C) 2011 Google Inc. All rights reserved.
+// * Copyright (C) 2021 Apple Inc. All rights reserved.
+// *
+// * Redistribution and use in source and binary forms, with or without
+// * modification, are permitted provided that the following conditions are
+// * met:
+// *
+// * * Redistributions of source code must retain the above copyright
+// * notice, this list of conditions and the following disclaimer.
+// * * Redistributions in binary form must reproduce the above
+// * copyright notice, this list of conditions and the following disclaimer
+// * in the documentation and/or other materials provided with the
+// * distribution.
+// * * Neither the name of Google Inc. nor the names of its
+// * contributors may be used to endorse or promote products derived from
+// * this software without specific prior written permission.
+// *
+// * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// */
+
+// #pragma once
+
+// #include <wtf/URL.h>
+// #include <wtf/WeakPtr.h>
+// #include <wtf/text/WTFString.h>
+
+// namespace WebCore {
+
+// class WebSocketHandshake {
+
+// public:
+// enum Mode {
+// Incomplete,
+// Normal,
+// Failed,
+// Connected
+// };
+// WebSocketHandshake(const URL&, const String& protocol);
+// ~WebSocketHandshake() {}
+
+// const URL& url() const;
+// void setURL(const URL&);
+// URL httpURLForAuthenticationAndCookies() const;
+// const String host() const;
+
+// const String& clientProtocol() const;
+// void setClientProtocol(const String&);
+
+// bool secure() const;
+
+// String clientLocation() const;
+
+// CString clientHandshakeMessage() const;
+
+// void reset();
+
+// int readServerHandshake(const uint8_t* header, size_t len);
+// Mode mode() const;
+// String failureReason() const; // Returns a string indicating the reason of failure if mode() == Failed.
+
+// String serverWebSocketProtocol() const;
+// String serverSetCookie() const;
+// String serverUpgrade() const;
+// String serverConnection() const;
+// String serverWebSocketAccept() const;
+// String acceptedExtensions() const;
+
+// // void addExtensionProcessor(std::unique_ptr<WebSocketExtensionProcessor>);
+
+// static String getExpectedWebSocketAccept(const String& secWebSocketKey);
+
+// private:
+// int readStatusLine(const uint8_t* header, size_t headerLength, int& statusCode, AtomString& statusText);
+
+// // Reads all headers except for the two predefined ones.
+// const uint8_t* readHTTPHeaders(const uint8_t* start, const uint8_t* end);
+// void processHeaders();
+// bool checkResponseHeaders();
+
+// URL m_url;
+// String m_clientProtocol;
+// bool m_secure;
+
+// Mode m_mode;
+// String m_failureReason;
+
+// String m_secWebSocketKey;
+// String m_expectedAccept;
+// };
+
+// } // namespace WebCore
diff --git a/src/javascript/jsc/bindings/webcore/WebSocketIdentifier.h b/src/javascript/jsc/bindings/webcore/WebSocketIdentifier.h
new file mode 100644
index 000000000..97884abd8
--- /dev/null
+++ b/src/javascript/jsc/bindings/webcore/WebSocketIdentifier.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2020 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice , this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <wtf/ObjectIdentifier.h>
+
+namespace WebCore {
+
+enum WebSocketIdentifierType { };
+using WebSocketIdentifier = ObjectIdentifier<WebSocketIdentifierType>;
+
+} // namespace WebCore
diff --git a/src/javascript/jsc/bindings/webcore/WebSocketStream.cpp b/src/javascript/jsc/bindings/webcore/WebSocketStream.cpp
new file mode 100644
index 000000000..434860854
--- /dev/null
+++ b/src/javascript/jsc/bindings/webcore/WebSocketStream.cpp
@@ -0,0 +1,83 @@
+#include "WebSocketStream.h"
+#include <uws/uSockets/src/libusockets.h>
+
+namespace WebCore {
+
+template<bool SSL, bool isServer>
+WebSocketStreamBase<SSL, isServer>* WebSocketStreamBase<SSL, isServer>::adoptSocket(us_socket_t* socket, ScriptExecutionContext* scriptCtx)
+{
+ using UserData = WebCore::WebSocket;
+
+ /* Adopting a socket invalidates it, do not rely on it directly to carry any data */
+ uWS::WebSocket<SSL, isServer, UserData>* webSocket = (uWS::WebSocket<SSL, isServer, UserData>*)us_socket_context_adopt_socket(SSL,
+ (us_socket_context_t*)webSocketContext, (us_socket_t*)this, sizeof(WebSocketData) + sizeof(UserData));
+
+ /* For whatever reason we were corked, update cork to the new socket */
+ if (wasCorked) {
+ webSocket->AsyncSocket<SSL>::corkUnchecked();
+ }
+
+ /* Initialize websocket with any moved backpressure intact */
+ webSocket->init(perMessageDeflate, compressOptions, std::move(backpressure));
+}
+
+void WebSocketStreamBase::registerHTTPContext(ScriptExecutionContext* script, us_socket_context_t* ctx, us_loop_t* loop)
+{
+ if constexpr (!isServer) {
+ if constexpr (SSL) {
+ Bun__SecureWebSocketUpgradeClient__register(script->jsGlobalObject(), loop, ctx);
+ } else {
+ Bun__WebSocketUpgradeClient__register(script->jsGlobalObject(), loop, ctx);
+ }
+ } else {
+ RELEASE_ASSERT_NOT_REACHED();
+ }
+}
+
+template<bool SSL, bool isServer>
+uWS::WebSocketContext* WebSocketStreamBase<SSL, isServer>::registerClientContext(ScriptExecutionContext*, us_socket_context_t* parent)
+{
+ uWS::Loop* loop = uWS::Loop::get();
+ uWS::WebSocketContext<SSL, isServer>* ctx = uWS::WebSocketContext<SSL, isServer>::create(loop, parent, nullptr);
+ auto* opts = ctx->getExt();
+
+ /* Maximum message size we can receive */
+ static unsigned int maxPayloadLength = 128 * 1024 * 1024;
+ /* 2 minutes timeout is good */
+ static unsigned short idleTimeout = 120;
+ /* 64kb backpressure is probably good */
+ static unsigned int maxBackpressure = 64 * 1024;
+ static bool closeOnBackpressureLimit = false;
+ /* This one depends on kernel timeouts and is a bad default */
+ static bool resetIdleTimeoutOnSend = false;
+ /* A good default, esp. for newcomers */
+ static bool sendPingsAutomatically = true;
+ /* Maximum socket lifetime in seconds before forced closure (defaults to disabled) */
+ static 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 = false;
+
+ opts->openHandler = [](uWS::WebSocket<SSL, isServer, WebCore::WebSocket>* ws) {
+ auto* webSocket = ws->getUserData();
+ webSocket->didOpen();
+ };
+
+ opts->messageHandler = [](uWS::WebSocket<SSL, isServer, WebCore::WebSocket>* ws, std::string_view input, uWS::OpCode opCode) {
+ auto* webSocket = ws->getUserData();
+ webSocket->didReceiveData<uWS::WebSocket<SSL, isServer, WebCore::WebSocket>*>(ws, input.data(), input.length());
+ };
+
+ opts->closeHandler = [](uWS::WebSocket<SSL, isServer, WebCore::WebSocket>* ws, int code, std::string_view message) {
+ auto* webSocket = ws->getUserData();
+ webSocket->didClose<uWS::WebSocket<SSL, isServer, WebCore::WebSocket>*>(ws, code, message.data(), message.length());
+ };
+}
+
+} \ No newline at end of file
diff --git a/src/javascript/jsc/bindings/webcore/WebSocketStream.h b/src/javascript/jsc/bindings/webcore/WebSocketStream.h
new file mode 100644
index 000000000..a80c94a85
--- /dev/null
+++ b/src/javascript/jsc/bindings/webcore/WebSocketStream.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "wtf/text/StringImpl.h"
+#include "wtf/text/StringView.h"
+#include "wtf/text/WTFString.h"
+#include "wtf/URL.h"
+#include "wtf/Vector.h"
+#include "wtf/Function.h"
+#include <uws/src/WebSocketContext.h>
+
+struct us_socket_context_t;
+struct us_socket_t;
+struct us_loop_t;
+
+namespace WebCore {
+
+enum ClosingHandshakeCompletionStatus {
+ ClosingHandshakeIncomplete,
+ ClosingHandshakeComplete
+};
+
+// This class expects the stream to already be connected & ready to go
+template<bool isSSL, bool isServer>
+class WebSocketStreamBase final {
+public:
+ using WebSocketImpl = uWS::WebSocket<isSSL, isServer>;
+ using WebSocketStreamImpl = WebSocketStreamBase<isSSL, isServer>;
+ using WebSocketContext = uWS::WebSocketContext<isSSL, isServer>;
+
+ ~WebSocketStreamBase();
+ void didConnect();
+ void didReceiveMessage(String&&);
+ void didReceiveBinaryData(Vector<uint8_t>&&);
+ void didReceiveMessageError(String&&);
+ void didUpdateBufferedAmount(unsigned bufferedAmount);
+ void didStartClosingHandshake();
+
+ static WebSocketStreamImpl* adoptSocket(us_socket_t* socket, ScriptExecutionContext* scriptCtx);
+ static void registerHTTPContext(ScriptExecutionContext*, us_socket_context_t*);
+
+ static WebSocketContext* registerClientContext(ScriptExecutionContext*, us_socket_context_t* parent);
+ void sendData(const uint8_t* data, size_t length, Function<void(bool)>);
+ void close(); // Disconnect after all data in buffer are sent.
+ void disconnect();
+ size_t bufferedAmount() const;
+
+ void close(int code, const String& reason); // Start closing handshake.
+ void fail(String&& reason);
+ enum CloseEventCode {
+ CloseEventCodeNotSpecified = -1,
+ CloseEventCodeNormalClosure = 1000,
+ CloseEventCodeGoingAway = 1001,
+ CloseEventCodeProtocolError = 1002,
+ CloseEventCodeUnsupportedData = 1003,
+ CloseEventCodeFrameTooLarge = 1004,
+ CloseEventCodeNoStatusRcvd = 1005,
+ CloseEventCodeAbnormalClosure = 1006,
+ CloseEventCodeInvalidFramePayloadData = 1007,
+ CloseEventCodePolicyViolation = 1008,
+ CloseEventCodeMessageTooBig = 1009,
+ CloseEventCodeMandatoryExt = 1010,
+ CloseEventCodeInternalError = 1011,
+ CloseEventCodeTLSHandshake = 1015,
+ CloseEventCodeMinimumUserDefined = 3000,
+ CloseEventCodeMaximumUserDefined = 4999
+ };
+
+ void didClose(unsigned unhandledBufferedAmount, ClosingHandshakeCompletionStatus, unsigned short code, const String& reason);
+ void didUpgradeURL();
+
+ WebSocketStreamBase()
+ {
+ }
+};
+
+using WebSocketStream = WebSocketStreamBase<false, false>;
+using SecureWebSocketStream = WebSocketStreamBase<true, false>;
+using ServerWebSocketStream = WebSocketStreamBase<false, true>;
+using ServerSecureWebSocketStream = WebSocketStreamBase<true, true>;
+
+} // namespace WebCore