diff --git a/packages/bun-usockets/src/socket.c b/packages/bun-usockets/src/socket.c index 76713eb77b..4ca98c88f0 100644 --- a/packages/bun-usockets/src/socket.c +++ b/packages/bun-usockets/src/socket.c @@ -513,9 +513,14 @@ void us_socket_nodelay(struct us_socket_t *s, int enabled) { } } +/// Returns 0 on success. Returned error values depend on the platform. +/// - on posix, returns `errno` +/// - on windows, when libuv is used, returns a UV err code +/// - on windows, LIBUS_USE_LIBUV is set, returns `WSAGetLastError()` +/// - on windows, otherwise returns result of `WSAGetLastError` int us_socket_keepalive(us_socket_r s, int enabled, unsigned int delay){ if (!us_socket_is_shut_down(0, s)) { - bsd_socket_keepalive(us_poll_fd((struct us_poll_t *) s), enabled, delay); + return bsd_socket_keepalive(us_poll_fd((struct us_poll_t *) s), enabled, delay); } return 0; } @@ -554,4 +559,4 @@ void us_socket_resume(int ssl, struct us_socket_t *s) { } // we are readable and writable so we resume everything us_poll_change(&s->p, s->context->loop, LIBUS_SOCKET_READABLE | LIBUS_SOCKET_WRITABLE); - } \ No newline at end of file + } diff --git a/src/bun.js/api/bun/socket.zig b/src/bun.js/api/bun/socket.zig index ee56fc1d05..83f66bdead 100644 --- a/src/bun.js/api/bun/socket.zig +++ b/src/bun.js/api/bun/socket.zig @@ -1660,7 +1660,7 @@ fn NewSocket(comptime ssl: bool) type { } } - pub fn closeAndDetach(this: *This, code: uws.CloseCode) void { + pub fn closeAndDetach(this: *This, code: uws.Socket.CloseCode) void { const socket = this.socket; this.buffered_data_for_node_net.deinitWithAllocator(bun.default_allocator); @@ -2108,12 +2108,10 @@ fn NewSocket(comptime ssl: bool) type { } var buf: [64]u8 = [_]u8{0} ** 64; - var length: i32 = 64; var text_buf: [512]u8 = undefined; - this.socket.remoteAddress(&buf, &length); - const address_bytes = buf[0..@as(usize, @intCast(length))]; - const address: std.net.Address = switch (length) { + const address_bytes: []const u8 = this.socket.remoteAddress(&buf) orelse return JSValue.jsUndefined(); + const address: std.net.Address = switch (address_bytes.len) { 4 => std.net.Address.initIp4(address_bytes[0..4].*, 0), 16 => std.net.Address.initIp6(address_bytes[0..16].*, 0, 0, 0), else => return JSValue.jsUndefined(), @@ -2250,7 +2248,7 @@ fn NewSocket(comptime ssl: bool) type { if (comptime !ssl and Environment.isPosix) { // fast-ish path: use writev() to avoid cloning to another buffer. if (this.socket.socket == .connected and buffer.slice().len > 0) { - const rc = this.socket.socket.connected.write2(this.buffered_data_for_node_net.slice(), buffer.slice()); + const rc = this.socket.socket.connected.write2(ssl, this.buffered_data_for_node_net.slice(), buffer.slice()); const written: usize = @intCast(@max(rc, 0)); const leftover = total_to_write -| written; if (leftover == 0) { diff --git a/src/bun.js/api/server.zig b/src/bun.js/api/server.zig index f34a7f52ed..51a0ace195 100644 --- a/src/bun.js/api/server.zig +++ b/src/bun.js/api/server.zig @@ -8237,10 +8237,11 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp if (this.cached_hostname.isEmpty()) { if (this.listener) |listener| { var buf: [1024]u8 = [_]u8{0} ** 1024; - var len: i32 = 1024; - listener.socket().remoteAddress(&buf, &len); - if (len > 0) { - this.cached_hostname = bun.String.createUTF8(buf[0..@as(usize, @intCast(len))]); + + if (listener.socket().remoteAddress(buf[0..1024])) |addr| { + if (addr.len > 0) { + this.cached_hostname = bun.String.createUTF8(addr); + } } } diff --git a/src/deps/uws.zig b/src/deps/uws.zig index 6aa55f5b69..c503ce1877 100644 --- a/src/deps/uws.zig +++ b/src/deps/uws.zig @@ -14,14 +14,7 @@ pub const LIBUS_SOCKET_IPV6_ONLY: i32 = 8; pub const LIBUS_LISTEN_REUSE_ADDR: i32 = 16; pub const LIBUS_LISTEN_DISALLOW_REUSE_PORT_FAILURE: i32 = 32; -pub const Socket = opaque { - pub fn write2(this: *Socket, first: []const u8, second: []const u8) i32 { - const rc = us_socket_write2(0, this, first.ptr, first.len, second.ptr, second.len); - debug("us_socket_write2({d}, {d}) = {d}", .{ first.len, second.len, rc }); - return rc; - } - extern "c" fn us_socket_write2(ssl: i32, *Socket, header: ?[*]const u8, len: usize, payload: ?[*]const u8, usize) i32; -}; +pub const Socket = @import("uws/socket.zig").Socket; pub const ConnectingSocket = opaque {}; const debug = bun.Output.scoped(.uws, false); const uws = @This(); @@ -34,11 +27,6 @@ const WriteResult = union(enum) { backpressure: usize, }; -pub const CloseCode = enum(i32) { - normal = 0, - failure = 1, -}; - const BoringSSL = bun.BoringSSL.c; fn NativeSocketHandleType(comptime ssl: bool) type { @@ -1131,13 +1119,7 @@ pub const InternalSocket = union(enum) { switch (this) { .detached => return true, .connected => |socket| { - if (pause) { - // Pause - us_socket_pause(@intFromBool(ssl), socket); - } else { - // Resume - us_socket_resume(@intFromBool(ssl), socket); - } + if (pause) socket.pause(ssl) else socket.@"resume"(ssl); return true; }, .connecting => |_| { @@ -1173,7 +1155,7 @@ pub const InternalSocket = union(enum) { .pipe, .upgradedDuplex, .connecting, .detached => return false, .connected => |socket| { // only supported by connected sockets - us_socket_nodelay(socket, @intFromBool(enabled)); + socket.setNodelay(enabled); return true; }, } @@ -1183,21 +1165,15 @@ pub const InternalSocket = union(enum) { .pipe, .upgradedDuplex, .connecting, .detached => return false, .connected => |socket| { // only supported by connected sockets and can fail - return us_socket_keepalive(socket, @intFromBool(enabled), delay) == 0; + return socket.setKeepalive(enabled, delay) == 0; }, } } - pub fn close(this: InternalSocket, comptime is_ssl: bool, code: CloseCode) void { + pub fn close(this: InternalSocket, comptime is_ssl: bool, code: Socket.CloseCode) void { switch (this) { .detached => {}, .connected => |socket| { - debug("us_socket_close({d})", .{@intFromPtr(socket)}); - _ = us_socket_close( - comptime @intFromBool(is_ssl), - socket, - code, - null, - ); + socket.close(is_ssl, code); }, .connecting => |socket| { debug("us_connecting_socket_close({d})", .{@intFromPtr(socket)}); @@ -1217,7 +1193,7 @@ pub const InternalSocket = union(enum) { pub fn isClosed(this: InternalSocket, comptime is_ssl: bool) bool { return switch (this) { - .connected => |socket| us_socket_is_closed(@intFromBool(is_ssl), socket) > 0, + .connected => |socket| socket.isClosed(is_ssl), .connecting => |socket| us_connecting_socket_is_closed(@intFromBool(is_ssl), socket) > 0, .detached => true, .upgradedDuplex => |socket| socket.isClosed(), @@ -1310,7 +1286,7 @@ pub fn NewSocketHandler(comptime is_ssl: bool) type { switch (this.socket) { .upgradedDuplex => |socket| socket.setTimeout(seconds), .pipe => |pipe| if (Environment.isWindows) pipe.setTimeout(seconds), - .connected => |socket| us_socket_timeout(comptime ssl_int, socket, seconds), + .connected => |socket| socket.setTimeout(is_ssl, seconds), .connecting => |socket| us_connecting_socket_timeout(comptime ssl_int, socket, seconds), .detached => {}, } @@ -1320,11 +1296,11 @@ pub fn NewSocketHandler(comptime is_ssl: bool) type { switch (this.socket) { .connected => |socket| { if (seconds > 240) { - us_socket_timeout(comptime ssl_int, socket, 0); - us_socket_long_timeout(comptime ssl_int, socket, seconds / 60); + socket.setTimeout(is_ssl, 0); + socket.setLongTimeout(is_ssl, seconds / 60); } else { - us_socket_timeout(comptime ssl_int, socket, seconds); - us_socket_long_timeout(comptime ssl_int, socket, 0); + socket.setTimeout(is_ssl, seconds); + socket.setLongTimeout(is_ssl, 0); } }, .connecting => |socket| { @@ -1345,8 +1321,8 @@ pub fn NewSocketHandler(comptime is_ssl: bool) type { pub fn setTimeoutMinutes(this: ThisSocket, minutes: c_uint) void { switch (this.socket) { .connected => |socket| { - us_socket_timeout(comptime ssl_int, socket, 0); - us_socket_long_timeout(comptime ssl_int, socket, minutes); + socket.setTimeout(is_ssl, 0); + socket.setLongTimeout(is_ssl, minutes); }, .connecting => |socket| { us_connecting_socket_timeout(comptime ssl_int, socket, 0); @@ -1359,8 +1335,7 @@ pub fn NewSocketHandler(comptime is_ssl: bool) type { } pub fn startTLS(this: ThisSocket, is_client: bool) void { - const socket = this.socket.get() orelse return; - _ = us_socket_open(comptime ssl_int, socket, @intFromBool(is_client), null, 0); + if (this.socket.get()) |socket| socket.open(is_ssl, is_client, null); } pub fn ssl(this: ThisSocket) ?*BoringSSL.SSL { @@ -1393,7 +1368,7 @@ pub fn NewSocketHandler(comptime is_ssl: bool) type { const ValueType = if (deref) ContextType else *ContextType; fn getValue(socket: *Socket) ValueType { if (comptime ContextType == anyopaque) { - return us_socket_ext(1, socket); + return socket.ext(true); } if (comptime deref_) { @@ -1504,7 +1479,7 @@ pub fn NewSocketHandler(comptime is_ssl: bool) type { pub fn getNativeHandle(this: ThisSocket) ?*NativeSocketHandleType(is_ssl) { return @ptrCast(switch (this.socket) { - .connected => |socket| us_socket_get_native_handle(comptime ssl_int, socket), + .connected => |socket| socket.getNativeHandle(is_ssl), .connecting => |socket| us_connecting_socket_get_native_handle(comptime ssl_int, socket), .detached => null, .upgradedDuplex => |socket| if (is_ssl) @as(*anyopaque, @ptrCast(socket.ssl() orelse return null)) else null, @@ -1517,11 +1492,12 @@ pub fn NewSocketHandler(comptime is_ssl: bool) type { @compileError("SSL sockets do not have a file descriptor accessible this way"); } const socket = this.socket.get() orelse return bun.invalid_fd; + return if (comptime Environment.isWindows) // on windows uSockets exposes SOCKET - bun.toFD(@as(bun.FDImpl.System, @ptrCast(us_socket_get_native_handle(0, socket)))) + bun.toFD(@as(bun.FDImpl.System, @ptrCast(socket.getNativeHandle(is_ssl).?))) else - bun.toFD(@as(i32, @intCast(@intFromPtr(us_socket_get_native_handle(0, socket))))); + bun.toFD(@as(i32, @intCast(@intFromPtr(socket.getNativeHandle(is_ssl))))); } pub fn markNeedsMoreForSendfile(this: ThisSocket) void { @@ -1529,7 +1505,7 @@ pub fn NewSocketHandler(comptime is_ssl: bool) type { @compileError("SSL sockets do not support sendfile yet"); } const socket = this.socket.get() orelse return; - us_socket_sendfile_needs_more(socket); + socket.sendFileNeedsMore(); } pub fn ext(this: ThisSocket, comptime ContextType: type) ?*ContextType { @@ -1539,7 +1515,7 @@ pub fn NewSocketHandler(comptime is_ssl: bool) type { std.meta.alignment(ContextType); const ptr = switch (this.socket) { - .connected => |sock| us_socket_ext(comptime ssl_int, sock), + .connected => |sock| sock.ext(is_ssl), .connecting => |sock| us_connecting_socket_ext(comptime ssl_int, sock), .detached => return null, .upgradedDuplex => return null, @@ -1552,7 +1528,7 @@ pub fn NewSocketHandler(comptime is_ssl: bool) type { /// This can be null if the socket was closed. pub fn context(this: ThisSocket) ?*SocketContext { switch (this.socket) { - .connected => |socket| return us_socket_context(comptime ssl_int, socket), + .connected => |socket| return socket.context(is_ssl), .connecting => |socket| return us_connecting_socket_context(comptime ssl_int, socket), .detached => return null, .upgradedDuplex => return null, @@ -1562,81 +1538,33 @@ pub fn NewSocketHandler(comptime is_ssl: bool) type { pub fn flush(this: ThisSocket) void { switch (this.socket) { - .upgradedDuplex => |socket| { - return socket.flush(); - }, - .pipe => |pipe| { - return if (Environment.isWindows) pipe.flush() else return; - }, - .connected => |socket| { - return us_socket_flush( - comptime ssl_int, - socket, - ); - }, + .upgradedDuplex => |socket| socket.flush(), + .pipe => |pipe| if (comptime Environment.isWindows) pipe.flush(), + .connected => |socket| socket.flush(is_ssl), .connecting, .detached => return, } } pub fn write(this: ThisSocket, data: []const u8, msg_more: bool) i32 { - switch (this.socket) { - .upgradedDuplex => |socket| { - return socket.encodeAndWrite(data, msg_more); - }, - .pipe => |pipe| { - return if (Environment.isWindows) pipe.encodeAndWrite(data, msg_more) else 0; - }, - .connected => |socket| { - const result = us_socket_write( - comptime ssl_int, - socket, - data.ptr, - // truncate to 31 bits since sign bit exists - @as(i32, @intCast(@as(u31, @truncate(data.len)))), - @as(i32, @intFromBool(msg_more)), - ); - - if (comptime Environment.allow_assert) { - debug("us_socket_write({*}, {d}) = {d}", .{ this.getNativeHandle(), data.len, result }); - } - - return result; - }, - .connecting, .detached => return 0, - } + return switch (this.socket) { + .upgradedDuplex => |socket| socket.encodeAndWrite(data, msg_more), + .pipe => |pipe| if (comptime Environment.isWindows) pipe.encodeAndWrite(data, msg_more) else 0, + .connected => |socket| socket.write(is_ssl, data, msg_more), + .connecting, .detached => 0, + }; } pub fn rawWrite(this: ThisSocket, data: []const u8, msg_more: bool) i32 { - switch (this.socket) { - .connected => |socket| { - return us_socket_raw_write( - comptime ssl_int, - socket, - data.ptr, - // truncate to 31 bits since sign bit exists - @as(i32, @intCast(@as(u31, @truncate(data.len)))), - @as(i32, @intFromBool(msg_more)), - ); - }, - .connecting, .detached => return 0, - .upgradedDuplex => |socket| { - return socket.rawWrite(data, msg_more); - }, - .pipe => |pipe| { - return if (Environment.isWindows) pipe.rawWrite(data, msg_more) else 0; - }, - } + return switch (this.socket) { + .connected => |socket| socket.rawWrite(is_ssl, data, msg_more), + .connecting, .detached => 0, + .upgradedDuplex => |socket| socket.rawWrite(data, msg_more), + .pipe => |pipe| if (comptime Environment.isWindows) pipe.rawWrite(data, msg_more) else 0, + }; } pub fn shutdown(this: ThisSocket) void { - // debug("us_socket_shutdown({d})", .{@intFromPtr(this.socket)}); switch (this.socket) { - .connected => |socket| { - debug("us_socket_shutdown({d})", .{@intFromPtr(socket)}); - return us_socket_shutdown( - comptime ssl_int, - socket, - ); - }, + .connected => |socket| socket.shutdown(is_ssl), .connecting => |socket| { debug("us_connecting_socket_shutdown({d})", .{@intFromPtr(socket)}); return us_connecting_socket_shutdown( @@ -1645,24 +1573,14 @@ pub fn NewSocketHandler(comptime is_ssl: bool) type { ); }, .detached => {}, - .upgradedDuplex => |socket| { - socket.shutdown(); - }, - .pipe => |pipe| { - if (Environment.isWindows) pipe.shutdown(); - }, + .upgradedDuplex => |socket| socket.shutdown(), + .pipe => |pipe| if (comptime Environment.isWindows) pipe.shutdown(), } } pub fn shutdownRead(this: ThisSocket) void { switch (this.socket) { - .connected => |socket| { - debug("us_socket_shutdown_read({d})", .{@intFromPtr(socket)}); - return us_socket_shutdown_read( - comptime ssl_int, - socket, - ); - }, + .connected => |socket| socket.shutdownRead(is_ssl), .connecting => |socket| { debug("us_connecting_socket_shutdown_read({d})", .{@intFromPtr(socket)}); return us_connecting_socket_shutdown_read( @@ -1670,40 +1588,26 @@ pub fn NewSocketHandler(comptime is_ssl: bool) type { socket, ); }, + .upgradedDuplex => |socket| socket.shutdownRead(), + .pipe => |pipe| if (comptime Environment.isWindows) pipe.shutdownRead(), .detached => {}, - .upgradedDuplex => |socket| { - socket.shutdownRead(); - }, - .pipe => |pipe| { - if (Environment.isWindows) pipe.shutdownRead(); - }, } } pub fn isShutdown(this: ThisSocket) bool { - switch (this.socket) { - .connected => |socket| { - debug("us_socket_is_shut_down({d})", .{@intFromPtr(socket)}); - return us_socket_is_shut_down( - comptime ssl_int, - socket, - ) > 0; - }, - .connecting => |socket| { + return switch (this.socket) { + .connected => |socket| socket.isShutDown(is_ssl), + .connecting => |socket| blk: { debug("us_connecting_socket_is_shut_down({d})", .{@intFromPtr(socket)}); - return us_connecting_socket_is_shut_down( + break :blk us_connecting_socket_is_shut_down( comptime ssl_int, socket, ) > 0; }, - .detached => return true, - .upgradedDuplex => |socket| { - return socket.isShutdown(); - }, - .pipe => |pipe| { - return if (Environment.isWindows) pipe.isShutdown() else false; - }, - } + .upgradedDuplex => |socket| socket.isShutdown(), + .pipe => |pipe| return if (Environment.isWindows) pipe.isShutdown() else false, + .detached => true, + }; } pub fn isClosedOrHasError(this: ThisSocket) bool { @@ -1744,36 +1648,24 @@ pub fn NewSocketHandler(comptime is_ssl: bool) type { return this.socket.isClosed(comptime is_ssl); } - pub fn close(this: ThisSocket, code: CloseCode) void { + pub fn close(this: ThisSocket, code: Socket.CloseCode) void { return this.socket.close(comptime is_ssl, code); } pub fn localPort(this: ThisSocket) i32 { - switch (this.socket) { - .connected => |socket| { - debug("us_socket_local_port({d})", .{@intFromPtr(socket)}); - return us_socket_local_port( - comptime ssl_int, - socket, - ); - }, - .pipe, .upgradedDuplex, .connecting, .detached => return 0, - } + return switch (this.socket) { + .connected => |socket| socket.localPort(is_ssl), + .pipe, .upgradedDuplex, .connecting, .detached => 0, + }; } - pub fn remoteAddress(this: ThisSocket, buf: [*]u8, length: *i32) void { - switch (this.socket) { - .connected => |socket| { - debug("us_socket_remote_address({d})", .{@intFromPtr(socket)}); - return us_socket_remote_address( - comptime ssl_int, - socket, - buf, - length, - ); + + /// `buf` cannot be longer than 2^31 bytes long. + pub fn remoteAddress(this: ThisSocket, buf: []u8) ?[]const u8 { + return switch (this.socket) { + .connected => |sock| sock.remoteAddress(is_ssl, buf) catch |e| { + bun.Output.panic("Failed to get socket's remote address: {s}", .{@errorName(e)}); }, - .pipe, .upgradedDuplex, .connecting, .detached => return { - length.* = 0; - }, - } + .pipe, .upgradedDuplex, .connecting, .detached => null, + }; } /// Get the local address of a socket in binary format. @@ -1784,23 +1676,12 @@ pub fn NewSocketHandler(comptime is_ssl: bool) type { /// # Returns /// This function returns a slice of the buffer on success, or null on failure. pub fn localAddress(this: ThisSocket, buf: []u8) ?[]const u8 { - switch (this.socket) { - .connected => |socket| { - var length: i32 = @intCast(buf.len); - us_socket_local_address( - comptime ssl_int, - socket, - buf.ptr, - &length, - ); - - if (length <= 0) { - return null; - } - return buf[0..@intCast(length)]; + return switch (this.socket) { + .connected => |sock| sock.localAddress(is_ssl, buf) catch |e| { + bun.Output.panic("Failed to get socket's local address: {s}", .{@errorName(e)}); }, - .pipe, .upgradedDuplex, .connecting, .detached => return null, - } + .pipe, .upgradedDuplex, .connecting, .detached => null, + }; } pub fn connect( @@ -1978,7 +1859,6 @@ pub fn NewSocketHandler(comptime is_ssl: bool) type { comptime Fields: anytype, ) void { const SocketHandlerType = NewSocketHandler(ssl_type); - const ssl_type_int: i32 = @intFromBool(ssl_type); const Type = comptime if (@TypeOf(Fields) != type) @TypeOf(Fields) else Fields; const SocketHandler = struct { @@ -1990,7 +1870,7 @@ pub fn NewSocketHandler(comptime is_ssl: bool) type { const ValueType = if (deref) ContextType else *ContextType; fn getValue(socket: *Socket) ValueType { if (comptime ContextType == anyopaque) { - return us_socket_ext(ssl_type_int, socket).?; + return socket.ext(is_ssl); } if (comptime deref_) { @@ -2061,7 +1941,7 @@ pub fn NewSocketHandler(comptime is_ssl: bool) type { } pub fn on_connect_error(socket: *Socket, code: i32) callconv(.C) ?*Socket { const val = if (comptime ContextType == anyopaque) - us_socket_ext(comptime ssl_int, socket) + socket.ext(is_ssl) else if (comptime deref_) SocketHandlerType.from(socket).ext(ContextType).?.* else @@ -2122,7 +2002,7 @@ pub fn NewSocketHandler(comptime is_ssl: bool) type { const ValueType = if (deref) ContextType else *ContextType; fn getValue(socket: *Socket) ValueType { if (comptime ContextType == anyopaque) { - return us_socket_ext(comptime ssl_int, socket); + return socket.ext(is_ssl); } if (comptime deref_) { @@ -2200,7 +2080,7 @@ pub fn NewSocketHandler(comptime is_ssl: bool) type { } pub fn on_connect_error(socket: *Socket, code: i32) callconv(.C) ?*Socket { const val = if (comptime ContextType == anyopaque) - us_socket_ext(comptime ssl_int, socket) + socket.ext(is_ssl) else if (comptime deref_) ThisSocket.from(socket).ext(ContextType).?.* else @@ -2805,27 +2685,8 @@ pub const Poll = opaque { extern fn us_poll_resize(p: ?*Poll, loop: ?*Loop, ext_size: c_uint) ?*Poll; }; -extern fn us_socket_get_native_handle(ssl: i32, s: ?*Socket) ?*anyopaque; extern fn us_connecting_socket_get_native_handle(ssl: i32, s: ?*ConnectingSocket) ?*anyopaque; -extern fn us_socket_timeout(ssl: i32, s: ?*Socket, seconds: c_uint) void; -extern fn us_socket_long_timeout(ssl: i32, s: ?*Socket, seconds: c_uint) void; -extern fn us_socket_ext(ssl: i32, s: ?*Socket) *anyopaque; -extern fn us_socket_context(ssl: i32, s: ?*Socket) ?*SocketContext; -extern fn us_socket_flush(ssl: i32, s: ?*Socket) void; -extern fn us_socket_write(ssl: i32, s: ?*Socket, data: [*c]const u8, length: i32, msg_more: i32) i32; -extern fn us_socket_raw_write(ssl: i32, s: ?*Socket, data: [*c]const u8, length: i32, msg_more: i32) i32; -extern fn us_socket_shutdown(ssl: i32, s: ?*Socket) void; -extern fn us_socket_shutdown_read(ssl: i32, s: ?*Socket) void; -extern fn us_socket_is_shut_down(ssl: i32, s: ?*Socket) i32; -extern fn us_socket_is_closed(ssl: i32, s: ?*Socket) i32; -extern fn us_socket_close(ssl: i32, s: ?*Socket, code: CloseCode, reason: ?*anyopaque) ?*Socket; - -extern fn us_socket_nodelay(s: ?*Socket, enable: c_int) void; -extern fn us_socket_keepalive(s: ?*Socket, enable: c_int, delay: c_uint) c_int; -extern fn us_socket_pause(ssl: i32, s: ?*Socket) void; -extern fn us_socket_resume(ssl: i32, s: ?*Socket) void; - extern fn us_connecting_socket_timeout(ssl: i32, s: ?*ConnectingSocket, seconds: c_uint) void; extern fn us_connecting_socket_long_timeout(ssl: i32, s: ?*ConnectingSocket, seconds: c_uint) void; extern fn us_connecting_socket_ext(ssl: i32, s: ?*ConnectingSocket) *anyopaque; @@ -2839,13 +2700,6 @@ extern fn us_connecting_socket_get_error(ssl: i32, s: ?*ConnectingSocket) i32; pub extern fn us_connecting_socket_get_loop(s: *ConnectingSocket) *Loop; -// if a TLS socket calls this, it will start SSL instance and call open event will also do TLS handshake if required -// will have no effect if the socket is closed or is not TLS -extern fn us_socket_open(ssl: i32, s: ?*Socket, is_client: i32, ip: [*c]const u8, ip_length: i32) ?*Socket; - -extern fn us_socket_local_port(ssl: i32, s: ?*Socket) i32; -extern fn us_socket_remote_address(ssl: i32, s: ?*Socket, buf: [*c]u8, length: [*c]i32) void; -extern fn us_socket_local_address(ssl: i32, s: ?*Socket, buf: [*c]u8, length: [*c]i32) void; pub const uws_app_s = opaque {}; pub const uws_req_s = opaque {}; pub const uws_header_iterator_s = opaque {}; @@ -3159,7 +3013,8 @@ pub const ListenSocket = opaque { us_listen_socket_close(@intFromBool(ssl), this); } pub fn getLocalPort(this: *ListenSocket, ssl: bool) i32 { - return us_socket_local_port(@intFromBool(ssl), @as(*uws.Socket, @ptrCast(this))); + const self: *uws.Socket = @ptrCast(this); + return self.localPort(ssl); } }; extern fn us_listen_socket_close(ssl: i32, ls: *ListenSocket) void; @@ -3452,7 +3307,8 @@ pub fn NewApp(comptime ssl: bool) type { return us_listen_socket_close(ssl_flag, @as(*uws.ListenSocket, @ptrCast(this))); } pub inline fn getLocalPort(this: *ThisApp.ListenSocket) i32 { - return us_socket_local_port(ssl_flag, @as(*uws.Socket, @ptrCast(this))); + const self: *uws.Socket = @ptrCast(this); + return self.localPort(ssl); } pub fn socket(this: *ThisApp.ListenSocket) NewSocketHandler(ssl) { @@ -4311,8 +4167,6 @@ pub const State = enum(u8) { } }; -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, @@ -4481,19 +4335,19 @@ pub const AnySocket = union(enum) { } pub fn shutdown(this: AnySocket) void { - debug("us_socket_shutdown({d})", .{@intFromPtr(this.socket())}); - return us_socket_shutdown( - @intFromBool(this.isSSL()), - this.socket(), - ); + switch (this) { + .SocketTCP => |sock| sock.shutdown(), + .SocketTLS => |sock| sock.shutdown(), + } } + pub fn shutdownRead(this: AnySocket) void { - debug("us_socket_shutdown_read({d})", .{@intFromPtr(this.socket())}); - return us_socket_shutdown_read( - @intFromBool(this.isSSL()), - this.socket(), - ); + switch (this) { + .SocketTCP => |sock| sock.shutdownRead(), + .SocketTLS => |sock| sock.shutdownRead(), + } } + pub fn isShutdown(this: AnySocket) bool { return switch (this) { .SocketTCP => this.SocketTCP.isShutdown(), @@ -4519,26 +4373,23 @@ pub const AnySocket = union(enum) { pub fn write(this: AnySocket, data: []const u8, msg_more: bool) i32 { return switch (this) { - .SocketTCP => return this.SocketTCP.write(data, msg_more), - .SocketTLS => return this.SocketTLS.write(data, msg_more), + .SocketTCP => |sock| sock.write(data, msg_more), + .SocketTLS => |sock| sock.write(data, msg_more), }; } pub fn getNativeHandle(this: AnySocket) ?*anyopaque { return switch (this.socket()) { - .connected => |sock| us_socket_get_native_handle( - @intFromBool(this.isSSL()), - sock, - ).?, + .connected => |sock| sock.getNativeHandle(this.isSSL()), else => null, }; } pub fn localPort(this: AnySocket) i32 { - return us_socket_local_port( - @intFromBool(this.isSSL()), - this.socket(), - ); + switch (this) { + .SocketTCP => |sock| sock.localPort(), + .SocketTLS => |sock| sock.localPort(), + } } pub fn isSSL(this: AnySocket) bool { @@ -4556,18 +4407,17 @@ pub const AnySocket = union(enum) { } pub fn ext(this: AnySocket, comptime ContextType: type) ?*ContextType { - const ptr = us_socket_ext( - this.isSSL(), - this.socket(), - ) orelse return null; + const ptr = this.socket().ext(this.isSSL()) orelse return null; return @ptrCast(@alignCast(ptr)); } + pub fn context(this: AnySocket) *SocketContext { - return us_socket_context( - this.isSSL(), - this.socket(), - ).?; + @setRuntimeSafety(true); + return switch (this) { + .SocketTCP => |sock| sock.context(), + .SocketTLS => |sock| sock.context(), + }.?; } }; diff --git a/src/deps/uws/socket.zig b/src/deps/uws/socket.zig new file mode 100644 index 0000000000..d4e5edff57 --- /dev/null +++ b/src/deps/uws/socket.zig @@ -0,0 +1,181 @@ +const std = @import("std"); +const bun = @import("root").bun; +const uws = @import("../uws.zig"); + +const SocketContext = uws.SocketContext; + +const debug = bun.Output.scoped(.uws, false); +const max_i32 = std.math.maxInt(i32); + +/// Zig bindings for `us_socket_t` +pub const Socket = opaque { + pub const CloseCode = enum(i32) { + normal = 0, + failure = 1, + }; + + pub fn open(this: *Socket, comptime is_ssl: bool, is_client: bool, ip_addr: ?[]const u8) void { + debug("us_socket_open({d}, is_client: {})", .{ @intFromPtr(this), is_client }); + const ssl = @intFromBool(is_ssl); + + if (ip_addr) |ip| { + bun.assert(ip.len < max_i32); + _ = us_socket_open(ssl, this, @intFromBool(is_client), ip.ptr, @intCast(ip.len)); + } else { + _ = us_socket_open(ssl, this, @intFromBool(is_client), null, 0); + } + } + + pub fn pause(this: *Socket, ssl: bool) void { + debug("us_socket_pause({d})", .{@intFromPtr(this)}); + us_socket_pause(@intFromBool(ssl), this); + } + + pub fn @"resume"(this: *Socket, ssl: bool) void { + debug("us_socket_resume({d})", .{@intFromPtr(this)}); + us_socket_resume(@intFromBool(ssl), this); + } + + pub fn close(this: *Socket, ssl: bool, code: CloseCode) void { + debug("us_socket_close({d}, {s})", .{ @intFromPtr(this), @tagName(code) }); + _ = us_socket_close(@intFromBool(ssl), this, code, null); + } + + pub fn shutdown(this: *Socket, ssl: bool) void { + debug("us_socket_shutdown({d})", .{@intFromPtr(this)}); + us_socket_shutdown(@intFromBool(ssl), this); + } + + pub fn shutdownRead(this: *Socket, ssl: bool) void { + us_socket_shutdown_read(@intFromBool(ssl), this); + } + + pub fn isClosed(this: *Socket, ssl: bool) bool { + return us_socket_is_closed(@intFromBool(ssl), this) > 0; + } + + pub fn isShutDown(this: *Socket, ssl: bool) bool { + return us_socket_is_shut_down(@intFromBool(ssl), this) > 0; + } + + pub fn localPort(this: *Socket, ssl: bool) i32 { + return us_socket_local_port(@intFromBool(ssl), this); + } + + /// Returned slice is a view into `buf`. + pub fn localAddress(this: *Socket, ssl: bool, buf: []u8) ![]const u8 { + var length: i32 = @intCast(buf.len); + + us_socket_local_address(@intFromBool(ssl), this, buf.ptr, &length); + if (length < 0) { + const errno = bun.C.getErrno(length); + bun.debugAssert(errno != .SUCCESS); + return bun.errnoToZigErr(errno); + } + bun.unsafeAssert(buf.len >= length); + + return buf[0..@intCast(length)]; + } + + /// Returned slice is a view into `buf`. On error, `errno` should be set + pub fn remoteAddress(this: *Socket, ssl: bool, buf: []u8) ![]const u8 { + var length: i32 = @intCast(buf.len); + + us_socket_remote_address(@intFromBool(ssl), this, buf.ptr, &length); + if (length < 0) { + const errno = bun.C.getErrno(length); + bun.debugAssert(errno != .SUCCESS); + return bun.errnoToZigErr(errno); + } + bun.unsafeAssert(buf.len >= length); + + return buf[0..@intCast(length)]; + } + + pub fn setTimeout(this: *Socket, ssl: bool, seconds: u32) void { + us_socket_timeout(@intFromBool(ssl), this, @intCast(seconds)); + } + + pub fn setLongTimeout(this: *Socket, ssl: bool, minutes: u32) void { + us_socket_long_timeout(@intFromBool(ssl), this, @intCast(minutes)); + } + + pub fn setNodelay(this: *Socket, enabled: bool) void { + us_socket_nodelay(this, @intFromBool(enabled)); + } + + /// Returns error code. `0` on success. error codes depend on platform an + /// configured event loop. + pub fn setKeepalive(this: *Socket, enabled: bool, delay: u32) i32 { + return us_socket_keepalive(this, @intFromBool(enabled), @intCast(delay)); + } + + pub fn getNativeHandle(this: *Socket, ssl: bool) ?*anyopaque { + return us_socket_get_native_handle(@intFromBool(ssl), this); + } + + pub fn ext(this: *Socket, ssl: bool) *anyopaque { + @setRuntimeSafety(true); + return us_socket_ext(@intFromBool(ssl), this).?; + } + + pub fn context(this: *Socket, ssl: bool) *SocketContext { + @setRuntimeSafety(true); + return us_socket_context(@intFromBool(ssl), this).?; + } + + pub fn write(this: *Socket, ssl: bool, data: []const u8, msg_more: bool) i32 { + debug("us_socket_write({d}, {d})", .{ @intFromPtr(this), data.len }); + return us_socket_write(@intFromBool(ssl), this, data.ptr, @intCast(data.len), @intFromBool(msg_more)); + } + + pub fn write2(this: *Socket, ssl: bool, first: []const u8, second: []const u8) i32 { + const rc = us_socket_write2(@intFromBool(ssl), this, first.ptr, first.len, second.ptr, second.len); + debug("us_socket_write2({d}, {d}, {d}) = {d}", .{ @intFromPtr(this), first.len, second.len, rc }); + return rc; + } + + pub fn rawWrite(this: *Socket, ssl: bool, data: []const u8, msg_more: bool) i32 { + debug("us_socket_raw_write({d}, {d})", .{ @intFromPtr(this), data.len }); + return us_socket_raw_write(@intFromBool(ssl), this, data.ptr, @intCast(data.len), @intFromBool(msg_more)); + } + + pub fn flush(this: *Socket, ssl: bool) void { + us_socket_flush(@intFromBool(ssl), this); + } + + pub fn sendFileNeedsMore(this: *Socket) void { + us_socket_sendfile_needs_more(this); + } + + extern fn us_socket_get_native_handle(ssl: i32, s: ?*Socket) ?*anyopaque; + + extern fn us_socket_local_port(ssl: i32, s: ?*Socket) i32; + extern fn us_socket_remote_address(ssl: i32, s: ?*Socket, buf: [*c]u8, length: [*c]i32) void; + extern fn us_socket_local_address(ssl: i32, s: ?*Socket, buf: [*c]u8, length: [*c]i32) void; + extern fn us_socket_timeout(ssl: i32, s: ?*Socket, seconds: c_uint) void; + extern fn us_socket_long_timeout(ssl: i32, s: ?*Socket, minutes: c_uint) void; + extern fn us_socket_nodelay(s: ?*Socket, enable: c_int) void; + extern fn us_socket_keepalive(s: ?*Socket, enable: c_int, delay: c_uint) c_int; + + extern fn us_socket_ext(ssl: i32, s: ?*Socket) ?*anyopaque; // nullish to be safe + extern fn us_socket_context(ssl: i32, s: ?*Socket) ?*SocketContext; + + extern fn us_socket_write(ssl: i32, s: ?*Socket, data: [*c]const u8, length: i32, msg_more: i32) i32; + extern "c" fn us_socket_write2(ssl: i32, *Socket, header: ?[*]const u8, len: usize, payload: ?[*]const u8, usize) i32; + extern fn us_socket_raw_write(ssl: i32, s: ?*Socket, data: [*c]const u8, length: i32, msg_more: i32) i32; + extern fn us_socket_flush(ssl: i32, s: ?*Socket) void; + + // if a TLS socket calls this, it will start SSL instance and call open event will also do TLS handshake if required + // will have no effect if the socket is closed or is not TLS + extern fn us_socket_open(ssl: i32, s: ?*Socket, is_client: i32, ip: [*c]const u8, ip_length: i32) ?*Socket; + extern fn us_socket_pause(ssl: i32, s: ?*Socket) void; + extern fn us_socket_resume(ssl: i32, s: ?*Socket) void; + extern fn us_socket_close(ssl: i32, s: ?*Socket, code: CloseCode, reason: ?*anyopaque) ?*Socket; + extern fn us_socket_shutdown(ssl: i32, s: ?*Socket) void; + extern fn us_socket_is_closed(ssl: i32, s: ?*Socket) i32; + extern fn us_socket_shutdown_read(ssl: i32, s: ?*Socket) void; + extern fn us_socket_is_shut_down(ssl: i32, s: ?*Socket) i32; + + extern fn us_socket_sendfile_needs_more(socket: *Socket) void; +};