mirror of
https://github.com/oven-sh/bun
synced 2026-02-14 21:01:52 +00:00
fix(uws): make Socket bindings safer (#18286)
Co-authored-by: DonIsaac <22823424+DonIsaac@users.noreply.github.com> Co-authored-by: chloe caruso <git@paperclover.net>
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
354
src/deps/uws.zig
354
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(),
|
||||
}.?;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
181
src/deps/uws/socket.zig
Normal file
181
src/deps/uws/socket.zig
Normal file
@@ -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;
|
||||
};
|
||||
Reference in New Issue
Block a user