| author | |
| committer | |
| log | 13068da43e8fbd0ae5d03aff27fb4e8802e1218c |
| tree | a5fd65eb86f80ac4b0d6495ac3b34784b45d0612 |
| parent | 2ab588049e41a96337a4fa8c2d9507320bc4278b |
Address comments from @ifreund and @MasterQ32 to address unsafeness and
ergonomics of the `Address` API.
Rename the `TCP` namespace to `tcp` as it does not contain any
top-level fields.
Fix missing reference to `sockaddr` which was identified by @kprotty in
os/bits/linux/arm64.zig.8 files changed, 534 insertions(+), 435 deletions(-)
lib/std/builtin.zig+1-17| ... | ... | @@ -150,23 +150,7 @@ pub const Mode = enum { |
| 150 | 150 | |
| 151 | 151 | /// This data structure is used by the Zig language code generation and |
| 152 | 152 | /// therefore must be kept in sync with the compiler implementation. |
| 153 | pub const CallingConvention = enum { | |
| 154 | Unspecified, | |
| 155 | C, | |
| 156 | Naked, | |
| 157 | Async, | |
| 158 | Inline, | |
| 159 | Interrupt, | |
| 160 | Signal, | |
| 161 | Stdcall, | |
| 162 | Fastcall, | |
| 163 | Vectorcall, | |
| 164 | Thiscall, | |
| 165 | APCS, | |
| 166 | AAPCS, | |
| 167 | AAPCSVFP, | |
| 168 | SysV | |
| 169 | }; | |
| 153 | pub const CallingConvention = enum { Unspecified, C, Naked, Async, Inline, Interrupt, Signal, Stdcall, Fastcall, Vectorcall, Thiscall, APCS, AAPCS, AAPCSVFP, SysV }; | |
| 170 | 154 | |
| 171 | 155 | /// This data structure is used by the Zig language code generation and |
| 172 | 156 | /// therefore must be kept in sync with the compiler implementation. |
lib/std/compress/deflate.zig+2-4| ... | ... | @@ -662,14 +662,12 @@ test "lengths overflow" { |
| 662 | 662 | // malformed final dynamic block, tries to write 321 code lengths (MAXCODES is 316) |
| 663 | 663 | // f dy hlit hdist hclen 16 17 18 0 (18) x138 (18) x138 (18) x39 (16) x6 |
| 664 | 664 | // 1 10 11101 11101 0000 010 010 010 010 (11) 1111111 (11) 1111111 (11) 0011100 (01) 11 |
| 665 | const stream = [_]u8{ | |
| 666 | 0b11101101, 0b00011101, 0b00100100, 0b11101001, 0b11111111, 0b11111111, 0b00111001, 0b00001110 | |
| 667 | }; | |
| 665 | const stream = [_]u8{ 0b11101101, 0b00011101, 0b00100100, 0b11101001, 0b11111111, 0b11111111, 0b00111001, 0b00001110 }; | |
| 668 | 666 | |
| 669 | 667 | const reader = std.io.fixedBufferStream(&stream).reader(); |
| 670 | 668 | var window: [0x8000]u8 = undefined; |
| 671 | 669 | var inflate = inflateStream(reader, &window); |
| 672 | 670 | |
| 673 | 671 | var buf: [1]u8 = undefined; |
| 674 | std.testing.expectError(error.InvalidLength, inflate.read(&buf)); | |
| 672 | std.testing.expectError(error.InvalidLength, inflate.read(&buf)); | |
| 675 | 673 | } |
lib/std/os/bits/linux/arm64.zig+1| ... | ... | @@ -9,6 +9,7 @@ |
| 9 | 9 | const std = @import("../../../std.zig"); |
| 10 | 10 | const linux = std.os.linux; |
| 11 | 11 | const socklen_t = linux.socklen_t; |
| 12 | const sockaddr = linux.sockaddr; | |
| 12 | 13 | const iovec = linux.iovec; |
| 13 | 14 | const iovec_const = linux.iovec_const; |
| 14 | 15 | const uid_t = linux.uid_t; |
lib/std/x.zig+15-1| ... | ... | @@ -1,8 +1,22 @@ |
| 1 | // SPDX-License-Identifier: MIT | |
| 2 | // Copyright (c) 2015-2021 Zig Contributors | |
| 3 | // This file is part of [zig](https://ziglang.org/), which is MIT licensed. | |
| 4 | // The MIT license requires this copyright notice to be included in all copies | |
| 5 | // and substantial portions of the software. | |
| 6 | ||
| 7 | const std = @import("std.zig"); | |
| 8 | ||
| 1 | 9 | pub const os = struct { |
| 2 | 10 | pub const Socket = @import("x/os/Socket.zig"); |
| 3 | 11 | pub usingnamespace @import("x/os/net.zig"); |
| 4 | 12 | }; |
| 5 | 13 | |
| 6 | 14 | pub const net = struct { |
| 7 | pub const TCP = @import("x/net/TCP.zig"); | |
| 15 | pub const tcp = @import("x/net/tcp.zig"); | |
| 8 | 16 | }; |
| 17 | ||
| 18 | test { | |
| 19 | inline for (.{ os, net }) |module| { | |
| 20 | std.testing.refAllDecls(module); | |
| 21 | } | |
| 22 | } |
lib/std/x/net/TCP.zig deleted-399| ... | ... | @@ -1,399 +0,0 @@ |
| 1 | const std = @import("../../std.zig"); | |
| 2 | ||
| 3 | const os = std.os; | |
| 4 | const fmt = std.fmt; | |
| 5 | const mem = std.mem; | |
| 6 | const testing = std.testing; | |
| 7 | ||
| 8 | const IPv4 = std.x.os.IPv4; | |
| 9 | const IPv6 = std.x.os.IPv6; | |
| 10 | const Socket = std.x.os.Socket; | |
| 11 | ||
| 12 | /// A generic TCP socket abstraction. | |
| 13 | const TCP = @This(); | |
| 14 | ||
| 15 | /// A TCP client-address pair. | |
| 16 | pub const Connection = struct { | |
| 17 | client: TCP.Client, | |
| 18 | address: TCP.Address, | |
| 19 | ||
| 20 | /// Enclose a TCP client and address into a client-address pair. | |
| 21 | pub fn from(socket: Socket, address: TCP.Address) Connection { | |
| 22 | return .{ .client = TCP.Client.from(socket), .address = address }; | |
| 23 | } | |
| 24 | ||
| 25 | /// Closes the underlying client of the connection. | |
| 26 | pub fn deinit(self: TCP.Connection) void { | |
| 27 | self.client.deinit(); | |
| 28 | } | |
| 29 | }; | |
| 30 | ||
| 31 | /// Possible domains that a TCP client/listener may operate over. | |
| 32 | pub const Domain = extern enum(u16) { | |
| 33 | ip = os.AF_INET, | |
| 34 | ipv6 = os.AF_INET6, | |
| 35 | }; | |
| 36 | ||
| 37 | /// A TCP client. | |
| 38 | pub const Client = struct { | |
| 39 | socket: Socket, | |
| 40 | ||
| 41 | /// Opens a new client. | |
| 42 | pub fn init(domain: TCP.Domain, flags: u32) !Client { | |
| 43 | return Client{ | |
| 44 | .socket = try Socket.init( | |
| 45 | @enumToInt(domain), | |
| 46 | os.SOCK_STREAM | flags, | |
| 47 | os.IPPROTO_TCP, | |
| 48 | ), | |
| 49 | }; | |
| 50 | } | |
| 51 | ||
| 52 | /// Enclose a TCP client over an existing socket. | |
| 53 | pub fn from(socket: Socket) Client { | |
| 54 | return Client{ .socket = socket }; | |
| 55 | } | |
| 56 | ||
| 57 | /// Closes the client. | |
| 58 | pub fn deinit(self: Client) void { | |
| 59 | self.socket.deinit(); | |
| 60 | } | |
| 61 | ||
| 62 | /// Shutdown either the read side, write side, or all sides of the client's underlying socket. | |
| 63 | pub fn shutdown(self: Client, how: os.ShutdownHow) !void { | |
| 64 | return self.socket.shutdown(how); | |
| 65 | } | |
| 66 | ||
| 67 | /// Have the client attempt to the connect to an address. | |
| 68 | pub fn connect(self: Client, address: TCP.Address) !void { | |
| 69 | return self.socket.connect(TCP.Address, address); | |
| 70 | } | |
| 71 | ||
| 72 | /// Read data from the socket into the buffer provided. It returns the | |
| 73 | /// number of bytes read into the buffer provided. | |
| 74 | pub fn read(self: Client, buf: []u8) !usize { | |
| 75 | return self.socket.read(buf); | |
| 76 | } | |
| 77 | ||
| 78 | /// Read data from the socket into the buffer provided with a set of flags | |
| 79 | /// specified. It returns the number of bytes read into the buffer provided. | |
| 80 | pub fn recv(self: Client, buf: []u8, flags: u32) !usize { | |
| 81 | return self.socket.recv(buf, flags); | |
| 82 | } | |
| 83 | ||
| 84 | /// Write a buffer of data provided to the socket. It returns the number | |
| 85 | /// of bytes that are written to the socket. | |
| 86 | pub fn write(self: Client, buf: []const u8) !usize { | |
| 87 | return self.socket.write(buf); | |
| 88 | } | |
| 89 | ||
| 90 | /// Writes multiple I/O vectors to the socket. It returns the number | |
| 91 | /// of bytes that are written to the socket. | |
| 92 | pub fn writev(self: Client, buffers: []const os.iovec_const) !usize { | |
| 93 | return self.socket.writev(buffers); | |
| 94 | } | |
| 95 | ||
| 96 | /// Write a buffer of data provided to the socket with a set of flags specified. | |
| 97 | /// It returns the number of bytes that are written to the socket. | |
| 98 | pub fn send(self: Client, buf: []const u8, flags: u32) !usize { | |
| 99 | return self.socket.send(buf, flags); | |
| 100 | } | |
| 101 | ||
| 102 | /// Writes multiple I/O vectors with a prepended message header to the socket | |
| 103 | /// with a set of flags specified. It returns the number of bytes that are | |
| 104 | /// written to the socket. | |
| 105 | pub fn sendmsg(self: Client, msg: os.msghdr_const, flags: u32) !usize { | |
| 106 | return self.socket.sendmsg(msg, flags); | |
| 107 | } | |
| 108 | ||
| 109 | /// Query and return the latest cached error on the client's underlying socket. | |
| 110 | pub fn getError(self: Client) !void { | |
| 111 | return self.socket.getError(); | |
| 112 | } | |
| 113 | ||
| 114 | /// Query the read buffer size of the client's underlying socket. | |
| 115 | pub fn getReadBufferSize(self: Client) !u32 { | |
| 116 | return self.socket.getReadBufferSize(); | |
| 117 | } | |
| 118 | ||
| 119 | /// Query the write buffer size of the client's underlying socket. | |
| 120 | pub fn getWriteBufferSize(self: Client) !u32 { | |
| 121 | return self.socket.getWriteBufferSize(); | |
| 122 | } | |
| 123 | ||
| 124 | /// Query the address that the client's socket is locally bounded to. | |
| 125 | pub fn getLocalAddress(self: Client) !TCP.Address { | |
| 126 | return self.socket.getLocalAddress(TCP.Address); | |
| 127 | } | |
| 128 | ||
| 129 | /// Disable Nagle's algorithm on a TCP socket. It returns `error.UnsupportedSocketOption` if | |
| 130 | /// the host does not support sockets disabling Nagle's algorithm. | |
| 131 | pub fn setNoDelay(self: Client, enabled: bool) !void { | |
| 132 | if (comptime @hasDecl(os, "TCP_NODELAY")) { | |
| 133 | const bytes = mem.asBytes(&@as(usize, @boolToInt(enabled))); | |
| 134 | return os.setsockopt(self.socket.fd, os.IPPROTO_TCP, os.TCP_NODELAY, bytes); | |
| 135 | } | |
| 136 | return error.UnsupportedSocketOption; | |
| 137 | } | |
| 138 | ||
| 139 | /// Set the write buffer size of the socket. | |
| 140 | pub fn setWriteBufferSize(self: Client, size: u32) !void { | |
| 141 | return self.socket.setWriteBufferSize(size); | |
| 142 | } | |
| 143 | ||
| 144 | /// Set the read buffer size of the socket. | |
| 145 | pub fn setReadBufferSize(self: Client, size: u32) !void { | |
| 146 | return self.socket.setReadBufferSize(size); | |
| 147 | } | |
| 148 | ||
| 149 | /// Set a timeout on the socket that is to occur if no messages are successfully written | |
| 150 | /// to its bound destination after a specified number of milliseconds. A subsequent write | |
| 151 | /// to the socket will thereafter return `error.WouldBlock` should the timeout be exceeded. | |
| 152 | pub fn setWriteTimeout(self: Client, milliseconds: usize) !void { | |
| 153 | return self.socket.setWriteTimeout(milliseconds); | |
| 154 | } | |
| 155 | ||
| 156 | /// Set a timeout on the socket that is to occur if no messages are successfully read | |
| 157 | /// from its bound destination after a specified number of milliseconds. A subsequent | |
| 158 | /// read from the socket will thereafter return `error.WouldBlock` should the timeout be | |
| 159 | /// exceeded. | |
| 160 | pub fn setReadTimeout(self: Client, milliseconds: usize) !void { | |
| 161 | return self.socket.setReadTimeout(milliseconds); | |
| 162 | } | |
| 163 | }; | |
| 164 | ||
| 165 | /// A TCP listener. | |
| 166 | pub const Listener = struct { | |
| 167 | socket: Socket, | |
| 168 | ||
| 169 | /// Opens a new listener. | |
| 170 | pub fn init(domain: TCP.Domain, flags: u32) !Listener { | |
| 171 | return Listener{ | |
| 172 | .socket = try Socket.init( | |
| 173 | @enumToInt(domain), | |
| 174 | os.SOCK_STREAM | flags, | |
| 175 | os.IPPROTO_TCP, | |
| 176 | ), | |
| 177 | }; | |
| 178 | } | |
| 179 | ||
| 180 | /// Closes the listener. | |
| 181 | pub fn deinit(self: Listener) void { | |
| 182 | self.socket.deinit(); | |
| 183 | } | |
| 184 | ||
| 185 | /// Shuts down the underlying listener's socket. The next subsequent call, or | |
| 186 | /// a current pending call to accept() after shutdown is called will return | |
| 187 | /// an error. | |
| 188 | pub fn shutdown(self: Listener) !void { | |
| 189 | return self.socket.shutdown(.recv); | |
| 190 | } | |
| 191 | ||
| 192 | /// Binds the listener's socket to an address. | |
| 193 | pub fn bind(self: Listener, address: TCP.Address) !void { | |
| 194 | return self.socket.bind(TCP.Address, address); | |
| 195 | } | |
| 196 | ||
| 197 | /// Start listening for incoming connections. | |
| 198 | pub fn listen(self: Listener, max_backlog_size: u31) !void { | |
| 199 | return self.socket.listen(max_backlog_size); | |
| 200 | } | |
| 201 | ||
| 202 | /// Accept a pending incoming connection queued to the kernel backlog | |
| 203 | /// of the listener's socket. | |
| 204 | pub fn accept(self: Listener, flags: u32) !TCP.Connection { | |
| 205 | return self.socket.accept(TCP.Connection, TCP.Address, flags); | |
| 206 | } | |
| 207 | ||
| 208 | /// Query and return the latest cached error on the listener's underlying socket. | |
| 209 | pub fn getError(self: Client) !void { | |
| 210 | return self.socket.getError(); | |
| 211 | } | |
| 212 | ||
| 213 | /// Query the address that the listener's socket is locally bounded to. | |
| 214 | pub fn getLocalAddress(self: Listener) !TCP.Address { | |
| 215 | return self.socket.getLocalAddress(TCP.Address); | |
| 216 | } | |
| 217 | ||
| 218 | /// Allow multiple sockets on the same host to listen on the same address. It returns `error.UnsupportedSocketOption` if | |
| 219 | /// the host does not support sockets listening the same address. | |
| 220 | pub fn setReuseAddress(self: Listener, enabled: bool) !void { | |
| 221 | return self.socket.setReuseAddress(enabled); | |
| 222 | } | |
| 223 | ||
| 224 | /// Allow multiple sockets on the same host to listen on the same port. It returns `error.UnsupportedSocketOption` if | |
| 225 | /// the host does not supports sockets listening on the same port. | |
| 226 | pub fn setReusePort(self: Listener, enabled: bool) !void { | |
| 227 | return self.socket.setReusePort(enabled); | |
| 228 | } | |
| 229 | ||
| 230 | /// Enables TCP Fast Open (RFC 7413) on a TCP socket. It returns `error.UnsupportedSocketOption` if the host does not | |
| 231 | /// support TCP Fast Open. | |
| 232 | pub fn setFastOpen(self: Listener, enabled: bool) !void { | |
| 233 | if (comptime @hasDecl(os, "TCP_FASTOPEN")) { | |
| 234 | return os.setsockopt(self.socket.fd, os.IPPROTO_TCP, os.TCP_FASTOPEN, mem.asBytes(&@as(usize, @boolToInt(enabled)))); | |
| 235 | } | |
| 236 | return error.UnsupportedSocketOption; | |
| 237 | } | |
| 238 | ||
| 239 | /// Enables TCP Quick ACK on a TCP socket to immediately send rather than delay ACKs when necessary. It returns | |
| 240 | /// `error.UnsupportedSocketOption` if the host does not support TCP Quick ACK. | |
| 241 | pub fn setQuickACK(self: Listener, enabled: bool) !void { | |
| 242 | if (comptime @hasDecl(os, "TCP_QUICKACK")) { | |
| 243 | return os.setsockopt(self.socket.fd, os.IPPROTO_TCP, os.TCP_QUICKACK, mem.asBytes(&@as(usize, @boolToInt(enabled)))); | |
| 244 | } | |
| 245 | return error.UnsupportedSocketOption; | |
| 246 | } | |
| 247 | ||
| 248 | /// Set a timeout on the listener that is to occur if no new incoming connections come in | |
| 249 | /// after a specified number of milliseconds. A subsequent accept call to the listener | |
| 250 | /// will thereafter return `error.WouldBlock` should the timeout be exceeded. | |
| 251 | pub fn setAcceptTimeout(self: Listener, milliseconds: usize) !void { | |
| 252 | return self.socket.setReadTimeout(milliseconds); | |
| 253 | } | |
| 254 | }; | |
| 255 | ||
| 256 | /// A TCP socket address designated by a host IP and port. A TCP socket | |
| 257 | /// address comprises of 28 bytes. It may freely be used in place of | |
| 258 | /// `sockaddr` when working with socket syscalls. | |
| 259 | /// | |
| 260 | /// It is not recommended to touch the fields of an `Address`, but to | |
| 261 | /// instead make use of its available accessor methods. | |
| 262 | pub const Address = extern struct { | |
| 263 | family: u16, | |
| 264 | port: u16, | |
| 265 | host: extern union { | |
| 266 | ipv4: extern struct { | |
| 267 | address: IPv4, | |
| 268 | }, | |
| 269 | ipv6: extern struct { | |
| 270 | flow_info: u32 = 0, | |
| 271 | address: IPv6, | |
| 272 | }, | |
| 273 | }, | |
| 274 | ||
| 275 | /// Instantiate a new TCP address with a IPv4 host and port. | |
| 276 | pub fn initIPv4(host: IPv4, port: u16) Address { | |
| 277 | return Address{ | |
| 278 | .family = os.AF_INET, | |
| 279 | .port = mem.nativeToBig(u16, port), | |
| 280 | .host = .{ | |
| 281 | .ipv4 = .{ | |
| 282 | .address = host, | |
| 283 | }, | |
| 284 | }, | |
| 285 | }; | |
| 286 | } | |
| 287 | ||
| 288 | /// Instantiate a new TCP address with a IPv6 host and port. | |
| 289 | pub fn initIPv6(host: IPv6, port: u16) Address { | |
| 290 | return Address{ | |
| 291 | .family = os.AF_INET6, | |
| 292 | .port = mem.nativeToBig(u16, port), | |
| 293 | .host = .{ | |
| 294 | .ipv6 = .{ | |
| 295 | .address = host, | |
| 296 | }, | |
| 297 | }, | |
| 298 | }; | |
| 299 | } | |
| 300 | ||
| 301 | /// Extract the host of the address. | |
| 302 | pub fn getHost(self: Address) union(enum) { v4: IPv4, v6: IPv6 } { | |
| 303 | return switch (self.family) { | |
| 304 | os.AF_INET => .{ .v4 = self.host.ipv4.address }, | |
| 305 | os.AF_INET6 => .{ .v6 = self.host.ipv6.address }, | |
| 306 | else => unreachable, | |
| 307 | }; | |
| 308 | } | |
| 309 | ||
| 310 | /// Extract the port of the address. | |
| 311 | pub fn getPort(self: Address) u16 { | |
| 312 | return mem.nativeToBig(u16, self.port); | |
| 313 | } | |
| 314 | ||
| 315 | /// Set the port of the address. | |
| 316 | pub fn setPort(self: *Address, port: u16) void { | |
| 317 | self.port = mem.nativeToBig(u16, port); | |
| 318 | } | |
| 319 | ||
| 320 | /// Implements the `std.fmt.format` API. | |
| 321 | pub fn format( | |
| 322 | self: Address, | |
| 323 | comptime layout: []const u8, | |
| 324 | opts: fmt.FormatOptions, | |
| 325 | writer: anytype, | |
| 326 | ) !void { | |
| 327 | switch (self.getHost()) { | |
| 328 | .v4 => |host| try fmt.format(writer, "{}:{}", .{ host, self.getPort() }), | |
| 329 | .v6 => |host| try fmt.format(writer, "{}:{}", .{ host, self.getPort() }), | |
| 330 | } | |
| 331 | } | |
| 332 | }; | |
| 333 | ||
| 334 | test { | |
| 335 | testing.refAllDecls(@This()); | |
| 336 | } | |
| 337 | ||
| 338 | test "tcp: create non-blocking pair" { | |
| 339 | const a = try TCP.Listener.init(.ip, os.SOCK_NONBLOCK | os.SOCK_CLOEXEC); | |
| 340 | defer a.deinit(); | |
| 341 | ||
| 342 | try a.bind(TCP.Address.initIPv4(IPv4.unspecified, 0)); | |
| 343 | try a.listen(128); | |
| 344 | ||
| 345 | const binded_address = try a.getLocalAddress(); | |
| 346 | ||
| 347 | const b = try TCP.Client.init(.ip, os.SOCK_NONBLOCK | os.SOCK_CLOEXEC); | |
| 348 | defer b.deinit(); | |
| 349 | ||
| 350 | testing.expectError(error.WouldBlock, b.connect(binded_address)); | |
| 351 | try b.getError(); | |
| 352 | ||
| 353 | const ab = try a.accept(os.SOCK_NONBLOCK | os.SOCK_CLOEXEC); | |
| 354 | defer ab.deinit(); | |
| 355 | } | |
| 356 | ||
| 357 | test "tcp/client: set read timeout of 1 millisecond on blocking client" { | |
| 358 | const a = try TCP.Listener.init(.ip, os.SOCK_CLOEXEC); | |
| 359 | defer a.deinit(); | |
| 360 | ||
| 361 | try a.bind(TCP.Address.initIPv4(IPv4.unspecified, 0)); | |
| 362 | try a.listen(128); | |
| 363 | ||
| 364 | const binded_address = try a.getLocalAddress(); | |
| 365 | ||
| 366 | const b = try TCP.Client.init(.ip, os.SOCK_CLOEXEC); | |
| 367 | defer b.deinit(); | |
| 368 | ||
| 369 | try b.connect(binded_address); | |
| 370 | try b.setReadTimeout(1); | |
| 371 | ||
| 372 | const ab = try a.accept(os.SOCK_CLOEXEC); | |
| 373 | defer ab.deinit(); | |
| 374 | ||
| 375 | var buf: [1]u8 = undefined; | |
| 376 | testing.expectError(error.WouldBlock, b.read(&buf)); | |
| 377 | } | |
| 378 | ||
| 379 | test "tcp/listener: bind to unspecified ipv4 address" { | |
| 380 | const socket = try TCP.Listener.init(.ip, os.SOCK_CLOEXEC); | |
| 381 | defer socket.deinit(); | |
| 382 | ||
| 383 | try socket.bind(TCP.Address.initIPv4(IPv4.unspecified, 0)); | |
| 384 | try socket.listen(128); | |
| 385 | ||
| 386 | const address = try socket.getLocalAddress(); | |
| 387 | testing.expect(address.getHost() == .v4); | |
| 388 | } | |
| 389 | ||
| 390 | test "tcp/listener: bind to unspecified ipv6 address" { | |
| 391 | const socket = try TCP.Listener.init(.ipv6, os.SOCK_CLOEXEC); | |
| 392 | defer socket.deinit(); | |
| 393 | ||
| 394 | try socket.bind(TCP.Address.initIPv6(IPv6.unspecified, 0)); | |
| 395 | try socket.listen(128); | |
| 396 | ||
| 397 | const address = try socket.getLocalAddress(); | |
| 398 | testing.expect(address.getHost() == .v6); | |
| 399 | } |
lib/std/x/net/tcp.zig created+383| ... | ... | @@ -0,0 +1,383 @@ |
| 1 | // SPDX-License-Identifier: MIT | |
| 2 | // Copyright (c) 2015-2021 Zig Contributors | |
| 3 | // This file is part of [zig](https://ziglang.org/), which is MIT licensed. | |
| 4 | // The MIT license requires this copyright notice to be included in all copies | |
| 5 | // and substantial portions of the software. | |
| 6 | ||
| 7 | const std = @import("../../std.zig"); | |
| 8 | ||
| 9 | const os = std.os; | |
| 10 | const fmt = std.fmt; | |
| 11 | const mem = std.mem; | |
| 12 | const testing = std.testing; | |
| 13 | ||
| 14 | const IPv4 = std.x.os.IPv4; | |
| 15 | const IPv6 = std.x.os.IPv6; | |
| 16 | const Socket = std.x.os.Socket; | |
| 17 | ||
| 18 | /// A generic TCP socket abstraction. | |
| 19 | const tcp = @This(); | |
| 20 | ||
| 21 | /// A union of all eligible types of socket addresses over TCP. | |
| 22 | pub const Address = union(enum) { | |
| 23 | ipv4: IPv4.Address, | |
| 24 | ipv6: IPv6.Address, | |
| 25 | ||
| 26 | /// Instantiate a new address with a IPv4 host and port. | |
| 27 | pub fn initIPv4(host: IPv4, port: u16) Address { | |
| 28 | return .{ .ipv4 = .{ .host = host, .port = port } }; | |
| 29 | } | |
| 30 | ||
| 31 | /// Instantiate a new address with a IPv6 host and port. | |
| 32 | pub fn initIPv6(host: IPv6, port: u16) Address { | |
| 33 | return .{ .ipv6 = .{ .host = host, .port = port } }; | |
| 34 | } | |
| 35 | ||
| 36 | /// Re-interpret a generic socket address into a TCP socket address. | |
| 37 | pub fn from(address: Socket.Address) tcp.Address { | |
| 38 | return switch (address) { | |
| 39 | .ipv4 => |ipv4_address| .{ .ipv4 = ipv4_address }, | |
| 40 | .ipv6 => |ipv6_address| .{ .ipv6 = ipv6_address }, | |
| 41 | }; | |
| 42 | } | |
| 43 | ||
| 44 | /// Re-interpret a TCP socket address into a generic socket address. | |
| 45 | pub fn into(self: tcp.Address) Socket.Address { | |
| 46 | return switch (self) { | |
| 47 | .ipv4 => |ipv4_address| .{ .ipv4 = ipv4_address }, | |
| 48 | .ipv6 => |ipv6_address| .{ .ipv6 = ipv6_address }, | |
| 49 | }; | |
| 50 | } | |
| 51 | ||
| 52 | /// Implements the `std.fmt.format` API. | |
| 53 | pub fn format( | |
| 54 | self: tcp.Address, | |
| 55 | comptime layout: []const u8, | |
| 56 | opts: fmt.FormatOptions, | |
| 57 | writer: anytype, | |
| 58 | ) !void { | |
| 59 | switch (self) { | |
| 60 | .ipv4 => |address| try fmt.format(writer, "{}:{}", .{ address.host, address.port }), | |
| 61 | .ipv6 => |address| try fmt.format(writer, "{}:{}", .{ address.host, address.port }), | |
| 62 | } | |
| 63 | } | |
| 64 | }; | |
| 65 | ||
| 66 | /// A TCP client-address pair. | |
| 67 | pub const Connection = struct { | |
| 68 | client: tcp.Client, | |
| 69 | address: tcp.Address, | |
| 70 | ||
| 71 | /// Enclose a TCP client and address into a client-address pair. | |
| 72 | pub fn from(conn: Socket.Connection) tcp.Connection { | |
| 73 | return .{ | |
| 74 | .client = tcp.Client.from(conn.socket), | |
| 75 | .address = tcp.Address.from(conn.address), | |
| 76 | }; | |
| 77 | } | |
| 78 | ||
| 79 | /// Unravel a TCP client-address pair into a socket-address pair. | |
| 80 | pub fn into(self: tcp.Connection) Socket.Connection { | |
| 81 | return .{ | |
| 82 | .socket = self.client.socket, | |
| 83 | .address = self.address.into(), | |
| 84 | }; | |
| 85 | } | |
| 86 | ||
| 87 | /// Closes the underlying client of the connection. | |
| 88 | pub fn deinit(self: tcp.Connection) void { | |
| 89 | self.client.deinit(); | |
| 90 | } | |
| 91 | }; | |
| 92 | ||
| 93 | /// Possible domains that a TCP client/listener may operate over. | |
| 94 | pub const Domain = extern enum(u16) { | |
| 95 | ip = os.AF_INET, | |
| 96 | ipv6 = os.AF_INET6, | |
| 97 | }; | |
| 98 | ||
| 99 | /// A TCP client. | |
| 100 | pub const Client = struct { | |
| 101 | socket: Socket, | |
| 102 | ||
| 103 | /// Opens a new client. | |
| 104 | pub fn init(domain: tcp.Domain, flags: u32) !Client { | |
| 105 | return Client{ | |
| 106 | .socket = try Socket.init( | |
| 107 | @enumToInt(domain), | |
| 108 | os.SOCK_STREAM | flags, | |
| 109 | os.IPPROTO_TCP, | |
| 110 | ), | |
| 111 | }; | |
| 112 | } | |
| 113 | ||
| 114 | /// Enclose a TCP client over an existing socket. | |
| 115 | pub fn from(socket: Socket) Client { | |
| 116 | return Client{ .socket = socket }; | |
| 117 | } | |
| 118 | ||
| 119 | /// Closes the client. | |
| 120 | pub fn deinit(self: Client) void { | |
| 121 | self.socket.deinit(); | |
| 122 | } | |
| 123 | ||
| 124 | /// Shutdown either the read side, write side, or all sides of the client's underlying socket. | |
| 125 | pub fn shutdown(self: Client, how: os.ShutdownHow) !void { | |
| 126 | return self.socket.shutdown(how); | |
| 127 | } | |
| 128 | ||
| 129 | /// Have the client attempt to the connect to an address. | |
| 130 | pub fn connect(self: Client, address: tcp.Address) !void { | |
| 131 | return self.socket.connect(address.into()); | |
| 132 | } | |
| 133 | ||
| 134 | /// Read data from the socket into the buffer provided. It returns the | |
| 135 | /// number of bytes read into the buffer provided. | |
| 136 | pub fn read(self: Client, buf: []u8) !usize { | |
| 137 | return self.socket.read(buf); | |
| 138 | } | |
| 139 | ||
| 140 | /// Read data from the socket into the buffer provided with a set of flags | |
| 141 | /// specified. It returns the number of bytes read into the buffer provided. | |
| 142 | pub fn recv(self: Client, buf: []u8, flags: u32) !usize { | |
| 143 | return self.socket.recv(buf, flags); | |
| 144 | } | |
| 145 | ||
| 146 | /// Write a buffer of data provided to the socket. It returns the number | |
| 147 | /// of bytes that are written to the socket. | |
| 148 | pub fn write(self: Client, buf: []const u8) !usize { | |
| 149 | return self.socket.write(buf); | |
| 150 | } | |
| 151 | ||
| 152 | /// Writes multiple I/O vectors to the socket. It returns the number | |
| 153 | /// of bytes that are written to the socket. | |
| 154 | pub fn writev(self: Client, buffers: []const os.iovec_const) !usize { | |
| 155 | return self.socket.writev(buffers); | |
| 156 | } | |
| 157 | ||
| 158 | /// Write a buffer of data provided to the socket with a set of flags specified. | |
| 159 | /// It returns the number of bytes that are written to the socket. | |
| 160 | pub fn send(self: Client, buf: []const u8, flags: u32) !usize { | |
| 161 | return self.socket.send(buf, flags); | |
| 162 | } | |
| 163 | ||
| 164 | /// Writes multiple I/O vectors with a prepended message header to the socket | |
| 165 | /// with a set of flags specified. It returns the number of bytes that are | |
| 166 | /// written to the socket. | |
| 167 | pub fn sendmsg(self: Client, msg: os.msghdr_const, flags: u32) !usize { | |
| 168 | return self.socket.sendmsg(msg, flags); | |
| 169 | } | |
| 170 | ||
| 171 | /// Query and return the latest cached error on the client's underlying socket. | |
| 172 | pub fn getError(self: Client) !void { | |
| 173 | return self.socket.getError(); | |
| 174 | } | |
| 175 | ||
| 176 | /// Query the read buffer size of the client's underlying socket. | |
| 177 | pub fn getReadBufferSize(self: Client) !u32 { | |
| 178 | return self.socket.getReadBufferSize(); | |
| 179 | } | |
| 180 | ||
| 181 | /// Query the write buffer size of the client's underlying socket. | |
| 182 | pub fn getWriteBufferSize(self: Client) !u32 { | |
| 183 | return self.socket.getWriteBufferSize(); | |
| 184 | } | |
| 185 | ||
| 186 | /// Query the address that the client's socket is locally bounded to. | |
| 187 | pub fn getLocalAddress(self: Client) !tcp.Address { | |
| 188 | return tcp.Address.from(try self.socket.getLocalAddress()); | |
| 189 | } | |
| 190 | ||
| 191 | /// Disable Nagle's algorithm on a TCP socket. It returns `error.UnsupportedSocketOption` if | |
| 192 | /// the host does not support sockets disabling Nagle's algorithm. | |
| 193 | pub fn setNoDelay(self: Client, enabled: bool) !void { | |
| 194 | if (comptime @hasDecl(os, "TCP_NODELAY")) { | |
| 195 | const bytes = mem.asBytes(&@as(usize, @boolToInt(enabled))); | |
| 196 | return os.setsockopt(self.socket.fd, os.IPPROTO_TCP, os.TCP_NODELAY, bytes); | |
| 197 | } | |
| 198 | return error.UnsupportedSocketOption; | |
| 199 | } | |
| 200 | ||
| 201 | /// Set the write buffer size of the socket. | |
| 202 | pub fn setWriteBufferSize(self: Client, size: u32) !void { | |
| 203 | return self.socket.setWriteBufferSize(size); | |
| 204 | } | |
| 205 | ||
| 206 | /// Set the read buffer size of the socket. | |
| 207 | pub fn setReadBufferSize(self: Client, size: u32) !void { | |
| 208 | return self.socket.setReadBufferSize(size); | |
| 209 | } | |
| 210 | ||
| 211 | /// Set a timeout on the socket that is to occur if no messages are successfully written | |
| 212 | /// to its bound destination after a specified number of milliseconds. A subsequent write | |
| 213 | /// to the socket will thereafter return `error.WouldBlock` should the timeout be exceeded. | |
| 214 | pub fn setWriteTimeout(self: Client, milliseconds: usize) !void { | |
| 215 | return self.socket.setWriteTimeout(milliseconds); | |
| 216 | } | |
| 217 | ||
| 218 | /// Set a timeout on the socket that is to occur if no messages are successfully read | |
| 219 | /// from its bound destination after a specified number of milliseconds. A subsequent | |
| 220 | /// read from the socket will thereafter return `error.WouldBlock` should the timeout be | |
| 221 | /// exceeded. | |
| 222 | pub fn setReadTimeout(self: Client, milliseconds: usize) !void { | |
| 223 | return self.socket.setReadTimeout(milliseconds); | |
| 224 | } | |
| 225 | }; | |
| 226 | ||
| 227 | /// A TCP listener. | |
| 228 | pub const Listener = struct { | |
| 229 | socket: Socket, | |
| 230 | ||
| 231 | /// Opens a new listener. | |
| 232 | pub fn init(domain: tcp.Domain, flags: u32) !Listener { | |
| 233 | return Listener{ | |
| 234 | .socket = try Socket.init( | |
| 235 | @enumToInt(domain), | |
| 236 | os.SOCK_STREAM | flags, | |
| 237 | os.IPPROTO_TCP, | |
| 238 | ), | |
| 239 | }; | |
| 240 | } | |
| 241 | ||
| 242 | /// Closes the listener. | |
| 243 | pub fn deinit(self: Listener) void { | |
| 244 | self.socket.deinit(); | |
| 245 | } | |
| 246 | ||
| 247 | /// Shuts down the underlying listener's socket. The next subsequent call, or | |
| 248 | /// a current pending call to accept() after shutdown is called will return | |
| 249 | /// an error. | |
| 250 | pub fn shutdown(self: Listener) !void { | |
| 251 | return self.socket.shutdown(.recv); | |
| 252 | } | |
| 253 | ||
| 254 | /// Binds the listener's socket to an address. | |
| 255 | pub fn bind(self: Listener, address: tcp.Address) !void { | |
| 256 | return self.socket.bind(address.into()); | |
| 257 | } | |
| 258 | ||
| 259 | /// Start listening for incoming connections. | |
| 260 | pub fn listen(self: Listener, max_backlog_size: u31) !void { | |
| 261 | return self.socket.listen(max_backlog_size); | |
| 262 | } | |
| 263 | ||
| 264 | /// Accept a pending incoming connection queued to the kernel backlog | |
| 265 | /// of the listener's socket. | |
| 266 | pub fn accept(self: Listener, flags: u32) !tcp.Connection { | |
| 267 | return tcp.Connection.from(try self.socket.accept(flags)); | |
| 268 | } | |
| 269 | ||
| 270 | /// Query and return the latest cached error on the listener's underlying socket. | |
| 271 | pub fn getError(self: Client) !void { | |
| 272 | return self.socket.getError(); | |
| 273 | } | |
| 274 | ||
| 275 | /// Query the address that the listener's socket is locally bounded to. | |
| 276 | pub fn getLocalAddress(self: Listener) !tcp.Address { | |
| 277 | return tcp.Address.from(try self.socket.getLocalAddress()); | |
| 278 | } | |
| 279 | ||
| 280 | /// Allow multiple sockets on the same host to listen on the same address. It returns `error.UnsupportedSocketOption` if | |
| 281 | /// the host does not support sockets listening the same address. | |
| 282 | pub fn setReuseAddress(self: Listener, enabled: bool) !void { | |
| 283 | return self.socket.setReuseAddress(enabled); | |
| 284 | } | |
| 285 | ||
| 286 | /// Allow multiple sockets on the same host to listen on the same port. It returns `error.UnsupportedSocketOption` if | |
| 287 | /// the host does not supports sockets listening on the same port. | |
| 288 | pub fn setReusePort(self: Listener, enabled: bool) !void { | |
| 289 | return self.socket.setReusePort(enabled); | |
| 290 | } | |
| 291 | ||
| 292 | /// Enables TCP Fast Open (RFC 7413) on a TCP socket. It returns `error.UnsupportedSocketOption` if the host does not | |
| 293 | /// support TCP Fast Open. | |
| 294 | pub fn setFastOpen(self: Listener, enabled: bool) !void { | |
| 295 | if (comptime @hasDecl(os, "TCP_FASTOPEN")) { | |
| 296 | return os.setsockopt(self.socket.fd, os.IPPROTO_TCP, os.TCP_FASTOPEN, mem.asBytes(&@as(usize, @boolToInt(enabled)))); | |
| 297 | } | |
| 298 | return error.UnsupportedSocketOption; | |
| 299 | } | |
| 300 | ||
| 301 | /// Enables TCP Quick ACK on a TCP socket to immediately send rather than delay ACKs when necessary. It returns | |
| 302 | /// `error.UnsupportedSocketOption` if the host does not support TCP Quick ACK. | |
| 303 | pub fn setQuickACK(self: Listener, enabled: bool) !void { | |
| 304 | if (comptime @hasDecl(os, "TCP_QUICKACK")) { | |
| 305 | return os.setsockopt(self.socket.fd, os.IPPROTO_TCP, os.TCP_QUICKACK, mem.asBytes(&@as(usize, @boolToInt(enabled)))); | |
| 306 | } | |
| 307 | return error.UnsupportedSocketOption; | |
| 308 | } | |
| 309 | ||
| 310 | /// Set a timeout on the listener that is to occur if no new incoming connections come in | |
| 311 | /// after a specified number of milliseconds. A subsequent accept call to the listener | |
| 312 | /// will thereafter return `error.WouldBlock` should the timeout be exceeded. | |
| 313 | pub fn setAcceptTimeout(self: Listener, milliseconds: usize) !void { | |
| 314 | return self.socket.setReadTimeout(milliseconds); | |
| 315 | } | |
| 316 | }; | |
| 317 | ||
| 318 | test { | |
| 319 | testing.refAllDecls(@This()); | |
| 320 | } | |
| 321 | ||
| 322 | test "tcp: create non-blocking pair" { | |
| 323 | const listener = try tcp.Listener.init(.ip, os.SOCK_NONBLOCK | os.SOCK_CLOEXEC); | |
| 324 | defer listener.deinit(); | |
| 325 | ||
| 326 | try listener.bind(tcp.Address.initIPv4(IPv4.unspecified, 0)); | |
| 327 | try listener.listen(128); | |
| 328 | ||
| 329 | const binded_address = try listener.getLocalAddress(); | |
| 330 | ||
| 331 | const client = try tcp.Client.init(.ip, os.SOCK_NONBLOCK | os.SOCK_CLOEXEC); | |
| 332 | defer client.deinit(); | |
| 333 | ||
| 334 | testing.expectError(error.WouldBlock, client.connect(binded_address)); | |
| 335 | try client.getError(); | |
| 336 | ||
| 337 | const conn = try listener.accept(os.SOCK_NONBLOCK | os.SOCK_CLOEXEC); | |
| 338 | defer conn.deinit(); | |
| 339 | } | |
| 340 | ||
| 341 | test "tcp/client: set read timeout of 1 millisecond on blocking client" { | |
| 342 | const listener = try tcp.Listener.init(.ip, os.SOCK_CLOEXEC); | |
| 343 | defer listener.deinit(); | |
| 344 | ||
| 345 | try listener.bind(tcp.Address.initIPv4(IPv4.unspecified, 0)); | |
| 346 | try listener.listen(128); | |
| 347 | ||
| 348 | const binded_address = try listener.getLocalAddress(); | |
| 349 | ||
| 350 | const client = try tcp.Client.init(.ip, os.SOCK_CLOEXEC); | |
| 351 | defer client.deinit(); | |
| 352 | ||
| 353 | try client.connect(binded_address); | |
| 354 | try client.setReadTimeout(1); | |
| 355 | ||
| 356 | const conn = try listener.accept(os.SOCK_CLOEXEC); | |
| 357 | defer conn.deinit(); | |
| 358 | ||
| 359 | var buf: [1]u8 = undefined; | |
| 360 | testing.expectError(error.WouldBlock, client.read(&buf)); | |
| 361 | } | |
| 362 | ||
| 363 | test "tcp/listener: bind to unspecified ipv4 address" { | |
| 364 | const listener = try tcp.Listener.init(.ip, os.SOCK_CLOEXEC); | |
| 365 | defer listener.deinit(); | |
| 366 | ||
| 367 | try listener.bind(tcp.Address.initIPv4(IPv4.unspecified, 0)); | |
| 368 | try listener.listen(128); | |
| 369 | ||
| 370 | const address = try listener.getLocalAddress(); | |
| 371 | testing.expect(address == .ipv4); | |
| 372 | } | |
| 373 | ||
| 374 | test "tcp/listener: bind to unspecified ipv6 address" { | |
| 375 | const listener = try tcp.Listener.init(.ipv6, os.SOCK_CLOEXEC); | |
| 376 | defer listener.deinit(); | |
| 377 | ||
| 378 | try listener.bind(tcp.Address.initIPv6(IPv6.unspecified, 0)); | |
| 379 | try listener.listen(128); | |
| 380 | ||
| 381 | const address = try listener.getLocalAddress(); | |
| 382 | testing.expect(address == .ipv6); | |
| 383 | } |
lib/std/x/os/Socket.zig+114-14| ... | ... | @@ -1,4 +1,11 @@ |
| 1 | // SPDX-License-Identifier: MIT | |
| 2 | // Copyright (c) 2015-2021 Zig Contributors | |
| 3 | // This file is part of [zig](https://ziglang.org/), which is MIT licensed. | |
| 4 | // The MIT license requires this copyright notice to be included in all copies | |
| 5 | // and substantial portions of the software. | |
| 6 | ||
| 1 | 7 | const std = @import("../../std.zig"); |
| 8 | const net = @import("net.zig"); | |
| 2 | 9 | |
| 3 | 10 | const os = std.os; |
| 4 | 11 | const mem = std.mem; |
| ... | ... | @@ -7,6 +14,98 @@ const time = std.time; |
| 7 | 14 | /// A generic socket abstraction. |
| 8 | 15 | const Socket = @This(); |
| 9 | 16 | |
| 17 | /// A socket-address pair. | |
| 18 | pub const Connection = struct { | |
| 19 | socket: Socket, | |
| 20 | address: Socket.Address, | |
| 21 | ||
| 22 | /// Enclose a socket and address into a socket-address pair. | |
| 23 | pub fn from(socket: Socket, address: Socket.Address) Socket.Connection { | |
| 24 | return .{ .socket = socket, .address = address }; | |
| 25 | } | |
| 26 | }; | |
| 27 | ||
| 28 | /// A generic socket address abstraction. It is safe to directly access and modify | |
| 29 | /// the fields of a `Socket.Address`. | |
| 30 | pub const Address = union(enum) { | |
| 31 | ipv4: net.IPv4.Address, | |
| 32 | ipv6: net.IPv6.Address, | |
| 33 | ||
| 34 | /// Instantiate a new address with a IPv4 host and port. | |
| 35 | pub fn initIPv4(host: net.IPv4, port: u16) Socket.Address { | |
| 36 | return .{ .ipv4 = .{ .host = host, .port = port } }; | |
| 37 | } | |
| 38 | ||
| 39 | /// Instantiate a new address with a IPv6 host and port. | |
| 40 | pub fn initIPv6(host: net.IPv6, port: u16) Socket.Address { | |
| 41 | return .{ .ipv6 = .{ .host = host, .port = port } }; | |
| 42 | } | |
| 43 | ||
| 44 | /// Parses a `sockaddr` into a generic socket address. | |
| 45 | pub fn fromNative(address: *align(4) const os.sockaddr) Socket.Address { | |
| 46 | switch (address.family) { | |
| 47 | os.AF_INET => { | |
| 48 | const info = @ptrCast(*const os.sockaddr_in, address); | |
| 49 | const host = net.IPv4{ .octets = @bitCast([4]u8, info.addr) }; | |
| 50 | const port = mem.bigToNative(u16, info.port); | |
| 51 | return Socket.Address.initIPv4(host, port); | |
| 52 | }, | |
| 53 | os.AF_INET6 => { | |
| 54 | const info = @ptrCast(*const os.sockaddr_in6, address); | |
| 55 | const host = net.IPv6{ .octets = info.addr, .scope_id = info.scope_id }; | |
| 56 | const port = mem.bigToNative(u16, info.port); | |
| 57 | return Socket.Address.initIPv6(host, port); | |
| 58 | }, | |
| 59 | else => unreachable, | |
| 60 | } | |
| 61 | } | |
| 62 | ||
| 63 | /// Encodes a generic socket address into an extern union that may be reliably | |
| 64 | /// casted into a `sockaddr` which may be passed into socket syscalls. | |
| 65 | pub fn toNative(self: Socket.Address) extern union { | |
| 66 | ipv4: os.sockaddr_in, | |
| 67 | ipv6: os.sockaddr_in6, | |
| 68 | } { | |
| 69 | return switch (self) { | |
| 70 | .ipv4 => |address| .{ | |
| 71 | .ipv4 = .{ | |
| 72 | .addr = @bitCast(u32, address.host.octets), | |
| 73 | .port = mem.nativeToBig(u16, address.port), | |
| 74 | }, | |
| 75 | }, | |
| 76 | .ipv6 => |address| .{ | |
| 77 | .ipv6 = .{ | |
| 78 | .addr = address.host.octets, | |
| 79 | .port = mem.nativeToBig(u16, address.port), | |
| 80 | .scope_id = address.host.scope_id, | |
| 81 | .flowinfo = 0, | |
| 82 | }, | |
| 83 | }, | |
| 84 | }; | |
| 85 | } | |
| 86 | ||
| 87 | /// Returns the number of bytes that make up the `sockaddr` equivalent to the address. | |
| 88 | pub fn getNativeSize(self: Socket.Address) u32 { | |
| 89 | return switch (self) { | |
| 90 | .ipv4 => @sizeOf(os.sockaddr_in), | |
| 91 | .ipv6 => @sizeOf(os.sockaddr_in6), | |
| 92 | }; | |
| 93 | } | |
| 94 | ||
| 95 | /// Implements the `std.fmt.format` API. | |
| 96 | pub fn format( | |
| 97 | self: Socket.Address, | |
| 98 | comptime layout: []const u8, | |
| 99 | opts: fmt.FormatOptions, | |
| 100 | writer: anytype, | |
| 101 | ) !void { | |
| 102 | switch (self) { | |
| 103 | .ipv4 => |address| try fmt.format(writer, "{}:{}", .{ address.host, address.port }), | |
| 104 | .ipv6 => |address| try fmt.format(writer, "{}:{}", .{ address.host, address.port }), | |
| 105 | } | |
| 106 | } | |
| 107 | }; | |
| 108 | ||
| 10 | 109 | /// The underlying handle of a socket. |
| 11 | 110 | fd: os.socket_t, |
| 12 | 111 | |
| ... | ... | @@ -31,8 +130,8 @@ pub fn shutdown(self: Socket, how: os.ShutdownHow) !void { |
| 31 | 130 | } |
| 32 | 131 | |
| 33 | 132 | /// Binds the socket to an address. |
| 34 | pub fn bind(self: Socket, comptime Address: type, address: Address) !void { | |
| 35 | return os.bind(self.fd, @ptrCast(*const os.sockaddr, &address), @sizeOf(Address)); | |
| 133 | pub fn bind(self: Socket, address: Socket.Address) !void { | |
| 134 | return os.bind(self.fd, @ptrCast(*const os.sockaddr, &address.toNative()), address.getNativeSize()); | |
| 36 | 135 | } |
| 37 | 136 | |
| 38 | 137 | /// Start listening for incoming connections on the socket. |
| ... | ... | @@ -41,19 +140,20 @@ pub fn listen(self: Socket, max_backlog_size: u31) !void { |
| 41 | 140 | } |
| 42 | 141 | |
| 43 | 142 | /// Have the socket attempt to the connect to an address. |
| 44 | pub fn connect(self: Socket, comptime Address: type, address: Address) !void { | |
| 45 | return os.connect(self.fd, @ptrCast(*const os.sockaddr, &address), @sizeOf(Address)); | |
| 143 | pub fn connect(self: Socket, address: Socket.Address) !void { | |
| 144 | return os.connect(self.fd, @ptrCast(*const os.sockaddr, &address.toNative()), address.getNativeSize()); | |
| 46 | 145 | } |
| 47 | 146 | |
| 48 | 147 | /// Accept a pending incoming connection queued to the kernel backlog |
| 49 | 148 | /// of the socket. |
| 50 | pub fn accept(self: Socket, comptime Connection: type, comptime Address: type, flags: u32) !Connection { | |
| 51 | var address: Address = undefined; | |
| 52 | var address_len: u32 = @sizeOf(Address); | |
| 149 | pub fn accept(self: Socket, flags: u32) !Socket.Connection { | |
| 150 | var address: os.sockaddr = undefined; | |
| 151 | var address_len: u32 = @sizeOf(os.sockaddr); | |
| 53 | 152 | |
| 54 | const fd = try os.accept(self.fd, @ptrCast(*os.sockaddr, &address), &address_len, flags); | |
| 153 | const socket = Socket{ .fd = try os.accept(self.fd, &address, &address_len, flags) }; | |
| 154 | const socket_address = Socket.Address.fromNative(@alignCast(4, &address)); | |
| 55 | 155 | |
| 56 | return Connection.from(.{ .fd = fd }, address); | |
| 156 | return Socket.Connection.from(socket, socket_address); | |
| 57 | 157 | } |
| 58 | 158 | |
| 59 | 159 | /// Read data from the socket into the buffer provided. It returns the |
| ... | ... | @@ -94,11 +194,11 @@ pub fn sendmsg(self: Socket, msg: os.msghdr_const, flags: u32) !usize { |
| 94 | 194 | } |
| 95 | 195 | |
| 96 | 196 | /// Query the address that the socket is locally bounded to. |
| 97 | pub fn getLocalAddress(self: Socket, comptime Address: type) !Address { | |
| 98 | var address: Address = undefined; | |
| 99 | var address_len: u32 = @sizeOf(Address); | |
| 100 | try os.getsockname(self.fd, @ptrCast(*os.sockaddr, &address), &address_len); | |
| 101 | return address; | |
| 197 | pub fn getLocalAddress(self: Socket) !Socket.Address { | |
| 198 | var address: os.sockaddr = undefined; | |
| 199 | var address_len: u32 = @sizeOf(os.sockaddr); | |
| 200 | try os.getsockname(self.fd, &address, &address_len); | |
| 201 | return Socket.Address.fromNative(@alignCast(4, &address)); | |
| 102 | 202 | } |
| 103 | 203 | |
| 104 | 204 | /// Query and return the latest cached error on the socket. |
lib/std/x/os/net.zig+18| ... | ... | @@ -1,3 +1,9 @@ |
| 1 | // SPDX-License-Identifier: MIT | |
| 2 | // Copyright (c) 2015-2021 Zig Contributors | |
| 3 | // This file is part of [zig](https://ziglang.org/), which is MIT licensed. | |
| 4 | // The MIT license requires this copyright notice to be included in all copies | |
| 5 | // and substantial portions of the software. | |
| 6 | ||
| 1 | 7 | const std = @import("../../std.zig"); |
| 2 | 8 | |
| 3 | 9 | const os = std.os; |
| ... | ... | @@ -27,6 +33,12 @@ pub fn resolveScopeID(name: []const u8) !u32 { |
| 27 | 33 | |
| 28 | 34 | /// An IPv4 address comprised of 4 bytes. |
| 29 | 35 | pub const IPv4 = extern struct { |
| 36 | /// A IPv4 host-port pair. | |
| 37 | pub const Address = extern struct { | |
| 38 | host: IPv4, | |
| 39 | port: u16, | |
| 40 | }; | |
| 41 | ||
| 30 | 42 | /// Octets of a IPv4 address designating the local host. |
| 31 | 43 | pub const localhost_octets = [_]u8{ 127, 0, 0, 1 }; |
| 32 | 44 | |
| ... | ... | @@ -200,6 +212,12 @@ pub const IPv4 = extern struct { |
| 200 | 212 | /// An IPv6 address comprised of 16 bytes for an address, and 4 bytes |
| 201 | 213 | /// for a scope ID; cumulatively summing to 20 bytes in total. |
| 202 | 214 | pub const IPv6 = extern struct { |
| 215 | /// A IPv6 host-port pair. | |
| 216 | pub const Address = extern struct { | |
| 217 | host: IPv6, | |
| 218 | port: u16, | |
| 219 | }; | |
| 220 | ||
| 203 | 221 | /// Octets of a IPv6 address designating the local host. |
| 204 | 222 | pub const localhost_octets = [_]u8{0} ** 15 ++ [_]u8{0x01}; |
| 205 | 223 |