Merge branch 'lun-4-net-reuseaddr-opt'

Closes #3820
master
Andrew Kelley 2019-12-30 19:36:43 -05:00
commit 86ba8c06bf
No known key found for this signature in database
GPG Key ID: 7C5F548F728501A9
3 changed files with 47 additions and 1 deletions

View File

@ -120,7 +120,8 @@ pub extern "c" fn listen(sockfd: fd_t, backlog: c_uint) c_int;
pub extern "c" fn getsockname(sockfd: fd_t, noalias addr: *sockaddr, noalias addrlen: *socklen_t) c_int;
pub extern "c" fn connect(sockfd: fd_t, sock_addr: *const sockaddr, addrlen: socklen_t) c_int;
pub extern "c" fn accept4(sockfd: fd_t, addr: *sockaddr, addrlen: *socklen_t, flags: c_uint) c_int;
pub extern "c" fn getsockopt(sockfd: fd_t, level: c_int, optname: c_int, optval: *c_void, optlen: *socklen_t) c_int;
pub extern "c" fn getsockopt(sockfd: fd_t, level: u32, optname: u32, optval: *c_void, optlen: *socklen_t) c_int;
pub extern "c" fn setsockopt(sockfd: fd_t, level: u32, optname: u32, optval: *c_void, optlen: socklen_t) c_int;
pub extern "c" fn send(sockfd: fd_t, buf: *const c_void, len: usize, flags: u32) isize;
pub extern "c" fn sendto(
sockfd: fd_t,

View File

@ -1279,6 +1279,7 @@ fn dnsParseCallback(ctx: dpc_ctx, rr: u8, data: []const u8, packet: []const u8)
pub const StreamServer = struct {
/// Copied from `Options` on `init`.
kernel_backlog: u32,
reuse_address: bool,
/// `undefined` until `listen` returns successfully.
listen_address: Address,
@ -1290,6 +1291,9 @@ pub const StreamServer = struct {
/// If more than this many connections pool in the kernel, clients will start
/// seeing "Connection refused".
kernel_backlog: u32 = 128,
/// Enable SO_REUSEADDR on the socket.
reuse_address: bool = false,
};
/// After this call succeeds, resources have been acquired and must
@ -1298,6 +1302,7 @@ pub const StreamServer = struct {
return StreamServer{
.sockfd = null,
.kernel_backlog = options.kernel_backlog,
.reuse_address = options.reuse_address,
.listen_address = undefined,
};
}
@ -1320,6 +1325,15 @@ pub const StreamServer = struct {
self.sockfd = null;
}
if (self.reuse_address) {
try os.setsockopt(
self.sockfd.?,
os.SOL_SOCKET,
os.SO_REUSEADDR,
&mem.toBytes(@as(c_int, 1)),
);
}
var socklen = address.getOsSockLen();
try os.bind(sockfd, &address.any, socklen);
try os.listen(sockfd, self.kernel_backlog);

View File

@ -3250,3 +3250,34 @@ pub fn sched_yield() SchedYieldError!void {
else => return error.SystemCannotYield,
}
}
pub const SetSockOptError = error{
/// The socket is already connected, and a specified option cannot be set while the socket is connected.
AlreadyConnected,
/// The option is not supported by the protocol.
InvalidProtocolOption,
/// The send and receive timeout values are too big to fit into the timeout fields in the socket structure.
TimeoutTooBig,
/// Insufficient resources are available in the system to complete the call.
SystemResources,
} || UnexpectedError;
/// Set a socket's options.
pub fn setsockopt(fd: fd_t, level: u32, optname: u32, opt: []const u8) SetSockOptError!void {
switch (errno(system.setsockopt(fd, level, optname, opt.ptr, @intCast(socklen_t, opt.len)))) {
0 => {},
EBADF => unreachable, // always a race condition
ENOTSOCK => unreachable, // always a race condition
EINVAL => unreachable,
EFAULT => unreachable,
EDOM => return error.TimeoutTooBig,
EISCONN => return error.AlreadyConnected,
ENOPROTOOPT => return error.InvalidProtocolOption,
ENOMEM => return error.SystemResources,
ENOBUFS => return error.SystemResources,
else => |err| return unexpectedErrno(err),
}
}