mirror of
https://github.com/oven-sh/bun
synced 2026-02-12 03:48:56 +00:00
Split server.zig into more files (#20139)
Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Jarred-Sumner <709451+Jarred-Sumner@users.noreply.github.com>
This commit is contained in:
@@ -55,11 +55,17 @@ src/bun.js/api/html_rewriter.zig
|
||||
src/bun.js/api/JSBundler.zig
|
||||
src/bun.js/api/JSTranspiler.zig
|
||||
src/bun.js/api/server.zig
|
||||
src/bun.js/api/server/AnyRequestContext.zig
|
||||
src/bun.js/api/server/HTMLBundle.zig
|
||||
src/bun.js/api/server/HTTPStatusText.zig
|
||||
src/bun.js/api/server/InspectorBunFrontendDevServerAgent.zig
|
||||
src/bun.js/api/server/NodeHTTPResponse.zig
|
||||
src/bun.js/api/server/RequestContext.zig
|
||||
src/bun.js/api/server/ServerConfig.zig
|
||||
src/bun.js/api/server/ServerWebSocket.zig
|
||||
src/bun.js/api/server/SSLConfig.zig
|
||||
src/bun.js/api/server/StaticRoute.zig
|
||||
src/bun.js/api/server/WebSocketServerContext.zig
|
||||
src/bun.js/api/streams.classes.zig
|
||||
src/bun.js/api/Timer.zig
|
||||
src/bun.js/api/TOMLObject.zig
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
229
src/bun.js/api/server/AnyRequestContext.zig
Normal file
229
src/bun.js/api/server/AnyRequestContext.zig
Normal file
@@ -0,0 +1,229 @@
|
||||
//! A generic wrapper for the HTTP(s) Server`RequestContext`s.
|
||||
//! Only really exists because of `NewServer()` and `NewRequestContext()` generics.
|
||||
const AnyRequestContext = @This();
|
||||
|
||||
pub const Pointer = bun.TaggedPointerUnion(.{
|
||||
HTTPServer.RequestContext,
|
||||
HTTPSServer.RequestContext,
|
||||
DebugHTTPServer.RequestContext,
|
||||
DebugHTTPSServer.RequestContext,
|
||||
});
|
||||
|
||||
tagged_pointer: Pointer,
|
||||
|
||||
pub const Null: @This() = .{ .tagged_pointer = Pointer.Null };
|
||||
|
||||
pub fn init(request_ctx: anytype) AnyRequestContext {
|
||||
return .{ .tagged_pointer = Pointer.init(request_ctx) };
|
||||
}
|
||||
|
||||
pub fn memoryCost(self: AnyRequestContext) usize {
|
||||
if (self.tagged_pointer.isNull()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (self.tagged_pointer.tag()) {
|
||||
@field(Pointer.Tag, bun.meta.typeBaseName(@typeName(HTTPServer.RequestContext))) => {
|
||||
return self.tagged_pointer.as(HTTPServer.RequestContext).memoryCost();
|
||||
},
|
||||
@field(Pointer.Tag, bun.meta.typeBaseName(@typeName(HTTPSServer.RequestContext))) => {
|
||||
return self.tagged_pointer.as(HTTPSServer.RequestContext).memoryCost();
|
||||
},
|
||||
@field(Pointer.Tag, bun.meta.typeBaseName(@typeName(DebugHTTPServer.RequestContext))) => {
|
||||
return self.tagged_pointer.as(DebugHTTPServer.RequestContext).memoryCost();
|
||||
},
|
||||
@field(Pointer.Tag, bun.meta.typeBaseName(@typeName(DebugHTTPSServer.RequestContext))) => {
|
||||
return self.tagged_pointer.as(DebugHTTPSServer.RequestContext).memoryCost();
|
||||
},
|
||||
else => @panic("Unexpected AnyRequestContext tag"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(self: AnyRequestContext, comptime T: type) ?*T {
|
||||
return self.tagged_pointer.get(T);
|
||||
}
|
||||
|
||||
pub fn setTimeout(self: AnyRequestContext, seconds: c_uint) bool {
|
||||
if (self.tagged_pointer.isNull()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (self.tagged_pointer.tag()) {
|
||||
@field(Pointer.Tag, bun.meta.typeBaseName(@typeName(HTTPServer.RequestContext))) => {
|
||||
return self.tagged_pointer.as(HTTPServer.RequestContext).setTimeout(seconds);
|
||||
},
|
||||
@field(Pointer.Tag, bun.meta.typeBaseName(@typeName(HTTPSServer.RequestContext))) => {
|
||||
return self.tagged_pointer.as(HTTPSServer.RequestContext).setTimeout(seconds);
|
||||
},
|
||||
@field(Pointer.Tag, bun.meta.typeBaseName(@typeName(DebugHTTPServer.RequestContext))) => {
|
||||
return self.tagged_pointer.as(DebugHTTPServer.RequestContext).setTimeout(seconds);
|
||||
},
|
||||
@field(Pointer.Tag, bun.meta.typeBaseName(@typeName(DebugHTTPSServer.RequestContext))) => {
|
||||
return self.tagged_pointer.as(DebugHTTPSServer.RequestContext).setTimeout(seconds);
|
||||
},
|
||||
else => @panic("Unexpected AnyRequestContext tag"),
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
pub fn setCookies(self: AnyRequestContext, cookie_map: ?*JSC.WebCore.CookieMap) void {
|
||||
if (self.tagged_pointer.isNull()) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (self.tagged_pointer.tag()) {
|
||||
@field(Pointer.Tag, bun.meta.typeBaseName(@typeName(HTTPServer.RequestContext))) => {
|
||||
return self.tagged_pointer.as(HTTPServer.RequestContext).setCookies(cookie_map);
|
||||
},
|
||||
@field(Pointer.Tag, bun.meta.typeBaseName(@typeName(HTTPSServer.RequestContext))) => {
|
||||
return self.tagged_pointer.as(HTTPSServer.RequestContext).setCookies(cookie_map);
|
||||
},
|
||||
@field(Pointer.Tag, bun.meta.typeBaseName(@typeName(DebugHTTPServer.RequestContext))) => {
|
||||
return self.tagged_pointer.as(DebugHTTPServer.RequestContext).setCookies(cookie_map);
|
||||
},
|
||||
@field(Pointer.Tag, bun.meta.typeBaseName(@typeName(DebugHTTPSServer.RequestContext))) => {
|
||||
return self.tagged_pointer.as(DebugHTTPSServer.RequestContext).setCookies(cookie_map);
|
||||
},
|
||||
else => @panic("Unexpected AnyRequestContext tag"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn enableTimeoutEvents(self: AnyRequestContext) void {
|
||||
if (self.tagged_pointer.isNull()) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (self.tagged_pointer.tag()) {
|
||||
@field(Pointer.Tag, bun.meta.typeBaseName(@typeName(HTTPServer.RequestContext))) => {
|
||||
return self.tagged_pointer.as(HTTPServer.RequestContext).setTimeoutHandler();
|
||||
},
|
||||
@field(Pointer.Tag, bun.meta.typeBaseName(@typeName(HTTPSServer.RequestContext))) => {
|
||||
return self.tagged_pointer.as(HTTPSServer.RequestContext).setTimeoutHandler();
|
||||
},
|
||||
@field(Pointer.Tag, bun.meta.typeBaseName(@typeName(DebugHTTPServer.RequestContext))) => {
|
||||
return self.tagged_pointer.as(DebugHTTPServer.RequestContext).setTimeoutHandler();
|
||||
},
|
||||
@field(Pointer.Tag, bun.meta.typeBaseName(@typeName(DebugHTTPSServer.RequestContext))) => {
|
||||
return self.tagged_pointer.as(DebugHTTPSServer.RequestContext).setTimeoutHandler();
|
||||
},
|
||||
else => @panic("Unexpected AnyRequestContext tag"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getRemoteSocketInfo(self: AnyRequestContext) ?uws.SocketAddress {
|
||||
if (self.tagged_pointer.isNull()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (self.tagged_pointer.tag()) {
|
||||
@field(Pointer.Tag, bun.meta.typeBaseName(@typeName(HTTPServer.RequestContext))) => {
|
||||
return self.tagged_pointer.as(HTTPServer.RequestContext).getRemoteSocketInfo();
|
||||
},
|
||||
@field(Pointer.Tag, bun.meta.typeBaseName(@typeName(HTTPSServer.RequestContext))) => {
|
||||
return self.tagged_pointer.as(HTTPSServer.RequestContext).getRemoteSocketInfo();
|
||||
},
|
||||
@field(Pointer.Tag, bun.meta.typeBaseName(@typeName(DebugHTTPServer.RequestContext))) => {
|
||||
return self.tagged_pointer.as(DebugHTTPServer.RequestContext).getRemoteSocketInfo();
|
||||
},
|
||||
@field(Pointer.Tag, bun.meta.typeBaseName(@typeName(DebugHTTPSServer.RequestContext))) => {
|
||||
return self.tagged_pointer.as(DebugHTTPSServer.RequestContext).getRemoteSocketInfo();
|
||||
},
|
||||
else => @panic("Unexpected AnyRequestContext tag"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn detachRequest(self: AnyRequestContext) void {
|
||||
if (self.tagged_pointer.isNull()) {
|
||||
return;
|
||||
}
|
||||
switch (self.tagged_pointer.tag()) {
|
||||
@field(Pointer.Tag, bun.meta.typeBaseName(@typeName(HTTPServer.RequestContext))) => {
|
||||
self.tagged_pointer.as(HTTPServer.RequestContext).req = null;
|
||||
},
|
||||
@field(Pointer.Tag, bun.meta.typeBaseName(@typeName(HTTPSServer.RequestContext))) => {
|
||||
self.tagged_pointer.as(HTTPSServer.RequestContext).req = null;
|
||||
},
|
||||
@field(Pointer.Tag, bun.meta.typeBaseName(@typeName(DebugHTTPServer.RequestContext))) => {
|
||||
self.tagged_pointer.as(DebugHTTPServer.RequestContext).req = null;
|
||||
},
|
||||
@field(Pointer.Tag, bun.meta.typeBaseName(@typeName(DebugHTTPSServer.RequestContext))) => {
|
||||
self.tagged_pointer.as(DebugHTTPSServer.RequestContext).req = null;
|
||||
},
|
||||
else => @panic("Unexpected AnyRequestContext tag"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Wont actually set anything if `self` is `.none`
|
||||
pub fn setRequest(self: AnyRequestContext, req: *uws.Request) void {
|
||||
if (self.tagged_pointer.isNull()) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (self.tagged_pointer.tag()) {
|
||||
@field(Pointer.Tag, bun.meta.typeBaseName(@typeName(HTTPServer.RequestContext))) => {
|
||||
self.tagged_pointer.as(HTTPServer.RequestContext).req = req;
|
||||
},
|
||||
@field(Pointer.Tag, bun.meta.typeBaseName(@typeName(HTTPSServer.RequestContext))) => {
|
||||
self.tagged_pointer.as(HTTPSServer.RequestContext).req = req;
|
||||
},
|
||||
@field(Pointer.Tag, bun.meta.typeBaseName(@typeName(DebugHTTPServer.RequestContext))) => {
|
||||
self.tagged_pointer.as(DebugHTTPServer.RequestContext).req = req;
|
||||
},
|
||||
@field(Pointer.Tag, bun.meta.typeBaseName(@typeName(DebugHTTPSServer.RequestContext))) => {
|
||||
self.tagged_pointer.as(DebugHTTPSServer.RequestContext).req = req;
|
||||
},
|
||||
else => @panic("Unexpected AnyRequestContext tag"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getRequest(self: AnyRequestContext) ?*uws.Request {
|
||||
if (self.tagged_pointer.isNull()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (self.tagged_pointer.tag()) {
|
||||
@field(Pointer.Tag, bun.meta.typeBaseName(@typeName(HTTPServer.RequestContext))) => {
|
||||
return self.tagged_pointer.as(HTTPServer.RequestContext).req;
|
||||
},
|
||||
@field(Pointer.Tag, bun.meta.typeBaseName(@typeName(HTTPSServer.RequestContext))) => {
|
||||
return self.tagged_pointer.as(HTTPSServer.RequestContext).req;
|
||||
},
|
||||
@field(Pointer.Tag, bun.meta.typeBaseName(@typeName(DebugHTTPServer.RequestContext))) => {
|
||||
return self.tagged_pointer.as(DebugHTTPServer.RequestContext).req;
|
||||
},
|
||||
@field(Pointer.Tag, bun.meta.typeBaseName(@typeName(DebugHTTPSServer.RequestContext))) => {
|
||||
return self.tagged_pointer.as(DebugHTTPSServer.RequestContext).req;
|
||||
},
|
||||
else => @panic("Unexpected AnyRequestContext tag"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deref(self: AnyRequestContext) void {
|
||||
if (self.tagged_pointer.isNull()) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (self.tagged_pointer.tag()) {
|
||||
@field(Pointer.Tag, bun.meta.typeBaseName(@typeName(HTTPServer.RequestContext))) => {
|
||||
self.tagged_pointer.as(HTTPServer.RequestContext).deref();
|
||||
},
|
||||
@field(Pointer.Tag, bun.meta.typeBaseName(@typeName(HTTPSServer.RequestContext))) => {
|
||||
self.tagged_pointer.as(HTTPSServer.RequestContext).deref();
|
||||
},
|
||||
@field(Pointer.Tag, bun.meta.typeBaseName(@typeName(DebugHTTPServer.RequestContext))) => {
|
||||
self.tagged_pointer.as(DebugHTTPServer.RequestContext).deref();
|
||||
},
|
||||
@field(Pointer.Tag, bun.meta.typeBaseName(@typeName(DebugHTTPSServer.RequestContext))) => {
|
||||
self.tagged_pointer.as(DebugHTTPSServer.RequestContext).deref();
|
||||
},
|
||||
else => @panic("Unexpected AnyRequestContext tag"),
|
||||
}
|
||||
}
|
||||
|
||||
const bun = @import("bun");
|
||||
const JSC = bun.JSC;
|
||||
const uws = bun.uws;
|
||||
const HTTPServer = @import("../server.zig").HTTPServer;
|
||||
const HTTPSServer = @import("../server.zig").HTTPSServer;
|
||||
const DebugHTTPServer = @import("../server.zig").DebugHTTPServer;
|
||||
const DebugHTTPSServer = @import("../server.zig").DebugHTTPSServer;
|
||||
68
src/bun.js/api/server/HTTPStatusText.zig
Normal file
68
src/bun.js/api/server/HTTPStatusText.zig
Normal file
@@ -0,0 +1,68 @@
|
||||
pub fn get(code: u16) ?[]const u8 {
|
||||
return switch (code) {
|
||||
100 => "100 Continue",
|
||||
101 => "101 Switching protocols",
|
||||
102 => "102 Processing",
|
||||
103 => "103 Early Hints",
|
||||
200 => "200 OK",
|
||||
201 => "201 Created",
|
||||
202 => "202 Accepted",
|
||||
203 => "203 Non-Authoritative Information",
|
||||
204 => "204 No Content",
|
||||
205 => "205 Reset Content",
|
||||
206 => "206 Partial Content",
|
||||
207 => "207 Multi-Status",
|
||||
208 => "208 Already Reported",
|
||||
226 => "226 IM Used",
|
||||
300 => "300 Multiple Choices",
|
||||
301 => "301 Moved Permanently",
|
||||
302 => "302 Found",
|
||||
303 => "303 See Other",
|
||||
304 => "304 Not Modified",
|
||||
305 => "305 Use Proxy",
|
||||
306 => "306 Switch Proxy",
|
||||
307 => "307 Temporary Redirect",
|
||||
308 => "308 Permanent Redirect",
|
||||
400 => "400 Bad Request",
|
||||
401 => "401 Unauthorized",
|
||||
402 => "402 Payment Required",
|
||||
403 => "403 Forbidden",
|
||||
404 => "404 Not Found",
|
||||
405 => "405 Method Not Allowed",
|
||||
406 => "406 Not Acceptable",
|
||||
407 => "407 Proxy Authentication Required",
|
||||
408 => "408 Request Timeout",
|
||||
409 => "409 Conflict",
|
||||
410 => "410 Gone",
|
||||
411 => "411 Length Required",
|
||||
412 => "412 Precondition Failed",
|
||||
413 => "413 Payload Too Large",
|
||||
414 => "414 URI Too Long",
|
||||
415 => "415 Unsupported Media Type",
|
||||
416 => "416 Range Not Satisfiable",
|
||||
417 => "417 Expectation Failed",
|
||||
418 => "418 I'm a Teapot",
|
||||
421 => "421 Misdirected Request",
|
||||
422 => "422 Unprocessable Entity",
|
||||
423 => "423 Locked",
|
||||
424 => "424 Failed Dependency",
|
||||
425 => "425 Too Early",
|
||||
426 => "426 Upgrade Required",
|
||||
428 => "428 Precondition Required",
|
||||
429 => "429 Too Many Requests",
|
||||
431 => "431 Request Header Fields Too Large",
|
||||
451 => "451 Unavailable For Legal Reasons",
|
||||
500 => "500 Internal Server Error",
|
||||
501 => "501 Not Implemented",
|
||||
502 => "502 Bad Gateway",
|
||||
503 => "503 Service Unavailable",
|
||||
504 => "504 Gateway Timeout",
|
||||
505 => "505 HTTP Version Not Supported",
|
||||
506 => "506 Variant Also Negotiates",
|
||||
507 => "507 Insufficient Storage",
|
||||
508 => "508 Loop Detected",
|
||||
510 => "510 Not Extended",
|
||||
511 => "511 Network Authentication Required",
|
||||
else => null,
|
||||
};
|
||||
}
|
||||
2539
src/bun.js/api/server/RequestContext.zig
Normal file
2539
src/bun.js/api/server/RequestContext.zig
Normal file
File diff suppressed because it is too large
Load Diff
620
src/bun.js/api/server/SSLConfig.zig
Normal file
620
src/bun.js/api/server/SSLConfig.zig
Normal file
@@ -0,0 +1,620 @@
|
||||
const SSLConfig = @This();
|
||||
|
||||
requires_custom_request_ctx: bool = false,
|
||||
server_name: [*c]const u8 = null,
|
||||
|
||||
key_file_name: [*c]const u8 = null,
|
||||
cert_file_name: [*c]const u8 = null,
|
||||
|
||||
ca_file_name: [*c]const u8 = null,
|
||||
dh_params_file_name: [*c]const u8 = null,
|
||||
|
||||
passphrase: [*c]const u8 = null,
|
||||
low_memory_mode: bool = false,
|
||||
|
||||
key: ?[][*c]const u8 = null,
|
||||
key_count: u32 = 0,
|
||||
|
||||
cert: ?[][*c]const u8 = null,
|
||||
cert_count: u32 = 0,
|
||||
|
||||
ca: ?[][*c]const u8 = null,
|
||||
ca_count: u32 = 0,
|
||||
|
||||
secure_options: u32 = 0,
|
||||
request_cert: i32 = 0,
|
||||
reject_unauthorized: i32 = 0,
|
||||
ssl_ciphers: ?[*:0]const u8 = null,
|
||||
protos: ?[*:0]const u8 = null,
|
||||
protos_len: usize = 0,
|
||||
client_renegotiation_limit: u32 = 0,
|
||||
client_renegotiation_window: u32 = 0,
|
||||
|
||||
const BlobFileContentResult = struct {
|
||||
data: [:0]const u8,
|
||||
|
||||
fn init(comptime fieldname: []const u8, js_obj: JSC.JSValue, global: *JSC.JSGlobalObject) bun.JSError!?BlobFileContentResult {
|
||||
{
|
||||
const body = try JSC.WebCore.Body.Value.fromJS(global, js_obj);
|
||||
if (body == .Blob and body.Blob.store != null and body.Blob.store.?.data == .file) {
|
||||
var fs: JSC.Node.fs.NodeFS = .{};
|
||||
const read = fs.readFileWithOptions(.{ .path = body.Blob.store.?.data.file.pathlike }, .sync, .null_terminated);
|
||||
switch (read) {
|
||||
.err => {
|
||||
return global.throwValue(read.err.toJSC(global));
|
||||
},
|
||||
else => {
|
||||
const str = read.result.null_terminated;
|
||||
if (str.len > 0) {
|
||||
return .{ .data = str };
|
||||
}
|
||||
return global.throwInvalidArguments(std.fmt.comptimePrint("Invalid {s} file", .{fieldname}), .{});
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
pub fn asUSockets(this: SSLConfig) uws.SocketContext.BunSocketContextOptions {
|
||||
var ctx_opts: uws.SocketContext.BunSocketContextOptions = .{};
|
||||
|
||||
if (this.key_file_name != null)
|
||||
ctx_opts.key_file_name = this.key_file_name;
|
||||
if (this.cert_file_name != null)
|
||||
ctx_opts.cert_file_name = this.cert_file_name;
|
||||
if (this.ca_file_name != null)
|
||||
ctx_opts.ca_file_name = this.ca_file_name;
|
||||
if (this.dh_params_file_name != null)
|
||||
ctx_opts.dh_params_file_name = this.dh_params_file_name;
|
||||
if (this.passphrase != null)
|
||||
ctx_opts.passphrase = this.passphrase;
|
||||
ctx_opts.ssl_prefer_low_memory_usage = @intFromBool(this.low_memory_mode);
|
||||
|
||||
if (this.key) |key| {
|
||||
ctx_opts.key = key.ptr;
|
||||
ctx_opts.key_count = this.key_count;
|
||||
}
|
||||
if (this.cert) |cert| {
|
||||
ctx_opts.cert = cert.ptr;
|
||||
ctx_opts.cert_count = this.cert_count;
|
||||
}
|
||||
if (this.ca) |ca| {
|
||||
ctx_opts.ca = ca.ptr;
|
||||
ctx_opts.ca_count = this.ca_count;
|
||||
}
|
||||
|
||||
if (this.ssl_ciphers != null) {
|
||||
ctx_opts.ssl_ciphers = this.ssl_ciphers;
|
||||
}
|
||||
ctx_opts.request_cert = this.request_cert;
|
||||
ctx_opts.reject_unauthorized = this.reject_unauthorized;
|
||||
|
||||
return ctx_opts;
|
||||
}
|
||||
|
||||
pub fn isSame(thisConfig: *const SSLConfig, otherConfig: *const SSLConfig) bool {
|
||||
{ //strings
|
||||
const fields = .{
|
||||
"server_name",
|
||||
"key_file_name",
|
||||
"cert_file_name",
|
||||
"ca_file_name",
|
||||
"dh_params_file_name",
|
||||
"passphrase",
|
||||
"ssl_ciphers",
|
||||
"protos",
|
||||
};
|
||||
|
||||
inline for (fields) |field| {
|
||||
const lhs = @field(thisConfig, field);
|
||||
const rhs = @field(otherConfig, field);
|
||||
if (lhs != null and rhs != null) {
|
||||
if (!stringsEqual(lhs, rhs))
|
||||
return false;
|
||||
} else if (lhs != null or rhs != null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
//numbers
|
||||
const fields = .{ "secure_options", "request_cert", "reject_unauthorized", "low_memory_mode" };
|
||||
|
||||
inline for (fields) |field| {
|
||||
const lhs = @field(thisConfig, field);
|
||||
const rhs = @field(otherConfig, field);
|
||||
if (lhs != rhs)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// complex fields
|
||||
const fields = .{ "key", "ca", "cert" };
|
||||
inline for (fields) |field| {
|
||||
const lhs_count = @field(thisConfig, field ++ "_count");
|
||||
const rhs_count = @field(otherConfig, field ++ "_count");
|
||||
if (lhs_count != rhs_count)
|
||||
return false;
|
||||
if (lhs_count > 0) {
|
||||
const lhs = @field(thisConfig, field);
|
||||
const rhs = @field(otherConfig, field);
|
||||
for (0..lhs_count) |i| {
|
||||
if (!stringsEqual(lhs.?[i], rhs.?[i]))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
fn stringsEqual(a: [*c]const u8, b: [*c]const u8) bool {
|
||||
const lhs = bun.asByteSlice(a);
|
||||
const rhs = bun.asByteSlice(b);
|
||||
return strings.eqlLong(lhs, rhs, true);
|
||||
}
|
||||
|
||||
pub fn deinit(this: *SSLConfig) void {
|
||||
const fields = .{
|
||||
"server_name",
|
||||
"key_file_name",
|
||||
"cert_file_name",
|
||||
"ca_file_name",
|
||||
"dh_params_file_name",
|
||||
"passphrase",
|
||||
"ssl_ciphers",
|
||||
"protos",
|
||||
};
|
||||
|
||||
inline for (fields) |field| {
|
||||
if (@field(this, field)) |slice_ptr| {
|
||||
const slice = std.mem.span(slice_ptr);
|
||||
if (slice.len > 0) {
|
||||
bun.freeSensitive(bun.default_allocator, slice);
|
||||
}
|
||||
@field(this, field) = "";
|
||||
}
|
||||
}
|
||||
|
||||
if (this.cert) |cert| {
|
||||
for (0..this.cert_count) |i| {
|
||||
const slice = std.mem.span(cert[i]);
|
||||
if (slice.len > 0) {
|
||||
bun.freeSensitive(bun.default_allocator, slice);
|
||||
}
|
||||
}
|
||||
|
||||
bun.default_allocator.free(cert);
|
||||
this.cert = null;
|
||||
}
|
||||
|
||||
if (this.key) |key| {
|
||||
for (0..this.key_count) |i| {
|
||||
const slice = std.mem.span(key[i]);
|
||||
if (slice.len > 0) {
|
||||
bun.freeSensitive(bun.default_allocator, slice);
|
||||
}
|
||||
}
|
||||
|
||||
bun.default_allocator.free(key);
|
||||
this.key = null;
|
||||
}
|
||||
|
||||
if (this.ca) |ca| {
|
||||
for (0..this.ca_count) |i| {
|
||||
const slice = std.mem.span(ca[i]);
|
||||
if (slice.len > 0) {
|
||||
bun.freeSensitive(bun.default_allocator, slice);
|
||||
}
|
||||
}
|
||||
|
||||
bun.default_allocator.free(ca);
|
||||
this.ca = null;
|
||||
}
|
||||
}
|
||||
|
||||
pub const zero = SSLConfig{};
|
||||
|
||||
pub fn fromJS(vm: *JSC.VirtualMachine, global: *JSC.JSGlobalObject, obj: JSC.JSValue) bun.JSError!?SSLConfig {
|
||||
var result = zero;
|
||||
errdefer result.deinit();
|
||||
|
||||
var arena: bun.ArenaAllocator = bun.ArenaAllocator.init(bun.default_allocator);
|
||||
defer arena.deinit();
|
||||
|
||||
if (!obj.isObject()) {
|
||||
return global.throwInvalidArguments("tls option expects an object", .{});
|
||||
}
|
||||
|
||||
var any = false;
|
||||
|
||||
result.reject_unauthorized = @intFromBool(vm.getTLSRejectUnauthorized());
|
||||
|
||||
// Required
|
||||
if (try obj.getTruthy(global, "keyFile")) |key_file_name| {
|
||||
var sliced = try key_file_name.toSlice(global, bun.default_allocator);
|
||||
defer sliced.deinit();
|
||||
if (sliced.len > 0) {
|
||||
result.key_file_name = try bun.default_allocator.dupeZ(u8, sliced.slice());
|
||||
if (std.posix.system.access(result.key_file_name, std.posix.F_OK) != 0) {
|
||||
return global.throwInvalidArguments("Unable to access keyFile path", .{});
|
||||
}
|
||||
any = true;
|
||||
result.requires_custom_request_ctx = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (try obj.getTruthy(global, "key")) |js_obj| {
|
||||
if (js_obj.jsType().isArray()) {
|
||||
const count = js_obj.getLength(global);
|
||||
if (count > 0) {
|
||||
const native_array = try bun.default_allocator.alloc([*c]const u8, count);
|
||||
|
||||
var valid_count: u32 = 0;
|
||||
for (0..count) |i| {
|
||||
const item = js_obj.getIndex(global, @intCast(i));
|
||||
if (try JSC.Node.StringOrBuffer.fromJS(global, arena.allocator(), item)) |sb| {
|
||||
defer sb.deinit();
|
||||
const sliced = sb.slice();
|
||||
if (sliced.len > 0) {
|
||||
native_array[valid_count] = try bun.default_allocator.dupeZ(u8, sliced);
|
||||
valid_count += 1;
|
||||
any = true;
|
||||
result.requires_custom_request_ctx = true;
|
||||
}
|
||||
} else if (try BlobFileContentResult.init("key", item, global)) |content| {
|
||||
if (content.data.len > 0) {
|
||||
native_array[valid_count] = content.data.ptr;
|
||||
valid_count += 1;
|
||||
result.requires_custom_request_ctx = true;
|
||||
any = true;
|
||||
} else {
|
||||
// mark and free all CA's
|
||||
result.cert = native_array;
|
||||
result.deinit();
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
// mark and free all keys
|
||||
result.key = native_array;
|
||||
return global.throwInvalidArguments("key argument must be an string, Buffer, TypedArray, BunFile or an array containing string, Buffer, TypedArray or BunFile", .{});
|
||||
}
|
||||
}
|
||||
|
||||
if (valid_count == 0) {
|
||||
bun.default_allocator.free(native_array);
|
||||
} else {
|
||||
result.key = native_array;
|
||||
}
|
||||
|
||||
result.key_count = valid_count;
|
||||
}
|
||||
} else if (try BlobFileContentResult.init("key", js_obj, global)) |content| {
|
||||
if (content.data.len > 0) {
|
||||
const native_array = try bun.default_allocator.alloc([*c]const u8, 1);
|
||||
native_array[0] = content.data.ptr;
|
||||
result.key = native_array;
|
||||
result.key_count = 1;
|
||||
any = true;
|
||||
result.requires_custom_request_ctx = true;
|
||||
} else {
|
||||
result.deinit();
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
const native_array = try bun.default_allocator.alloc([*c]const u8, 1);
|
||||
if (try JSC.Node.StringOrBuffer.fromJS(global, arena.allocator(), js_obj)) |sb| {
|
||||
defer sb.deinit();
|
||||
const sliced = sb.slice();
|
||||
if (sliced.len > 0) {
|
||||
native_array[0] = try bun.default_allocator.dupeZ(u8, sliced);
|
||||
any = true;
|
||||
result.requires_custom_request_ctx = true;
|
||||
result.key = native_array;
|
||||
result.key_count = 1;
|
||||
} else {
|
||||
bun.default_allocator.free(native_array);
|
||||
}
|
||||
} else {
|
||||
// mark and free all certs
|
||||
result.key = native_array;
|
||||
return global.throwInvalidArguments("key argument must be an string, Buffer, TypedArray, BunFile or an array containing string, Buffer, TypedArray or BunFile", .{});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (try obj.getTruthy(global, "certFile")) |cert_file_name| {
|
||||
var sliced = try cert_file_name.toSlice(global, bun.default_allocator);
|
||||
defer sliced.deinit();
|
||||
if (sliced.len > 0) {
|
||||
result.cert_file_name = try bun.default_allocator.dupeZ(u8, sliced.slice());
|
||||
if (std.posix.system.access(result.cert_file_name, std.posix.F_OK) != 0) {
|
||||
return global.throwInvalidArguments("Unable to access certFile path", .{});
|
||||
}
|
||||
any = true;
|
||||
result.requires_custom_request_ctx = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (try obj.getTruthy(global, "ALPNProtocols")) |protocols| {
|
||||
if (try JSC.Node.StringOrBuffer.fromJS(global, arena.allocator(), protocols)) |sb| {
|
||||
defer sb.deinit();
|
||||
const sliced = sb.slice();
|
||||
if (sliced.len > 0) {
|
||||
result.protos = try bun.default_allocator.dupeZ(u8, sliced);
|
||||
result.protos_len = sliced.len;
|
||||
}
|
||||
|
||||
any = true;
|
||||
result.requires_custom_request_ctx = true;
|
||||
} else {
|
||||
return global.throwInvalidArguments("ALPNProtocols argument must be an string, Buffer or TypedArray", .{});
|
||||
}
|
||||
}
|
||||
|
||||
if (try obj.getTruthy(global, "cert")) |js_obj| {
|
||||
if (js_obj.jsType().isArray()) {
|
||||
const count = js_obj.getLength(global);
|
||||
if (count > 0) {
|
||||
const native_array = try bun.default_allocator.alloc([*c]const u8, count);
|
||||
|
||||
var valid_count: u32 = 0;
|
||||
for (0..count) |i| {
|
||||
const item = js_obj.getIndex(global, @intCast(i));
|
||||
if (try JSC.Node.StringOrBuffer.fromJS(global, arena.allocator(), item)) |sb| {
|
||||
defer sb.deinit();
|
||||
const sliced = sb.slice();
|
||||
if (sliced.len > 0) {
|
||||
native_array[valid_count] = try bun.default_allocator.dupeZ(u8, sliced);
|
||||
valid_count += 1;
|
||||
any = true;
|
||||
result.requires_custom_request_ctx = true;
|
||||
}
|
||||
} else if (try BlobFileContentResult.init("cert", item, global)) |content| {
|
||||
if (content.data.len > 0) {
|
||||
native_array[valid_count] = content.data.ptr;
|
||||
valid_count += 1;
|
||||
result.requires_custom_request_ctx = true;
|
||||
any = true;
|
||||
} else {
|
||||
// mark and free all CA's
|
||||
result.cert = native_array;
|
||||
result.deinit();
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
// mark and free all certs
|
||||
result.cert = native_array;
|
||||
return global.throwInvalidArguments("cert argument must be an string, Buffer, TypedArray, BunFile or an array containing string, Buffer, TypedArray or BunFile", .{});
|
||||
}
|
||||
}
|
||||
|
||||
if (valid_count == 0) {
|
||||
bun.default_allocator.free(native_array);
|
||||
} else {
|
||||
result.cert = native_array;
|
||||
}
|
||||
|
||||
result.cert_count = valid_count;
|
||||
}
|
||||
} else if (try BlobFileContentResult.init("cert", js_obj, global)) |content| {
|
||||
if (content.data.len > 0) {
|
||||
const native_array = try bun.default_allocator.alloc([*c]const u8, 1);
|
||||
native_array[0] = content.data.ptr;
|
||||
result.cert = native_array;
|
||||
result.cert_count = 1;
|
||||
any = true;
|
||||
result.requires_custom_request_ctx = true;
|
||||
} else {
|
||||
result.deinit();
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
const native_array = try bun.default_allocator.alloc([*c]const u8, 1);
|
||||
if (try JSC.Node.StringOrBuffer.fromJS(global, arena.allocator(), js_obj)) |sb| {
|
||||
defer sb.deinit();
|
||||
const sliced = sb.slice();
|
||||
if (sliced.len > 0) {
|
||||
native_array[0] = try bun.default_allocator.dupeZ(u8, sliced);
|
||||
any = true;
|
||||
result.requires_custom_request_ctx = true;
|
||||
result.cert = native_array;
|
||||
result.cert_count = 1;
|
||||
} else {
|
||||
bun.default_allocator.free(native_array);
|
||||
}
|
||||
} else {
|
||||
// mark and free all certs
|
||||
result.cert = native_array;
|
||||
return global.throwInvalidArguments("cert argument must be an string, Buffer, TypedArray, BunFile or an array containing string, Buffer, TypedArray or BunFile", .{});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (try obj.getBooleanStrict(global, "requestCert")) |request_cert| {
|
||||
result.request_cert = if (request_cert) 1 else 0;
|
||||
any = true;
|
||||
}
|
||||
|
||||
if (try obj.getBooleanStrict(global, "rejectUnauthorized")) |reject_unauthorized| {
|
||||
result.reject_unauthorized = if (reject_unauthorized) 1 else 0;
|
||||
any = true;
|
||||
}
|
||||
|
||||
if (try obj.getTruthy(global, "ciphers")) |ssl_ciphers| {
|
||||
var sliced = try ssl_ciphers.toSlice(global, bun.default_allocator);
|
||||
defer sliced.deinit();
|
||||
if (sliced.len > 0) {
|
||||
result.ssl_ciphers = try bun.default_allocator.dupeZ(u8, sliced.slice());
|
||||
any = true;
|
||||
result.requires_custom_request_ctx = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (try obj.getTruthy(global, "serverName") orelse try obj.getTruthy(global, "servername")) |server_name| {
|
||||
var sliced = try server_name.toSlice(global, bun.default_allocator);
|
||||
defer sliced.deinit();
|
||||
if (sliced.len > 0) {
|
||||
result.server_name = try bun.default_allocator.dupeZ(u8, sliced.slice());
|
||||
any = true;
|
||||
result.requires_custom_request_ctx = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (try obj.getTruthy(global, "ca")) |js_obj| {
|
||||
if (js_obj.jsType().isArray()) {
|
||||
const count = js_obj.getLength(global);
|
||||
if (count > 0) {
|
||||
const native_array = try bun.default_allocator.alloc([*c]const u8, count);
|
||||
|
||||
var valid_count: u32 = 0;
|
||||
for (0..count) |i| {
|
||||
const item = js_obj.getIndex(global, @intCast(i));
|
||||
if (try JSC.Node.StringOrBuffer.fromJS(global, arena.allocator(), item)) |sb| {
|
||||
defer sb.deinit();
|
||||
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;
|
||||
result.requires_custom_request_ctx = true;
|
||||
}
|
||||
} else if (try BlobFileContentResult.init("ca", item, global)) |content| {
|
||||
if (content.data.len > 0) {
|
||||
native_array[valid_count] = content.data.ptr;
|
||||
valid_count += 1;
|
||||
any = true;
|
||||
result.requires_custom_request_ctx = true;
|
||||
} else {
|
||||
// mark and free all CA's
|
||||
result.cert = native_array;
|
||||
result.deinit();
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
// mark and free all CA's
|
||||
result.cert = native_array;
|
||||
return global.throwInvalidArguments("ca argument must be an string, Buffer, TypedArray, BunFile or an array containing string, Buffer, TypedArray or BunFile", .{});
|
||||
}
|
||||
}
|
||||
|
||||
if (valid_count == 0) {
|
||||
bun.default_allocator.free(native_array);
|
||||
} else {
|
||||
result.ca = native_array;
|
||||
}
|
||||
|
||||
result.ca_count = valid_count;
|
||||
}
|
||||
} else if (try BlobFileContentResult.init("ca", js_obj, global)) |content| {
|
||||
if (content.data.len > 0) {
|
||||
const native_array = try bun.default_allocator.alloc([*c]const u8, 1);
|
||||
native_array[0] = content.data.ptr;
|
||||
result.ca = native_array;
|
||||
result.ca_count = 1;
|
||||
any = true;
|
||||
result.requires_custom_request_ctx = true;
|
||||
} else {
|
||||
result.deinit();
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
const native_array = try bun.default_allocator.alloc([*c]const u8, 1);
|
||||
if (try JSC.Node.StringOrBuffer.fromJS(global, arena.allocator(), js_obj)) |sb| {
|
||||
defer sb.deinit();
|
||||
const sliced = sb.slice();
|
||||
if (sliced.len > 0) {
|
||||
native_array[0] = try bun.default_allocator.dupeZ(u8, sliced);
|
||||
any = true;
|
||||
result.requires_custom_request_ctx = true;
|
||||
result.ca = native_array;
|
||||
result.ca_count = 1;
|
||||
} else {
|
||||
bun.default_allocator.free(native_array);
|
||||
}
|
||||
} else {
|
||||
// mark and free all certs
|
||||
result.ca = native_array;
|
||||
return global.throwInvalidArguments("ca argument must be an string, Buffer, TypedArray, BunFile or an array containing string, Buffer, TypedArray or BunFile", .{});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (try obj.getTruthy(global, "caFile")) |ca_file_name| {
|
||||
var sliced = try ca_file_name.toSlice(global, bun.default_allocator);
|
||||
defer sliced.deinit();
|
||||
if (sliced.len > 0) {
|
||||
result.ca_file_name = try bun.default_allocator.dupeZ(u8, sliced.slice());
|
||||
if (std.posix.system.access(result.ca_file_name, std.posix.F_OK) != 0) {
|
||||
return global.throwInvalidArguments("Invalid caFile path", .{});
|
||||
}
|
||||
}
|
||||
}
|
||||
// Optional
|
||||
if (any) {
|
||||
if (try obj.getTruthy(global, "secureOptions")) |secure_options| {
|
||||
if (secure_options.isNumber()) {
|
||||
result.secure_options = secure_options.toU32();
|
||||
}
|
||||
}
|
||||
|
||||
if (try obj.getTruthy(global, "clientRenegotiationLimit")) |client_renegotiation_limit| {
|
||||
if (client_renegotiation_limit.isNumber()) {
|
||||
result.client_renegotiation_limit = client_renegotiation_limit.toU32();
|
||||
}
|
||||
}
|
||||
|
||||
if (try obj.getTruthy(global, "clientRenegotiationWindow")) |client_renegotiation_window| {
|
||||
if (client_renegotiation_window.isNumber()) {
|
||||
result.client_renegotiation_window = client_renegotiation_window.toU32();
|
||||
}
|
||||
}
|
||||
|
||||
if (try obj.getTruthy(global, "dhParamsFile")) |dh_params_file_name| {
|
||||
var sliced = try dh_params_file_name.toSlice(global, bun.default_allocator);
|
||||
defer sliced.deinit();
|
||||
if (sliced.len > 0) {
|
||||
result.dh_params_file_name = try bun.default_allocator.dupeZ(u8, sliced.slice());
|
||||
if (std.posix.system.access(result.dh_params_file_name, std.posix.F_OK) != 0) {
|
||||
return global.throwInvalidArguments("Invalid dhParamsFile path", .{});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (try obj.getTruthy(global, "passphrase")) |passphrase| {
|
||||
var sliced = try passphrase.toSlice(global, bun.default_allocator);
|
||||
defer sliced.deinit();
|
||||
if (sliced.len > 0) {
|
||||
result.passphrase = try bun.default_allocator.dupeZ(u8, sliced.slice());
|
||||
}
|
||||
}
|
||||
|
||||
if (try obj.get(global, "lowMemoryMode")) |low_memory_mode| {
|
||||
if (low_memory_mode.isBoolean() or low_memory_mode.isUndefined()) {
|
||||
result.low_memory_mode = low_memory_mode.toBoolean();
|
||||
any = true;
|
||||
} else {
|
||||
return global.throw("Expected lowMemoryMode to be a boolean", .{});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!any)
|
||||
return null;
|
||||
return result;
|
||||
}
|
||||
|
||||
const std = @import("std");
|
||||
const bun = @import("bun");
|
||||
const JSC = bun.JSC;
|
||||
const uws = bun.uws;
|
||||
const JSValue = JSC.JSValue;
|
||||
const JSGlobalObject = JSC.JSGlobalObject;
|
||||
const VirtualMachine = JSC.VirtualMachine;
|
||||
const strings = bun.strings;
|
||||
1092
src/bun.js/api/server/ServerConfig.zig
Normal file
1092
src/bun.js/api/server/ServerConfig.zig
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1289,6 +1289,6 @@ const bun = @import("bun");
|
||||
const string = []const u8;
|
||||
const std = @import("std");
|
||||
const ZigString = JSC.ZigString;
|
||||
const WebSocketServer = @import("../server.zig").WebSocketServer;
|
||||
const WebSocketServer = @import("../server.zig").WebSocketServerContext;
|
||||
const uws = bun.uws;
|
||||
const Output = bun.Output;
|
||||
|
||||
322
src/bun.js/api/server/WebSocketServerContext.zig
Normal file
322
src/bun.js/api/server/WebSocketServerContext.zig
Normal file
@@ -0,0 +1,322 @@
|
||||
const WebSocketServerContext = @This();
|
||||
|
||||
globalObject: *JSC.JSGlobalObject = undefined,
|
||||
handler: Handler = .{},
|
||||
|
||||
maxPayloadLength: u32 = 1024 * 1024 * 16, // 16MB
|
||||
maxLifetime: u16 = 0,
|
||||
idleTimeout: u16 = 120, // 2 minutes
|
||||
compression: i32 = 0,
|
||||
backpressureLimit: u32 = 1024 * 1024 * 16, // 16MB
|
||||
sendPingsAutomatically: bool = true,
|
||||
resetIdleTimeoutOnSend: bool = true,
|
||||
closeOnBackpressureLimit: bool = false,
|
||||
|
||||
pub const Handler = struct {
|
||||
onOpen: JSC.JSValue = .zero,
|
||||
onMessage: JSC.JSValue = .zero,
|
||||
onClose: JSC.JSValue = .zero,
|
||||
onDrain: JSC.JSValue = .zero,
|
||||
onError: JSC.JSValue = .zero,
|
||||
onPing: JSC.JSValue = .zero,
|
||||
onPong: JSC.JSValue = .zero,
|
||||
|
||||
app: ?*anyopaque = null,
|
||||
|
||||
// Always set manually.
|
||||
vm: *JSC.VirtualMachine = undefined,
|
||||
globalObject: *JSC.JSGlobalObject = undefined,
|
||||
active_connections: usize = 0,
|
||||
|
||||
/// used by publish()
|
||||
flags: packed struct(u2) {
|
||||
ssl: bool = false,
|
||||
publish_to_self: bool = false,
|
||||
} = .{},
|
||||
|
||||
pub fn runErrorCallback(this: *const Handler, vm: *JSC.VirtualMachine, globalObject: *JSC.JSGlobalObject, error_value: JSC.JSValue) void {
|
||||
const onError = this.onError;
|
||||
if (!onError.isEmptyOrUndefinedOrNull()) {
|
||||
_ = onError.call(globalObject, .undefined, &.{error_value}) catch |err|
|
||||
this.globalObject.reportActiveExceptionAsUnhandled(err);
|
||||
return;
|
||||
}
|
||||
|
||||
_ = vm.uncaughtException(globalObject, error_value, false);
|
||||
}
|
||||
|
||||
pub fn fromJS(globalObject: *JSC.JSGlobalObject, object: JSC.JSValue) bun.JSError!Handler {
|
||||
var handler = Handler{ .globalObject = globalObject, .vm = VirtualMachine.get() };
|
||||
|
||||
var valid = false;
|
||||
|
||||
if (try object.getTruthyComptime(globalObject, "message")) |message_| {
|
||||
if (!message_.isCallable()) {
|
||||
return globalObject.throwInvalidArguments("websocket expects a function for the message option", .{});
|
||||
}
|
||||
const message = message_.withAsyncContextIfNeeded(globalObject);
|
||||
handler.onMessage = message;
|
||||
message.ensureStillAlive();
|
||||
valid = true;
|
||||
}
|
||||
|
||||
if (try object.getTruthy(globalObject, "open")) |open_| {
|
||||
if (!open_.isCallable()) {
|
||||
return globalObject.throwInvalidArguments("websocket expects a function for the open option", .{});
|
||||
}
|
||||
const open = open_.withAsyncContextIfNeeded(globalObject);
|
||||
handler.onOpen = open;
|
||||
open.ensureStillAlive();
|
||||
valid = true;
|
||||
}
|
||||
|
||||
if (try object.getTruthy(globalObject, "close")) |close_| {
|
||||
if (!close_.isCallable()) {
|
||||
return globalObject.throwInvalidArguments("websocket expects a function for the close option", .{});
|
||||
}
|
||||
const close = close_.withAsyncContextIfNeeded(globalObject);
|
||||
handler.onClose = close;
|
||||
close.ensureStillAlive();
|
||||
valid = true;
|
||||
}
|
||||
|
||||
if (try object.getTruthy(globalObject, "drain")) |drain_| {
|
||||
if (!drain_.isCallable()) {
|
||||
return globalObject.throwInvalidArguments("websocket expects a function for the drain option", .{});
|
||||
}
|
||||
const drain = drain_.withAsyncContextIfNeeded(globalObject);
|
||||
handler.onDrain = drain;
|
||||
drain.ensureStillAlive();
|
||||
valid = true;
|
||||
}
|
||||
|
||||
if (try object.getTruthy(globalObject, "onError")) |onError_| {
|
||||
if (!onError_.isCallable()) {
|
||||
return globalObject.throwInvalidArguments("websocket expects a function for the onError option", .{});
|
||||
}
|
||||
const onError = onError_.withAsyncContextIfNeeded(globalObject);
|
||||
handler.onError = onError;
|
||||
onError.ensureStillAlive();
|
||||
}
|
||||
|
||||
if (try object.getTruthy(globalObject, "ping")) |cb| {
|
||||
if (!cb.isCallable()) {
|
||||
return globalObject.throwInvalidArguments("websocket expects a function for the ping option", .{});
|
||||
}
|
||||
handler.onPing = cb;
|
||||
cb.ensureStillAlive();
|
||||
valid = true;
|
||||
}
|
||||
|
||||
if (try object.getTruthy(globalObject, "pong")) |cb| {
|
||||
if (!cb.isCallable()) {
|
||||
return globalObject.throwInvalidArguments("websocket expects a function for the pong option", .{});
|
||||
}
|
||||
handler.onPong = cb;
|
||||
cb.ensureStillAlive();
|
||||
valid = true;
|
||||
}
|
||||
|
||||
if (valid)
|
||||
return handler;
|
||||
|
||||
return globalObject.throwInvalidArguments("WebSocketServerContext expects a message handler", .{});
|
||||
}
|
||||
|
||||
pub fn protect(this: Handler) void {
|
||||
this.onOpen.protect();
|
||||
this.onMessage.protect();
|
||||
this.onClose.protect();
|
||||
this.onDrain.protect();
|
||||
this.onError.protect();
|
||||
this.onPing.protect();
|
||||
this.onPong.protect();
|
||||
}
|
||||
|
||||
pub fn unprotect(this: Handler) void {
|
||||
if (this.vm.isShuttingDown()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.onOpen.unprotect();
|
||||
this.onMessage.unprotect();
|
||||
this.onClose.unprotect();
|
||||
this.onDrain.unprotect();
|
||||
this.onError.unprotect();
|
||||
this.onPing.unprotect();
|
||||
this.onPong.unprotect();
|
||||
}
|
||||
};
|
||||
|
||||
pub fn toBehavior(this: WebSocketServerContext) uws.WebSocketBehavior {
|
||||
return .{
|
||||
.maxPayloadLength = this.maxPayloadLength,
|
||||
.idleTimeout = this.idleTimeout,
|
||||
.compression = this.compression,
|
||||
.maxBackpressure = this.backpressureLimit,
|
||||
.sendPingsAutomatically = this.sendPingsAutomatically,
|
||||
.maxLifetime = this.maxLifetime,
|
||||
.resetIdleTimeoutOnSend = this.resetIdleTimeoutOnSend,
|
||||
.closeOnBackpressureLimit = this.closeOnBackpressureLimit,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn protect(this: WebSocketServerContext) void {
|
||||
this.handler.protect();
|
||||
}
|
||||
pub fn unprotect(this: WebSocketServerContext) void {
|
||||
this.handler.unprotect();
|
||||
}
|
||||
|
||||
const CompressTable = bun.ComptimeStringMap(i32, .{
|
||||
.{ "disable", 0 },
|
||||
.{ "shared", uws.SHARED_COMPRESSOR },
|
||||
.{ "dedicated", uws.DEDICATED_COMPRESSOR },
|
||||
.{ "3KB", uws.DEDICATED_COMPRESSOR_3KB },
|
||||
.{ "4KB", uws.DEDICATED_COMPRESSOR_4KB },
|
||||
.{ "8KB", uws.DEDICATED_COMPRESSOR_8KB },
|
||||
.{ "16KB", uws.DEDICATED_COMPRESSOR_16KB },
|
||||
.{ "32KB", uws.DEDICATED_COMPRESSOR_32KB },
|
||||
.{ "64KB", uws.DEDICATED_COMPRESSOR_64KB },
|
||||
.{ "128KB", uws.DEDICATED_COMPRESSOR_128KB },
|
||||
.{ "256KB", uws.DEDICATED_COMPRESSOR_256KB },
|
||||
});
|
||||
|
||||
const DecompressTable = bun.ComptimeStringMap(i32, .{
|
||||
.{ "disable", 0 },
|
||||
.{ "shared", uws.SHARED_DECOMPRESSOR },
|
||||
.{ "dedicated", uws.DEDICATED_DECOMPRESSOR },
|
||||
.{ "3KB", uws.DEDICATED_COMPRESSOR_3KB },
|
||||
.{ "4KB", uws.DEDICATED_COMPRESSOR_4KB },
|
||||
.{ "8KB", uws.DEDICATED_COMPRESSOR_8KB },
|
||||
.{ "16KB", uws.DEDICATED_COMPRESSOR_16KB },
|
||||
.{ "32KB", uws.DEDICATED_COMPRESSOR_32KB },
|
||||
.{ "64KB", uws.DEDICATED_COMPRESSOR_64KB },
|
||||
.{ "128KB", uws.DEDICATED_COMPRESSOR_128KB },
|
||||
.{ "256KB", uws.DEDICATED_COMPRESSOR_256KB },
|
||||
});
|
||||
|
||||
pub fn onCreate(globalObject: *JSC.JSGlobalObject, object: JSValue) bun.JSError!WebSocketServerContext {
|
||||
var server = WebSocketServerContext{};
|
||||
server.handler = try Handler.fromJS(globalObject, object);
|
||||
|
||||
if (try object.get(globalObject, "perMessageDeflate")) |per_message_deflate| {
|
||||
getter: {
|
||||
if (per_message_deflate.isUndefined()) {
|
||||
break :getter;
|
||||
}
|
||||
|
||||
if (per_message_deflate.isBoolean() or per_message_deflate.isNull()) {
|
||||
if (per_message_deflate.toBoolean()) {
|
||||
server.compression = uws.SHARED_COMPRESSOR | uws.SHARED_DECOMPRESSOR;
|
||||
} else {
|
||||
server.compression = 0;
|
||||
}
|
||||
break :getter;
|
||||
}
|
||||
|
||||
if (try per_message_deflate.getTruthy(globalObject, "compress")) |compression| {
|
||||
if (compression.isBoolean()) {
|
||||
server.compression |= if (compression.toBoolean()) uws.SHARED_COMPRESSOR else 0;
|
||||
} else if (compression.isString()) {
|
||||
server.compression |= CompressTable.getWithEql(try compression.getZigString(globalObject), ZigString.eqlComptime) orelse {
|
||||
return globalObject.throwInvalidArguments("WebSocketServerContext expects a valid compress option, either disable \"shared\" \"dedicated\" \"3KB\" \"4KB\" \"8KB\" \"16KB\" \"32KB\" \"64KB\" \"128KB\" or \"256KB\"", .{});
|
||||
};
|
||||
} else {
|
||||
return globalObject.throwInvalidArguments("websocket expects a valid compress option, either disable \"shared\" \"dedicated\" \"3KB\" \"4KB\" \"8KB\" \"16KB\" \"32KB\" \"64KB\" \"128KB\" or \"256KB\"", .{});
|
||||
}
|
||||
}
|
||||
|
||||
if (try per_message_deflate.getTruthy(globalObject, "decompress")) |compression| {
|
||||
if (compression.isBoolean()) {
|
||||
server.compression |= if (compression.toBoolean()) uws.SHARED_DECOMPRESSOR else 0;
|
||||
} else if (compression.isString()) {
|
||||
server.compression |= DecompressTable.getWithEql(try compression.getZigString(globalObject), ZigString.eqlComptime) orelse {
|
||||
return globalObject.throwInvalidArguments("websocket expects a valid decompress option, either \"disable\" \"shared\" \"dedicated\" \"3KB\" \"4KB\" \"8KB\" \"16KB\" \"32KB\" \"64KB\" \"128KB\" or \"256KB\"", .{});
|
||||
};
|
||||
} else {
|
||||
return globalObject.throwInvalidArguments("websocket expects a valid decompress option, either \"disable\" \"shared\" \"dedicated\" \"3KB\" \"4KB\" \"8KB\" \"16KB\" \"32KB\" \"64KB\" \"128KB\" or \"256KB\"", .{});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (try object.get(globalObject, "maxPayloadLength")) |value| {
|
||||
if (!value.isUndefinedOrNull()) {
|
||||
if (!value.isAnyInt()) {
|
||||
return globalObject.throwInvalidArguments("websocket expects maxPayloadLength to be an integer", .{});
|
||||
}
|
||||
server.maxPayloadLength = @truncate(@max(value.toInt64(), 0));
|
||||
}
|
||||
}
|
||||
|
||||
if (try object.get(globalObject, "idleTimeout")) |value| {
|
||||
if (!value.isUndefinedOrNull()) {
|
||||
if (!value.isAnyInt()) {
|
||||
return globalObject.throwInvalidArguments("websocket expects idleTimeout to be an integer", .{});
|
||||
}
|
||||
|
||||
var idleTimeout: u16 = @truncate(@max(value.toInt64(), 0));
|
||||
if (idleTimeout > 960) {
|
||||
return globalObject.throwInvalidArguments("websocket expects idleTimeout to be 960 or less", .{});
|
||||
} else if (idleTimeout > 0) {
|
||||
// uws does not allow idleTimeout to be between (0, 8),
|
||||
// since its timer is not that accurate, therefore round up.
|
||||
idleTimeout = @max(idleTimeout, 8);
|
||||
}
|
||||
|
||||
server.idleTimeout = idleTimeout;
|
||||
}
|
||||
}
|
||||
if (try object.get(globalObject, "backpressureLimit")) |value| {
|
||||
if (!value.isUndefinedOrNull()) {
|
||||
if (!value.isAnyInt()) {
|
||||
return globalObject.throwInvalidArguments("websocket expects backpressureLimit to be an integer", .{});
|
||||
}
|
||||
|
||||
server.backpressureLimit = @truncate(@max(value.toInt64(), 0));
|
||||
}
|
||||
}
|
||||
|
||||
if (try object.get(globalObject, "closeOnBackpressureLimit")) |value| {
|
||||
if (!value.isUndefinedOrNull()) {
|
||||
if (!value.isBoolean()) {
|
||||
return globalObject.throwInvalidArguments("websocket expects closeOnBackpressureLimit to be a boolean", .{});
|
||||
}
|
||||
|
||||
server.closeOnBackpressureLimit = value.toBoolean();
|
||||
}
|
||||
}
|
||||
|
||||
if (try object.get(globalObject, "sendPings")) |value| {
|
||||
if (!value.isUndefinedOrNull()) {
|
||||
if (!value.isBoolean()) {
|
||||
return globalObject.throwInvalidArguments("websocket expects sendPings to be a boolean", .{});
|
||||
}
|
||||
|
||||
server.sendPingsAutomatically = value.toBoolean();
|
||||
}
|
||||
}
|
||||
|
||||
if (try object.get(globalObject, "publishToSelf")) |value| {
|
||||
if (!value.isUndefinedOrNull()) {
|
||||
if (!value.isBoolean()) {
|
||||
return globalObject.throwInvalidArguments("websocket expects publishToSelf to be a boolean", .{});
|
||||
}
|
||||
|
||||
server.handler.flags.publish_to_self = value.toBoolean();
|
||||
}
|
||||
}
|
||||
|
||||
server.protect();
|
||||
return server;
|
||||
}
|
||||
|
||||
const bun = @import("bun");
|
||||
const uws = bun.uws;
|
||||
const JSC = bun.JSC;
|
||||
const JSValue = JSC.JSValue;
|
||||
const JSGlobalObject = JSC.JSGlobalObject;
|
||||
const JSError = bun.JSError;
|
||||
const VirtualMachine = JSC.VirtualMachine;
|
||||
const ZigString = JSC.ZigString;
|
||||
Reference in New Issue
Block a user