aboutsummaryrefslogtreecommitdiff
path: root/src/bun.js/api
diff options
context:
space:
mode:
authorGravatar Ciro Spaciari <ciro.spaciari@gmail.com> 2023-05-21 13:50:53 -0300
committerGravatar GitHub <noreply@github.com> 2023-05-21 09:50:53 -0700
commit3870f674f90b2780e247bcd670a89ab8dd41fa22 (patch)
treefc8226fe554da7e15352055f9689778aabfb1d0c /src/bun.js/api
parent12b34c625833c7cff4ecb6ebd67b3872028f4a80 (diff)
downloadbun-3870f674f90b2780e247bcd670a89ab8dd41fa22.tar.gz
bun-3870f674f90b2780e247bcd670a89ab8dd41fa22.tar.zst
bun-3870f674f90b2780e247bcd670a89ab8dd41fa22.zip
fix(tls.connect) fix SNI on tls sockets and also servername (mongodb) (#2934)
* fixes SNI on tls sockets and also servername * 💅 * 💅 * add support for https and wss * fix bun types * support Bun.file on ca, key and cert * 💅 * add setTimeout (makes fastify run) * fix httpVersion * fix WebSocketServer and add listen event * fix ws exports and http listening * fix default import * bump uws * add nodebuffer compatibility * fix drain and allow more passing tests to run * fix enqueud messages * default to arraybuffer * fix constructor binaryType * fmt * fixup * skip some tests * skip more * skip fault tests * reuse encoder instance * fix handshake WS Client * temporary revert handshake fix * fix handshake * disable all socket.io test temp * fixup * add back socket.io tests * use node_fs to read cert, ca and key on server.zig * throw the error returned by NodeFS * 💅
Diffstat (limited to 'src/bun.js/api')
-rw-r--r--src/bun.js/api/bun/socket.zig29
-rw-r--r--src/bun.js/api/server.zig285
2 files changed, 243 insertions, 71 deletions
diff --git a/src/bun.js/api/bun/socket.zig b/src/bun.js/api/bun/socket.zig
index 8b78cf035..667df4eb7 100644
--- a/src/bun.js/api/bun/socket.zig
+++ b/src/bun.js/api/bun/socket.zig
@@ -15,6 +15,7 @@ const JSGlobalObject = JSC.JSGlobalObject;
const Which = @import("../../../which.zig");
const uws = @import("root").bun.uws;
const ZigString = JSC.ZigString;
+const BoringSSL = bun.BoringSSL;
// const Corker = struct {
// ptr: ?*[16384]u8 = null,
// holder: ?*anyopaque = null,
@@ -831,6 +832,7 @@ pub const Listener = struct {
.handlers = handlers_ptr,
.this_value = .zero,
.socket = undefined,
+ .connection = connection,
};
TLSSocket.dataSetCached(tls.getThisValue(globalObject), globalObject, default_data);
@@ -853,6 +855,7 @@ pub const Listener = struct {
.handlers = handlers_ptr,
.this_value = .zero,
.socket = undefined,
+ .connection = null,
};
TCPSocket.dataSetCached(tcp.getThisValue(globalObject), globalObject, default_data);
@@ -891,6 +894,7 @@ fn NewSocket(comptime ssl: bool) type {
reffer: JSC.Ref = JSC.Ref.init(),
last_4: [4]u8 = .{ 0, 0, 0, 0 },
authorized: bool = false,
+ connection: ?Listener.UnixOrHost = null,
// TODO: switch to something that uses `visitAggregate` and have the
// `Listener` keep a list of all the sockets JSValue in there
@@ -1068,7 +1072,25 @@ fn NewSocket(comptime ssl: bool) type {
pub fn onOpen(this: *This, socket: Socket) void {
JSC.markBinding(@src());
- log("onOpen", .{});
+ log("onOpen ssl: {}", .{comptime ssl});
+
+ // Add SNI support for TLS (mongodb and others requires this)
+ if (comptime ssl) {
+ if (this.connection) |connection| {
+ if (connection == .host) {
+ const host = normalizeHost(connection.host.host);
+ if (host.len > 0) {
+ var ssl_ptr: *BoringSSL.SSL = @ptrCast(*BoringSSL.SSL, socket.getNativeHandle());
+ if (!ssl_ptr.isInitFinished()) {
+ var host__ = default_allocator.dupeZ(u8, host) catch unreachable;
+ defer default_allocator.free(host__);
+ ssl_ptr.setHostname(host__);
+ }
+ }
+ }
+ }
+ }
+
this.poll_ref.ref(this.handlers.vm);
this.detached = false;
this.socket = socket;
@@ -1145,6 +1167,7 @@ fn NewSocket(comptime ssl: bool) type {
}
pub fn onHandshake(this: *This, _: Socket, success: i32, ssl_error: uws.us_bun_verify_error_t) void {
+ log("onHandshake({d})", .{success});
JSC.markBinding(@src());
const authorized = if (success == 1) true else false;
@@ -1651,6 +1674,10 @@ fn NewSocket(comptime ssl: bool) type {
if (!this.socket.isClosed()) {
this.socket.close(0, null);
}
+ if (this.connection) |connection| {
+ connection.deinit();
+ this.connection = null;
+ }
this.markInactive();
this.poll_ref.unref(JSC.VirtualMachine.get());
}
diff --git a/src/bun.js/api/server.zig b/src/bun.js/api/server.zig
index 762d35bb6..823456777 100644
--- a/src/bun.js/api/server.zig
+++ b/src/bun.js/api/server.zig
@@ -91,6 +91,34 @@ const SendfileContext = struct {
const DateTime = bun.DateTime;
const linux = std.os.linux;
+const BlobFileContentResult = struct {
+ data: [:0]const u8,
+ fn init(comptime fieldname: []const u8, js_obj: JSC.JSValue, global: *JSC.JSGlobalObject, exception: JSC.C.ExceptionRef) ?BlobFileContentResult {
+ if (JSC.WebCore.Body.Value.fromJS(global, js_obj)) |body| {
+ if (body == .Blob and body.Blob.store != null and body.Blob.store.?.data == .file) {
+ var fs: JSC.Node.NodeFS = .{};
+ const read = fs.readFileWithOptions(.{ .path = body.Blob.store.?.data.file.pathlike }, .sync, .null_terminated);
+ switch (read) {
+ .err => {
+ global.throwValue(read.err.toJSC(global));
+ return .{ .data = "" };
+ },
+ else => {
+ const str = read.result.null_terminated;
+ if (str.len > 0) {
+ return .{ .data = str };
+ }
+ JSC.throwInvalidArguments(std.fmt.comptimePrint("Invalid {s} file", .{fieldname}), .{}, global, exception);
+ return .{ .data = str };
+ },
+ }
+ }
+ }
+
+ return null;
+ }
+};
+
pub const ServerConfig = struct {
port: u16 = 0,
hostname: [*:0]const u8 = "localhost",
@@ -276,8 +304,20 @@ pub const ServerConfig = struct {
valid_count += 1;
any = true;
}
+ } else if (BlobFileContentResult.init("key", item, global, exception)) |content| {
+ if (content.data.len > 0) {
+ native_array[valid_count] = content.data.ptr;
+ valid_count += 1;
+ any = true;
+ } else {
+ arena.deinit();
+ // mark and free all CA's
+ result.cert = native_array;
+ result.deinit();
+ return null;
+ }
} else {
- global.throwInvalidArguments("key argument must be an array containing string, Buffer or TypedArray", .{});
+ global.throwInvalidArguments("key argument must be an string, Buffer, TypedArray, BunFile or an array containing string, Buffer, TypedArray or BunFile", .{});
arena.deinit();
// mark and free all keys
result.key = native_array;
@@ -296,10 +336,40 @@ pub const ServerConfig = struct {
result.key_count = valid_count;
}
+ } else if (BlobFileContentResult.init("key", js_obj, global, exception)) |content| {
+ if (content.data.len > 0) {
+ const native_array = bun.default_allocator.alloc([*c]const u8, 1) catch unreachable;
+ native_array[0] = content.data.ptr;
+ result.key = native_array;
+ result.key_count = 1;
+ any = true;
+ } else {
+ result.deinit();
+ return null;
+ }
} else {
- global.throwInvalidArguments("key argument must be an array containing string, Buffer or TypedArray", .{});
- result.deinit();
- return null;
+ const native_array = bun.default_allocator.alloc([*c]const u8, 1) catch unreachable;
+ var arena: std.heap.ArenaAllocator = std.heap.ArenaAllocator.init(bun.default_allocator);
+ if (JSC.Node.StringOrBuffer.fromJS(global, arena.allocator(), js_obj, exception)) |sb| {
+ const sliced = sb.slice();
+ if (sliced.len > 0) {
+ native_array[0] = bun.default_allocator.dupeZ(u8, sliced) catch unreachable;
+ any = true;
+ result.key = native_array;
+ result.key_count = 1;
+ } else {
+ bun.default_allocator.free(native_array);
+ }
+ } else {
+ global.throwInvalidArguments("key argument must be an string, Buffer, TypedArray, BunFile or an array containing string, Buffer, TypedArray or BunFile", .{});
+ arena.deinit();
+ // mark and free all certs
+ result.key = native_array;
+ result.deinit();
+ return null;
+ }
+
+ arena.deinit();
}
}
@@ -336,8 +406,20 @@ pub const ServerConfig = struct {
valid_count += 1;
any = true;
}
+ } else if (BlobFileContentResult.init("cert", item, global, exception)) |content| {
+ if (content.data.len > 0) {
+ native_array[valid_count] = content.data.ptr;
+ valid_count += 1;
+ any = true;
+ } else {
+ arena.deinit();
+ // mark and free all CA's
+ result.cert = native_array;
+ result.deinit();
+ return null;
+ }
} else {
- global.throwInvalidArguments("cert argument must be an array containing string, Buffer or TypedArray", .{});
+ global.throwInvalidArguments("cert argument must be an string, Buffer, TypedArray, BunFile or an array containing string, Buffer, TypedArray or BunFile", .{});
arena.deinit();
// mark and free all certs
result.cert = native_array;
@@ -356,19 +438,51 @@ pub const ServerConfig = struct {
result.cert_count = valid_count;
}
+ } else if (BlobFileContentResult.init("cert", js_obj, global, exception)) |content| {
+ if (content.data.len > 0) {
+ const native_array = bun.default_allocator.alloc([*c]const u8, 1) catch unreachable;
+ native_array[0] = content.data.ptr;
+ result.cert = native_array;
+ result.cert_count = 1;
+ any = true;
+ } else {
+ result.deinit();
+ return null;
+ }
} else {
- global.throwInvalidArguments("cert argument must be an array containing string, Buffer or TypedArray", .{});
- result.deinit();
- return null;
+ const native_array = bun.default_allocator.alloc([*c]const u8, 1) catch unreachable;
+ var arena: std.heap.ArenaAllocator = std.heap.ArenaAllocator.init(bun.default_allocator);
+ if (JSC.Node.StringOrBuffer.fromJS(global, arena.allocator(), js_obj, exception)) |sb| {
+ const sliced = sb.slice();
+ if (sliced.len > 0) {
+ native_array[0] = bun.default_allocator.dupeZ(u8, sliced) catch unreachable;
+ any = true;
+ result.cert = native_array;
+ result.cert_count = 1;
+ } else {
+ bun.default_allocator.free(native_array);
+ }
+ } else {
+ global.throwInvalidArguments("cert argument must be an string, Buffer, TypedArray, BunFile or an array containing string, Buffer, TypedArray or BunFile", .{});
+ arena.deinit();
+ // mark and free all certs
+ result.cert = native_array;
+ result.deinit();
+ return null;
+ }
+
+ arena.deinit();
}
}
if (obj.getTruthy(global, "requestCert")) |request_cert| {
result.request_cert = if (request_cert.asBoolean()) 1 else 0;
+ any = true;
}
if (obj.getTruthy(global, "rejectUnauthorized")) |reject_unauthorized| {
result.reject_unauthorized = if (reject_unauthorized.asBoolean()) 1 else 0;
+ any = true;
}
if (obj.getTruthy(global, "ciphers")) |ssl_ciphers| {
@@ -379,79 +493,119 @@ pub const ServerConfig = struct {
any = true;
}
}
-
- // Optional
- if (any) {
- if (obj.getTruthy(global, "secureOptions")) |secure_options| {
- if (secure_options.isNumber()) {
- result.secure_options = secure_options.toU32();
- }
+ if (obj.getTruthy(global, "serverName")) |server_name| {
+ var sliced = server_name.toSlice(global, bun.default_allocator);
+ defer sliced.deinit();
+ if (sliced.len > 0) {
+ result.server_name = bun.default_allocator.dupeZ(u8, sliced.slice()) catch unreachable;
+ any = true;
}
+ }
- if (obj.getTruthy(global, "serverName")) |key_file_name| {
- var sliced = key_file_name.toSlice(global, bun.default_allocator);
- defer sliced.deinit();
- if (sliced.len > 0) {
- result.server_name = bun.default_allocator.dupeZ(u8, sliced.slice()) catch unreachable;
- }
- }
+ if (obj.getTruthy(global, "ca")) |js_obj| {
+ if (js_obj.jsType().isArray()) {
+ const count = js_obj.getLengthOfArray(global);
+ if (count > 0) {
+ const native_array = bun.default_allocator.alloc([*c]const u8, count) catch unreachable;
+
+ var i: u32 = 0;
+ var valid_count: u32 = 0;
- if (obj.getTruthy(global, "ca")) |js_obj| {
- if (js_obj.jsType().isArray()) {
- const count = js_obj.getLengthOfArray(global);
- if (count > 0) {
- const native_array = bun.default_allocator.alloc([*c]const u8, count) catch unreachable;
-
- var i: u32 = 0;
- var valid_count: u32 = 0;
-
- var arena: std.heap.ArenaAllocator = std.heap.ArenaAllocator.init(bun.default_allocator);
- while (i < count) : (i += 1) {
- const item = js_obj.getIndex(global, i);
- if (JSC.Node.StringOrBuffer.fromJS(global, arena.allocator(), item, exception)) |sb| {
- const sliced = sb.slice();
- if (sliced.len > 0) {
- native_array[valid_count] = bun.default_allocator.dupeZ(u8, sliced) catch unreachable;
- valid_count += 1;
- any = true;
- }
+ var arena: std.heap.ArenaAllocator = std.heap.ArenaAllocator.init(bun.default_allocator);
+ while (i < count) : (i += 1) {
+ const item = js_obj.getIndex(global, i);
+ if (JSC.Node.StringOrBuffer.fromJS(global, arena.allocator(), item, exception)) |sb| {
+ const sliced = sb.slice();
+ if (sliced.len > 0) {
+ native_array[valid_count] = bun.default_allocator.dupeZ(u8, sliced) catch unreachable;
+ valid_count += 1;
+ any = true;
+ }
+ } else if (BlobFileContentResult.init("ca", item, global, exception)) |content| {
+ if (content.data.len > 0) {
+ native_array[valid_count] = content.data.ptr;
+ valid_count += 1;
+ any = true;
} else {
- global.throwInvalidArguments("ca argument must be an array containing string, Buffer or TypedArray", .{});
arena.deinit();
// mark and free all CA's
result.cert = native_array;
result.deinit();
return null;
}
+ } else {
+ global.throwInvalidArguments("ca argument must be an string, Buffer, TypedArray, BunFile or an array containing string, Buffer, TypedArray or BunFile", .{});
+ arena.deinit();
+ // mark and free all CA's
+ result.cert = native_array;
+ result.deinit();
+ return null;
}
+ }
- arena.deinit();
+ arena.deinit();
- if (valid_count == 0) {
- bun.default_allocator.free(native_array);
- } else {
- result.ca = native_array;
- }
+ if (valid_count == 0) {
+ bun.default_allocator.free(native_array);
+ } else {
+ result.ca = native_array;
+ }
- result.ca_count = valid_count;
+ result.ca_count = valid_count;
+ }
+ } else if (BlobFileContentResult.init("ca", js_obj, global, exception)) |content| {
+ if (content.data.len > 0) {
+ const native_array = bun.default_allocator.alloc([*c]const u8, 1) catch unreachable;
+ native_array[0] = content.data.ptr;
+ result.ca = native_array;
+ result.ca_count = 1;
+ any = true;
+ } else {
+ result.deinit();
+ return null;
+ }
+ } else {
+ const native_array = bun.default_allocator.alloc([*c]const u8, 1) catch unreachable;
+ var arena: std.heap.ArenaAllocator = std.heap.ArenaAllocator.init(bun.default_allocator);
+ if (JSC.Node.StringOrBuffer.fromJS(global, arena.allocator(), js_obj, exception)) |sb| {
+ const sliced = sb.slice();
+ if (sliced.len > 0) {
+ native_array[0] = bun.default_allocator.dupeZ(u8, sliced) catch unreachable;
+ any = true;
+ result.ca = native_array;
+ result.ca_count = 1;
+ } else {
+ bun.default_allocator.free(native_array);
}
} else {
- global.throwInvalidArguments("cert argument must be an array containing string, Buffer or TypedArray", .{});
+ JSC.throwInvalidArguments("ca argument must be an string, Buffer, TypedArray, BunFile or an array containing string, Buffer, TypedArray or BunFile", .{}, global, exception);
+ arena.deinit();
+ // mark and free all certs
+ result.ca = native_array;
result.deinit();
return null;
}
+ arena.deinit();
}
+ }
- if (obj.getTruthy(global, "caFile")) |ca_file_name| {
- var sliced = ca_file_name.toSlice(global, bun.default_allocator);
- defer sliced.deinit();
- if (sliced.len > 0) {
- result.ca_file_name = bun.default_allocator.dupeZ(u8, sliced.slice()) catch unreachable;
- if (std.os.system.access(result.ca_file_name, std.os.F_OK) != 0) {
- JSC.throwInvalidArguments("Invalid caFile path", .{}, global, exception);
- result.deinit();
- return null;
- }
+ if (obj.getTruthy(global, "caFile")) |ca_file_name| {
+ var sliced = ca_file_name.toSlice(global, bun.default_allocator);
+ defer sliced.deinit();
+ if (sliced.len > 0) {
+ result.ca_file_name = bun.default_allocator.dupeZ(u8, sliced.slice()) catch unreachable;
+ if (std.os.system.access(result.ca_file_name, std.os.F_OK) != 0) {
+ JSC.throwInvalidArguments("Invalid caFile path", .{}, global, exception);
+ result.deinit();
+ return null;
+ }
+ }
+ }
+ // Optional
+ if (any) {
+ if (obj.getTruthy(global, "secureOptions")) |secure_options| {
+ if (secure_options.isNumber()) {
+ result.secure_options = secure_options.toU32();
}
}
@@ -5142,16 +5296,7 @@ pub fn NewServer(comptime ssl_enabled_: bool, comptime debug_mode_: bool) type {
if (ssl_enabled) {
BoringSSL.load();
const ssl_config = this.config.ssl_config orelse @panic("Assertion failure: ssl_config");
- this.app = App.create(
- .{
- .key_file_name = ssl_config.key_file_name,
- .cert_file_name = ssl_config.cert_file_name,
- .passphrase = ssl_config.passphrase,
- .dh_params_file_name = ssl_config.dh_params_file_name,
- .ca_file_name = ssl_config.ca_file_name,
- .ssl_prefer_low_memory_usage = @as(c_int, @boolToInt(ssl_config.low_memory_mode)),
- },
- );
+ this.app = App.create(ssl_config.asUSockets());
if (ssl_config.server_name != null and std.mem.span(ssl_config.server_name).len > 0) {
this.app.addServerName(ssl_config.server_name);