From 60a2a4df0194444338cde3325cac211404b3627f Mon Sep 17 00:00:00 2001 From: Kai Tamkun Date: Thu, 23 Jan 2025 16:53:11 -0800 Subject: [PATCH] Make IPV6_V6ONLY configurable in Bun.serve --- packages/bun-types/bun.d.ts | 8 +++++++- packages/bun-usockets/src/bsd.c | 36 +++++++-------------------------- src/bun.js/api/server.zig | 21 +++++++++++++++---- 3 files changed, 31 insertions(+), 34 deletions(-) diff --git a/packages/bun-types/bun.d.ts b/packages/bun-types/bun.d.ts index 23f1c6d3ba98df..a4c2cd1fe64a36 100644 --- a/packages/bun-types/bun.d.ts +++ b/packages/bun-types/bun.d.ts @@ -3734,7 +3734,7 @@ declare module "bun" { port?: string | number; /** - * If the `SO_REUSEPORT` flag should be set. + * Whether the `SO_REUSEPORT` flag should be set. * * This allows multiple processes to bind to the same port, which is useful for load balancing. * @@ -3742,6 +3742,12 @@ declare module "bun" { */ reusePort?: boolean; + /** + * Whether the `IPV6_V6ONLY` flag should be set. + * @default false + */ + ipv6Only?: boolean; + /** * What hostname should the server listen on? * diff --git a/packages/bun-usockets/src/bsd.c b/packages/bun-usockets/src/bsd.c index aee19f26a34273..e299884d90c9d7 100644 --- a/packages/bun-usockets/src/bsd.c +++ b/packages/bun-usockets/src/bsd.c @@ -863,31 +863,13 @@ inline __attribute__((always_inline)) LIBUS_SOCKET_DESCRIPTOR bsd_bind_listen_fd ) { if (bsd_set_reuse(listenFd, options) != 0) { - if (errno == ENOTSUP) { - errno = 0; - } else { - return LIBUS_SOCKET_ERROR; - } + return LIBUS_SOCKET_ERROR; } -#if defined(SO_REUSEADDR) - #ifndef _WIN32 - - // Unlike on Unix, here we don't set SO_REUSEADDR, because it doesn't just - // allow binding to addresses that are in use by sockets in TIME_WAIT, it - // effectively allows 'stealing' a port which is in use by another application. - // See libuv issue #1360. - - - int optval3 = 1; - setsockopt(listenFd, SOL_SOCKET, SO_REUSEADDR, &optval3, sizeof(optval3)); - #endif -#endif - #ifdef IPV6_V6ONLY if (listenAddr->ai_family == AF_INET6) { - int disabled = (options & LIBUS_SOCKET_IPV6_ONLY) != 0; - setsockopt(listenFd, IPPROTO_IPV6, IPV6_V6ONLY, &disabled, sizeof(disabled)); + int enabled = (options & LIBUS_SOCKET_IPV6_ONLY) != 0; + setsockopt(listenFd, IPPROTO_IPV6, IPV6_V6ONLY, &enabled, sizeof(enabled)); } #endif @@ -1148,18 +1130,14 @@ LIBUS_SOCKET_DESCRIPTOR bsd_create_udp_socket(const char *host, int port, int op } if (bsd_set_reuse(listenFd, options) != 0) { - if (errno == ENOTSUP) { - errno = 0; - } else { - freeaddrinfo(result); - return LIBUS_SOCKET_ERROR; - } + freeaddrinfo(result); + return LIBUS_SOCKET_ERROR; } #ifdef IPV6_V6ONLY if (listenAddr->ai_family == AF_INET6) { - int disabled = (options & LIBUS_SOCKET_IPV6_ONLY) != 0; - setsockopt(listenFd, IPPROTO_IPV6, IPV6_V6ONLY, &disabled, sizeof(disabled)); + int enabled = (options & LIBUS_SOCKET_IPV6_ONLY) != 0; + setsockopt(listenFd, IPPROTO_IPV6, IPV6_V6ONLY, &enabled, sizeof(enabled)); } #endif diff --git a/src/bun.js/api/server.zig b/src/bun.js/api/server.zig index ae8224caa03c93..c7626e951c614c 100644 --- a/src/bun.js/api/server.zig +++ b/src/bun.js/api/server.zig @@ -270,6 +270,7 @@ pub const ServerConfig = struct { reuse_port: bool = false, id: []const u8 = "", allow_hot: bool = true, + ipv6_only: bool = false, static_routes: std.ArrayList(StaticRouteEntry) = std.ArrayList(StaticRouteEntry).init(bun.default_allocator), @@ -449,6 +450,15 @@ pub const ServerConfig = struct { return arraylist.items; } + pub fn getUsocketsOptions(this: *const ServerConfig) i32 { + // Unlike Node.js, we set exclusive port in case reuse port is not set + var out: i32 = if (this.reuse_port) uws.LIBUS_LISTEN_REUSE_PORT else uws.LIBUS_LISTEN_EXCLUSIVE_PORT; + if (this.ipv6_only) { + out |= uws.LIBUS_SOCKET_IPV6_ONLY; + } + return out; + } + pub const SSLConfig = struct { requires_custom_request_ctx: bool = false, server_name: [*c]const u8 = null, @@ -1279,6 +1289,11 @@ pub const ServerConfig = struct { } if (global.hasException()) return error.JSError; + if (try arg.get(global, "ipv6Only")) |dev| { + args.ipv6_only = dev.coerce(bool, global); + } + if (global.hasException()) return error.JSError; + if (try arg.get(global, "inspector")) |inspector| { args.inspector = inspector.coerce(bool, global); @@ -7554,8 +7569,7 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp app.listenWithConfig(*ThisServer, this, onListen, .{ .port = tcp.port, .host = host, - // IPV6_ONLY is the default for bun, different from node it also set exclusive port in case reuse port is not set - .options = (if (this.config.reuse_port) uws.LIBUS_LISTEN_REUSE_PORT else uws.LIBUS_LISTEN_EXCLUSIVE_PORT) | uws.LIBUS_SOCKET_IPV6_ONLY, + .options = this.config.getUsocketsOptions(), }); }, @@ -7565,8 +7579,7 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp this, onListen, unix, - // IPV6_ONLY is the default for bun, different from node it also set exclusive port in case reuse port is not set - (if (this.config.reuse_port) uws.LIBUS_LISTEN_REUSE_PORT else uws.LIBUS_LISTEN_EXCLUSIVE_PORT) | uws.LIBUS_SOCKET_IPV6_ONLY, + this.config.getUsocketsOptions(), ); }, }