aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <jarred@jarredsumner.com> 2023-08-24 22:49:58 -0700
committerGravatar GitHub <noreply@github.com> 2023-08-24 22:49:58 -0700
commitf269432d90826ad3e5b66c7685a6e826e0fb05e2 (patch)
tree78a05d1ba0a9bb7079ba111db7f3ddbac9b021ed /src
parent73b3fb7b0fa7cc786e147ccf1247cd9883ad8e59 (diff)
downloadbun-f269432d90826ad3e5b66c7685a6e826e0fb05e2.tar.gz
bun-f269432d90826ad3e5b66c7685a6e826e0fb05e2.tar.zst
bun-f269432d90826ad3e5b66c7685a6e826e0fb05e2.zip
Listen on a unix domain socket with Bun.serve() (#4311)
* Update response.zig * Comment this out for now * Support unix domain socket in Bun.serve() * Add test * add types * Update JSFetchHeaders.cpp * comment this test out * tls unix web socket serve options --------- Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> Co-authored-by: dave caruso <me@paperdave.net>
Diffstat (limited to 'src')
-rw-r--r--src/bun.js/api/server.zig199
-rw-r--r--src/bun.js/base.zig3
-rw-r--r--src/bun.js/bindings/webcore/JSFetchHeaders.cpp4
-rw-r--r--src/bun.js/webcore/response.zig2
-rw-r--r--src/deps/uws.zig40
5 files changed, 196 insertions, 52 deletions
diff --git a/src/bun.js/api/server.zig b/src/bun.js/api/server.zig
index ca803cef2..81a50a5a7 100644
--- a/src/bun.js/api/server.zig
+++ b/src/bun.js/api/server.zig
@@ -120,8 +120,28 @@ const BlobFileContentResult = struct {
};
pub const ServerConfig = struct {
- port: u16 = 0,
- hostname: ?[*:0]const u8 = null,
+ address: union(enum) {
+ tcp: struct {
+ port: u16 = 0,
+ hostname: ?[*:0]const u8 = null,
+ },
+ unix: [*:0]const u8,
+
+ pub fn deinit(this: *@This(), allocator: std.mem.Allocator) void {
+ switch (this.*) {
+ .tcp => |tcp| {
+ if (tcp.hostname) |host| {
+ allocator.free(bun.sliceTo(host, 0));
+ }
+ },
+ .unix => |addr| {
+ allocator.free(bun.sliceTo(addr, 0));
+ },
+ }
+ }
+ } = .{
+ .tcp = .{},
+ },
// TODO: use webkit URL parser instead of bun's
base_url: URL = URL{},
@@ -657,8 +677,12 @@ pub const ServerConfig = struct {
var env = arguments.vm.bundler.env;
var args = ServerConfig{
- .port = 3000,
- .hostname = null,
+ .address = .{
+ .tcp = .{
+ .port = 3000,
+ .hostname = null,
+ },
+ },
.development = true,
};
var has_hostname = false;
@@ -670,19 +694,24 @@ pub const ServerConfig = struct {
args.development = false;
}
- const PORT_ENV = .{ "BUN_PORT", "PORT", "NODE_PORT" };
+ args.address.tcp.port = brk: {
+ const PORT_ENV = .{ "BUN_PORT", "PORT", "NODE_PORT" };
- inline for (PORT_ENV) |PORT| {
- if (env.get(PORT)) |port| {
- if (std.fmt.parseInt(u16, port, 10)) |_port| {
- args.port = _port;
- } else |_| {}
+ inline for (PORT_ENV) |PORT| {
+ if (env.get(PORT)) |port| {
+ if (std.fmt.parseInt(u16, port, 10)) |_port| {
+ break :brk _port;
+ } else |_| {}
+ }
}
- }
- if (arguments.vm.bundler.options.transform_options.port) |port| {
- args.port = port;
- }
+ if (arguments.vm.bundler.options.transform_options.port) |port| {
+ break :brk port;
+ }
+
+ break :brk args.address.tcp.port;
+ };
+ var port = args.address.tcp.port;
if (arguments.vm.bundler.options.transform_options.origin) |origin| {
args.base_uri = origin;
@@ -714,13 +743,14 @@ pub const ServerConfig = struct {
}
if (arg.getTruthy(global, "port")) |port_| {
- args.port = @as(
+ args.address.tcp.port = @as(
u16,
@intCast(@min(
@max(0, port_.coerce(i32, global)),
std.math.maxInt(u16),
)),
);
+ port = args.address.tcp.port;
}
if (arg.getTruthy(global, "baseURI")) |baseURI| {
@@ -737,12 +767,33 @@ pub const ServerConfig = struct {
global,
bun.default_allocator,
);
+ defer host_str.deinit();
+
if (host_str.len > 0) {
- args.hostname = bun.default_allocator.dupeZ(u8, host_str.slice()) catch unreachable;
+ args.address.tcp.hostname = bun.default_allocator.dupeZ(u8, host_str.slice()) catch unreachable;
has_hostname = true;
}
}
+ if (arg.getTruthy(global, "unix")) |unix| {
+ const unix_str = unix.toSlice(
+ global,
+ bun.default_allocator,
+ );
+ defer unix_str.deinit();
+ if (unix_str.len > 0) {
+ if (has_hostname) {
+ JSC.throwInvalidArguments("Cannot specify both hostname and unix", .{}, global, exception);
+ if (args.ssl_config) |*conf| {
+ conf.deinit();
+ }
+ return args;
+ }
+
+ args.address = .{ .unix = bun.default_allocator.dupeZ(u8, unix_str.slice()) catch unreachable };
+ }
+ }
+
if (arg.get(global, "development")) |dev| {
args.development = dev.coerce(bool, global);
args.reuse_port = !args.development;
@@ -846,7 +897,7 @@ pub const ServerConfig = struct {
const hostname = args.base_url.hostname;
const needsBrackets: bool = strings.isIPV6Address(hostname) and hostname[0] != '[';
if (needsBrackets) {
- args.base_uri = (if ((args.port == 80 and args.ssl_config == null) or (args.port == 443 and args.ssl_config != null))
+ args.base_uri = (if ((port == 80 and args.ssl_config == null) or (port == 443 and args.ssl_config != null))
std.fmt.allocPrint(bun.default_allocator, "{s}://[{s}]/{s}", .{
protocol,
hostname,
@@ -856,11 +907,11 @@ pub const ServerConfig = struct {
std.fmt.allocPrint(bun.default_allocator, "{s}://[{s}]:{d}/{s}", .{
protocol,
hostname,
- args.port,
+ port,
strings.trimLeadingChar(args.base_url.pathname, '/'),
})) catch unreachable;
} else {
- args.base_uri = (if ((args.port == 80 and args.ssl_config == null) or (args.port == 443 and args.ssl_config != null))
+ args.base_uri = (if ((port == 80 and args.ssl_config == null) or (port == 443 and args.ssl_config != null))
std.fmt.allocPrint(bun.default_allocator, "{s}://{s}/{s}", .{
protocol,
hostname,
@@ -870,7 +921,7 @@ pub const ServerConfig = struct {
std.fmt.allocPrint(bun.default_allocator, "{s}://{s}:{d}/{s}", .{
protocol,
hostname,
- args.port,
+ port,
strings.trimLeadingChar(args.base_url.pathname, '/'),
})) catch unreachable;
}
@@ -879,27 +930,27 @@ pub const ServerConfig = struct {
}
} else {
const hostname: string =
- if (has_hostname and std.mem.span(args.hostname.?).len > 0) std.mem.span(args.hostname.?) else "0.0.0.0";
+ if (has_hostname) std.mem.span(args.address.tcp.hostname.?) else "0.0.0.0";
const needsBrackets: bool = strings.isIPV6Address(hostname) and hostname[0] != '[';
const protocol: string = if (args.ssl_config != null) "https" else "http";
if (needsBrackets) {
- args.base_uri = (if ((args.port == 80 and args.ssl_config == null) or (args.port == 443 and args.ssl_config != null))
+ args.base_uri = (if ((port == 80 and args.ssl_config == null) or (port == 443 and args.ssl_config != null))
std.fmt.allocPrint(bun.default_allocator, "{s}://[{s}]/", .{
protocol,
hostname,
})
else
- std.fmt.allocPrint(bun.default_allocator, "{s}://[{s}]:{d}/", .{ protocol, hostname, args.port })) catch unreachable;
+ std.fmt.allocPrint(bun.default_allocator, "{s}://[{s}]:{d}/", .{ protocol, hostname, port })) catch unreachable;
} else {
- args.base_uri = (if ((args.port == 80 and args.ssl_config == null) or (args.port == 443 and args.ssl_config != null))
+ args.base_uri = (if ((port == 80 and args.ssl_config == null) or (port == 443 and args.ssl_config != null))
std.fmt.allocPrint(bun.default_allocator, "{s}://{s}/", .{
protocol,
hostname,
})
else
- std.fmt.allocPrint(bun.default_allocator, "{s}://{s}:{d}/", .{ protocol, hostname, args.port })) catch unreachable;
+ std.fmt.allocPrint(bun.default_allocator, "{s}://{s}:{d}/", .{ protocol, hostname, port })) catch unreachable;
}
if (!strings.isAllASCII(hostname)) {
@@ -5007,7 +5058,11 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp
this: *ThisServer,
_: *JSC.JSGlobalObject,
) callconv(.C) JSC.JSValue {
- var listener = this.listener orelse return JSC.JSValue.jsNumber(this.config.port);
+ if (this.config.address != .tcp) {
+ return JSValue.undefined;
+ }
+
+ var listener = this.listener orelse return JSC.JSValue.jsNumber(this.config.address.tcp.port);
return JSC.JSValue.jsNumber(listener.getLocalPort());
}
@@ -5036,8 +5091,18 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp
}
}
- if (this.cached_hostname.isEmpty())
- this.cached_hostname = bun.String.create(bun.span(this.config.hostname orelse "localhost"));
+ if (this.cached_hostname.isEmpty()) {
+ switch (this.config.address) {
+ .tcp => |tcp| {
+ if (tcp.hostname) |hostname| {
+ this.cached_hostname = bun.String.create(bun.sliceTo(hostname, 0));
+ } else {
+ this.cached_hostname = bun.String.createAtom("localhost");
+ }
+ },
+ else => {},
+ }
+ }
}
return this.cached_hostname.toJS(globalThis);
@@ -5130,9 +5195,7 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp
this.cached_hostname.deref();
this.cached_protocol.deref();
- if (this.config.hostname) |host| {
- bun.default_allocator.free(host[0 .. std.mem.len(host) + 1]);
- }
+ this.config.address.deinit(bun.default_allocator);
if (this.config.base_url.href.len > 0) {
bun.default_allocator.free(this.config.base_url.href);
@@ -5224,7 +5287,28 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp
}
if (error_instance == .zero) {
- error_instance = ZigString.init(std.fmt.bufPrint(&output_buf, "Failed to start server. Is port {d} in use?", .{this.config.port}) catch "Failed to start server").toErrorInstance(this.globalThis);
+ switch (this.config.address) {
+ .tcp => |tcp| {
+ error_instance = ZigString.init(
+ std.fmt.bufPrint(&output_buf, "Failed to start server. Is port {d} in use?", .{tcp.port}) catch "Failed to start server",
+ ).toErrorInstance(
+ this.globalThis,
+ );
+ },
+ .unix => |unix| {
+ error_instance = ZigString.init(
+ std.fmt.bufPrint(
+ &output_buf,
+ "Failed to listen on unix socket {}",
+ .{
+ strings.QuotedFormatter{ .text = bun.sliceTo(unix, 0) },
+ },
+ ) catch "Failed to start server",
+ ).toErrorInstance(
+ this.globalThis,
+ );
+ },
+ }
}
// store the exception in here
@@ -5545,19 +5629,6 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp
this.app.get("/src:/*", *ThisServer, this, onSrcRequest);
}
- var host: ?[*:0]const u8 = null;
- var host_buff: [1024:0]u8 = undefined;
-
- if (this.config.hostname) |existing| {
- const hostname = bun.span(existing);
-
- if (hostname.len > 2 and hostname[0] == '[') {
- // remove "[" and "]" from hostname
- host = std.fmt.bufPrintZ(&host_buff, "{s}", .{hostname[1 .. hostname.len - 1]}) catch unreachable;
- } else {
- host = this.config.hostname;
- }
- }
this.ref();
@@ -5568,11 +5639,39 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp
this.vm.eventLoop().performGC();
}
- this.app.listenWithConfig(*ThisServer, this, onListen, .{
- .port = this.config.port,
- .host = host,
- .options = if (this.config.reuse_port) 0 else 1,
- });
+ switch (this.config.address) {
+ .tcp => |tcp| {
+ var host: ?[*:0]const u8 = null;
+ var host_buff: [1024:0]u8 = undefined;
+
+ if (tcp.hostname) |existing| {
+ const hostname = bun.span(existing);
+
+ if (hostname.len > 2 and hostname[0] == '[') {
+ // remove "[" and "]" from hostname
+ host = std.fmt.bufPrintZ(&host_buff, "{s}", .{hostname[1 .. hostname.len - 1]}) catch unreachable;
+ } else {
+ host = tcp.hostname;
+ }
+ }
+
+ this.app.listenWithConfig(*ThisServer, this, onListen, .{
+ .port = tcp.port,
+ .host = host,
+ .options = if (this.config.reuse_port) 0 else 1,
+ });
+ },
+
+ .unix => |unix| {
+ this.app.listenOnUnixSocket(
+ *ThisServer,
+ this,
+ onListen,
+ unix,
+ if (this.config.reuse_port) 0 else 1,
+ );
+ },
+ }
}
};
}
diff --git a/src/bun.js/base.zig b/src/bun.js/base.zig
index 0ed780a5d..66cc50f5d 100644
--- a/src/bun.js/base.zig
+++ b/src/bun.js/base.zig
@@ -2519,9 +2519,10 @@ pub const MemoryReportingAllocator = struct {
this.child_allocator.rawFree(buf, buf_align, ret_addr);
const prev = this.memory_cost.fetchSub(buf.len, .Monotonic);
+ _ = prev;
if (comptime Environment.allow_assert) {
// check for overflow, racily
- std.debug.assert(prev > this.memory_cost.load(.Monotonic));
+ // std.debug.assert(prev > this.memory_cost.load(.Monotonic));
log("free({d}) = {d}", .{ buf.len, this.memory_cost.loadUnchecked() });
}
}
diff --git a/src/bun.js/bindings/webcore/JSFetchHeaders.cpp b/src/bun.js/bindings/webcore/JSFetchHeaders.cpp
index eec128373..2ae8dd547 100644
--- a/src/bun.js/bindings/webcore/JSFetchHeaders.cpp
+++ b/src/bun.js/bindings/webcore/JSFetchHeaders.cpp
@@ -148,6 +148,10 @@ template<> EncodedJSValue JSC_HOST_CALL_ATTRIBUTES JSFetchHeadersDOMConstructor:
RETURN_IF_EXCEPTION(throwScope, {});
setSubclassStructureIfNeeded<FetchHeaders>(lexicalGlobalObject, callFrame, asObject(jsValue));
RETURN_IF_EXCEPTION(throwScope, {});
+
+ if (argument0.value())
+ jsCast<JSFetchHeaders*>(jsValue)->computeMemoryCost();
+
return JSValue::encode(jsValue);
}
JSC_ANNOTATE_HOST_FUNCTION(JSFetchHeadersDOMConstructorConstruct, JSFetchHeadersDOMConstructor::construct);
diff --git a/src/bun.js/webcore/response.zig b/src/bun.js/webcore/response.zig
index 0838a4580..df92dff15 100644
--- a/src/bun.js/webcore/response.zig
+++ b/src/bun.js/webcore/response.zig
@@ -735,7 +735,7 @@ pub const Fetch = struct {
var reporter = this.memory_reporter;
if (this.http) |http| reporter.allocator().destroy(http);
reporter.allocator().destroy(this);
- reporter.assert();
+ // reporter.assert();
bun.default_allocator.destroy(reporter);
}
diff --git a/src/deps/uws.zig b/src/deps/uws.zig
index 2610b0720..3f1fb276c 100644
--- a/src/deps/uws.zig
+++ b/src/deps/uws.zig
@@ -1635,6 +1635,37 @@ pub fn NewApp(comptime ssl: bool) type {
};
return uws_app_listen_with_config(ssl_flag, @as(*uws_app_t, @ptrCast(app)), config.host, @as(u16, @intCast(config.port)), config.options, Wrapper.handle, user_data);
}
+
+ pub fn listenOnUnixSocket(
+ app: *ThisApp,
+ comptime UserData: type,
+ user_data: UserData,
+ comptime handler: fn (UserData, ?*ThisApp.ListenSocket) void,
+ domain: [*:0]const u8,
+ flags: i32,
+ ) void {
+ const Wrapper = struct {
+ pub fn handle(socket: ?*uws.ListenSocket, _: [*:0]const u8, _: i32, data: *anyopaque) callconv(.C) void {
+ if (comptime UserData == void) {
+ @call(.always_inline, handler, .{ {}, @as(?*ThisApp.ListenSocket, @ptrCast(socket)) });
+ } else {
+ @call(.always_inline, handler, .{
+ @as(UserData, @ptrCast(@alignCast(data))),
+ @as(?*ThisApp.ListenSocket, @ptrCast(socket)),
+ });
+ }
+ }
+ };
+ return uws_app_listen_domain_with_options(
+ ssl_flag,
+ @as(*uws_app_t, @ptrCast(app)),
+ domain,
+ flags,
+ Wrapper.handle,
+ user_data,
+ );
+ }
+
pub fn constructorFailed(app: *ThisApp) bool {
return uws_constructor_failed(ssl_flag, app);
}
@@ -2206,3 +2237,12 @@ pub const State = enum(i32) {
};
extern fn us_socket_sendfile_needs_more(socket: *Socket) void;
+
+extern fn uws_app_listen_domain_with_options(
+ ssl_flag: c_int,
+ app: *uws_app_t,
+ domain: [*:0]const u8,
+ i32,
+ *const (fn (*ListenSocket, domain: [*:0]const u8, i32, *anyopaque) callconv(.C) void),
+ ?*anyopaque,
+) void;