authorgravatar for truemedian@gmail.comNameless <truemedian@gmail.com> 2023-12-14 15:52:39-06:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-01-13 18:51:38-08:00
logb723296e1fa65b73a43b0790bdddcbfcea7d656d
tree33173696d644f655374e134b73df3d694e4a1a28
parent832f6d8f7f7f7b10b86b109a8b26bb5eacc8d13e

std.http: add missing documentation and a few examples


4 files changed, 141 insertions(+), 13 deletions(-)

lib/std/http/Client.zig+50-3
...@@ -1,4 +1,8 @@...@@ -1,4 +1,8 @@
1//! Connecting and opening requests are threadsafe. Individual requests are not.1//! HTTP(S) Client implementation.
2//!
3//! Connections are opened in a thread-safe manner, but individual Requests are not.
4//!
5//! TLS support may be disabled via `std.options.http_disable_tls`.
26
3const std = @import("../std.zig");7const std = @import("../std.zig");
4const builtin = @import("builtin");8const builtin = @import("builtin");
...@@ -157,6 +161,9 @@ pub const ConnectionPool = struct {...@@ -157,6 +161,9 @@ pub const ConnectionPool = struct {
157 pool.free_size = new_size;161 pool.free_size = new_size;
158 }162 }
159163
164 /// Frees the connection pool and closes all connections within. This function is threadsafe.
165 ///
166 /// All future operations on the connection pool will deadlock.
160 pub fn deinit(pool: *ConnectionPool, allocator: Allocator) void {167 pub fn deinit(pool: *ConnectionPool, allocator: Allocator) void {
161 pool.mutex.lock();168 pool.mutex.lock();
162169
...@@ -191,11 +198,19 @@ pub const Connection = struct {...@@ -191,11 +198,19 @@ pub const Connection = struct {
191 /// undefined unless protocol is tls.198 /// undefined unless protocol is tls.
192 tls_client: if (!disable_tls) *std.crypto.tls.Client else void,199 tls_client: if (!disable_tls) *std.crypto.tls.Client else void,
193200
201 /// The protocol that this connection is using.
194 protocol: Protocol,202 protocol: Protocol,
203
204 /// The host that this connection is connected to.
195 host: []u8,205 host: []u8,
206
207 /// The port that this connection is connected to.
196 port: u16,208 port: u16,
197209
210 /// Whether this connection is proxied and is not directly connected.
198 proxied: bool = false,211 proxied: bool = false,
212
213 /// Whether this connection is closing when we're done with it.
199 closing: bool = false,214 closing: bool = false,
200215
201 read_start: BufferSize = 0,216 read_start: BufferSize = 0,
...@@ -232,6 +247,7 @@ pub const Connection = struct {...@@ -232,6 +247,7 @@ pub const Connection = struct {
232 };247 };
233 }248 }
234249
250 /// Refills the read buffer with data from the connection.
235 pub fn fill(conn: *Connection) ReadError!void {251 pub fn fill(conn: *Connection) ReadError!void {
236 if (conn.read_end != conn.read_start) return;252 if (conn.read_end != conn.read_start) return;
237253
...@@ -244,14 +260,17 @@ pub const Connection = struct {...@@ -244,14 +260,17 @@ pub const Connection = struct {
244 conn.read_end = @intCast(nread);260 conn.read_end = @intCast(nread);
245 }261 }
246262
263 /// Returns the current slice of buffered data.
247 pub fn peek(conn: *Connection) []const u8 {264 pub fn peek(conn: *Connection) []const u8 {
248 return conn.read_buf[conn.read_start..conn.read_end];265 return conn.read_buf[conn.read_start..conn.read_end];
249 }266 }
250267
268 /// Discards the given number of bytes from the read buffer.
251 pub fn drop(conn: *Connection, num: BufferSize) void {269 pub fn drop(conn: *Connection, num: BufferSize) void {
252 conn.read_start += num;270 conn.read_start += num;
253 }271 }
254272
273 /// Reads data from the connection into the given buffer.
255 pub fn read(conn: *Connection, buffer: []u8) ReadError!usize {274 pub fn read(conn: *Connection, buffer: []u8) ReadError!usize {
256 const available_read = conn.read_end - conn.read_start;275 const available_read = conn.read_end - conn.read_start;
257 const available_buffer = buffer.len;276 const available_buffer = buffer.len;
...@@ -318,6 +337,7 @@ pub const Connection = struct {...@@ -318,6 +337,7 @@ pub const Connection = struct {
318 };337 };
319 }338 }
320339
340 /// Writes the given buffer to the connection.
321 pub fn write(conn: *Connection, buffer: []const u8) WriteError!usize {341 pub fn write(conn: *Connection, buffer: []const u8) WriteError!usize {
322 if (conn.write_end + buffer.len > conn.write_buf.len) {342 if (conn.write_end + buffer.len > conn.write_buf.len) {
323 try conn.flush();343 try conn.flush();
...@@ -334,6 +354,7 @@ pub const Connection = struct {...@@ -334,6 +354,7 @@ pub const Connection = struct {
334 return buffer.len;354 return buffer.len;
335 }355 }
336356
357 /// Flushes the write buffer to the connection.
337 pub fn flush(conn: *Connection) WriteError!void {358 pub fn flush(conn: *Connection) WriteError!void {
338 if (conn.write_end == 0) return;359 if (conn.write_end == 0) return;
339360
...@@ -352,6 +373,7 @@ pub const Connection = struct {...@@ -352,6 +373,7 @@ pub const Connection = struct {
352 return Writer{ .context = conn };373 return Writer{ .context = conn };
353 }374 }
354375
376 /// Closes the connection.
355 pub fn close(conn: *Connection, allocator: Allocator) void {377 pub fn close(conn: *Connection, allocator: Allocator) void {
356 if (conn.protocol == .tls) {378 if (conn.protocol == .tls) {
357 if (disable_tls) unreachable;379 if (disable_tls) unreachable;
...@@ -502,8 +524,13 @@ pub const Response = struct {...@@ -502,8 +524,13 @@ pub const Response = struct {
502 try expectEqual(@as(u10, 999), parseInt3("999"));524 try expectEqual(@as(u10, 999), parseInt3("999"));
503 }525 }
504526
527 /// The HTTP version this response is using.
505 version: http.Version,528 version: http.Version,
529
530 /// The status code of the response.
506 status: http.Status,531 status: http.Status,
532
533 /// The reason phrase of the response.
507 reason: []const u8,534 reason: []const u8,
508535
509 /// If present, the number of bytes in the response body.536 /// If present, the number of bytes in the response body.
...@@ -528,22 +555,36 @@ pub const Response = struct {...@@ -528,22 +555,36 @@ pub const Response = struct {
528///555///
529/// Order of operations: open -> send[ -> write -> finish] -> wait -> read556/// Order of operations: open -> send[ -> write -> finish] -> wait -> read
530pub const Request = struct {557pub const Request = struct {
558 /// The uri that this request is being sent to.
531 uri: Uri,559 uri: Uri,
560
561 /// The client that this request was created from.
532 client: *Client,562 client: *Client,
533 /// is null when this connection is released563
564 /// Underlying connection to the server. This is null when the connection is released.
534 connection: ?*Connection,565 connection: ?*Connection,
535566
536 method: http.Method,567 method: http.Method,
537 version: http.Version = .@"HTTP/1.1",568 version: http.Version = .@"HTTP/1.1",
569
570 /// The list of HTTP request headers.
538 headers: http.Headers,571 headers: http.Headers,
539572
540 /// The transfer encoding of the request body.573 /// The transfer encoding of the request body.
541 transfer_encoding: RequestTransfer = .none,574 transfer_encoding: RequestTransfer = .none,
542575
576 /// The redirect quota left for this request.
543 redirects_left: u32,577 redirects_left: u32,
578
579 /// Whether the request should follow redirects.
544 handle_redirects: bool,580 handle_redirects: bool,
581
582 /// Whether the request should handle a 100-continue response before sending the request body.
545 handle_continue: bool,583 handle_continue: bool,
546584
585 /// The response associated with this request.
586 ///
587 /// This field is undefined until `wait` is called.
547 response: Response,588 response: Response,
548589
549 /// Used as a allocator for resolving redirects locations.590 /// Used as a allocator for resolving redirects locations.
...@@ -993,6 +1034,7 @@ pub const Request = struct {...@@ -993,6 +1034,7 @@ pub const Request = struct {
993 }1034 }
994};1035};
9951036
1037/// A HTTP proxy server.
996pub const Proxy = struct {1038pub const Proxy = struct {
997 allocator: Allocator,1039 allocator: Allocator,
998 headers: http.Headers,1040 headers: http.Headers,
...@@ -1144,6 +1186,7 @@ pub fn loadDefaultProxies(client: *Client) !void {...@@ -1144,6 +1186,7 @@ pub fn loadDefaultProxies(client: *Client) !void {
1144pub const ConnectTcpError = Allocator.Error || error{ ConnectionRefused, NetworkUnreachable, ConnectionTimedOut, ConnectionResetByPeer, TemporaryNameServerFailure, NameServerFailure, UnknownHostName, HostLacksNetworkAddresses, UnexpectedConnectFailure, TlsInitializationFailed };1186pub const ConnectTcpError = Allocator.Error || error{ ConnectionRefused, NetworkUnreachable, ConnectionTimedOut, ConnectionResetByPeer, TemporaryNameServerFailure, NameServerFailure, UnknownHostName, HostLacksNetworkAddresses, UnexpectedConnectFailure, TlsInitializationFailed };
11451187
1146/// Connect to `host:port` using the specified protocol. This will reuse a connection if one is already open.1188/// Connect to `host:port` using the specified protocol. This will reuse a connection if one is already open.
1189///
1147/// This function is threadsafe.1190/// This function is threadsafe.
1148pub fn connectTcp(client: *Client, host: []const u8, port: u16, protocol: Connection.Protocol) ConnectTcpError!*Connection {1191pub fn connectTcp(client: *Client, host: []const u8, port: u16, protocol: Connection.Protocol) ConnectTcpError!*Connection {
1149 if (client.connection_pool.findConnection(.{1192 if (client.connection_pool.findConnection(.{
...@@ -1203,6 +1246,7 @@ pub fn connectTcp(client: *Client, host: []const u8, port: u16, protocol: Connec...@@ -1203,6 +1246,7 @@ pub fn connectTcp(client: *Client, host: []const u8, port: u16, protocol: Connec
1203pub const ConnectUnixError = Allocator.Error || std.os.SocketError || error{ NameTooLong, Unsupported } || std.os.ConnectError;1246pub const ConnectUnixError = Allocator.Error || std.os.SocketError || error{ NameTooLong, Unsupported } || std.os.ConnectError;
12041247
1205/// Connect to `path` as a unix domain socket. This will reuse a connection if one is already open.1248/// Connect to `path` as a unix domain socket. This will reuse a connection if one is already open.
1249///
1206/// This function is threadsafe.1250/// This function is threadsafe.
1207pub fn connectUnix(client: *Client, path: []const u8) ConnectUnixError!*Connection {1251pub fn connectUnix(client: *Client, path: []const u8) ConnectUnixError!*Connection {
1208 if (!net.has_unix_sockets) return error.Unsupported;1252 if (!net.has_unix_sockets) return error.Unsupported;
...@@ -1237,6 +1281,7 @@ pub fn connectUnix(client: *Client, path: []const u8) ConnectUnixError!*Connecti...@@ -1237,6 +1281,7 @@ pub fn connectUnix(client: *Client, path: []const u8) ConnectUnixError!*Connecti
1237}1281}
12381282
1239/// Connect to `tunnel_host:tunnel_port` using the specified proxy with HTTP CONNECT. This will reuse a connection if one is already open.1283/// Connect to `tunnel_host:tunnel_port` using the specified proxy with HTTP CONNECT. This will reuse a connection if one is already open.
1284///
1240/// This function is threadsafe.1285/// This function is threadsafe.
1241pub fn connectTunnel(1286pub fn connectTunnel(
1242 client: *Client,1287 client: *Client,
...@@ -1318,7 +1363,6 @@ const ConnectErrorPartial = ConnectTcpError || error{ UnsupportedUrlScheme, Conn...@@ -1318,7 +1363,6 @@ const ConnectErrorPartial = ConnectTcpError || error{ UnsupportedUrlScheme, Conn
1318pub const ConnectError = ConnectErrorPartial || RequestError;1363pub const ConnectError = ConnectErrorPartial || RequestError;
13191364
1320/// Connect to `host:port` using the specified protocol. This will reuse a connection if one is already open.1365/// Connect to `host:port` using the specified protocol. This will reuse a connection if one is already open.
1321///
1322/// If a proxy is configured for the client, then the proxy will be used to connect to the host.1366/// If a proxy is configured for the client, then the proxy will be used to connect to the host.
1323///1367///
1324/// This function is threadsafe.1368/// This function is threadsafe.
...@@ -1375,7 +1419,10 @@ pub const RequestOptions = struct {...@@ -1375,7 +1419,10 @@ pub const RequestOptions = struct {
1375 /// request, then the request *will* deadlock.1419 /// request, then the request *will* deadlock.
1376 handle_continue: bool = true,1420 handle_continue: bool = true,
13771421
1422 /// Automatically follow redirects. This will only follow redirects for repeatable requests (ie. with no payload or the server has acknowledged the payload)
1378 handle_redirects: bool = true,1423 handle_redirects: bool = true,
1424
1425 /// How many redirects to follow before returning an error.
1379 max_redirects: u32 = 3,1426 max_redirects: u32 = 3,
1380 header_strategy: StorageStrategy = .{ .dynamic = 16 * 1024 },1427 header_strategy: StorageStrategy = .{ .dynamic = 16 * 1024 },
13811428
lib/std/http/Headers.zig+12-1
...@@ -35,6 +35,7 @@ pub const CaseInsensitiveStringContext = struct {...@@ -35,6 +35,7 @@ pub const CaseInsensitiveStringContext = struct {
35 }35 }
36};36};
3737
38/// A single HTTP header field.
38pub const Field = struct {39pub const Field = struct {
39 name: []const u8,40 name: []const u8,
40 value: []const u8,41 value: []const u8,
...@@ -47,6 +48,7 @@ pub const Field = struct {...@@ -47,6 +48,7 @@ pub const Field = struct {
47 }48 }
48};49};
4950
51/// A list of HTTP header fields.
50pub const Headers = struct {52pub const Headers = struct {
51 allocator: Allocator,53 allocator: Allocator,
52 list: HeaderList = .{},54 list: HeaderList = .{},
...@@ -56,10 +58,12 @@ pub const Headers = struct {...@@ -56,10 +58,12 @@ pub const Headers = struct {
56 /// Use with caution.58 /// Use with caution.
57 owned: bool = true,59 owned: bool = true,
5860
61 /// Initialize an empty list of headers.
59 pub fn init(allocator: Allocator) Headers {62 pub fn init(allocator: Allocator) Headers {
60 return .{ .allocator = allocator };63 return .{ .allocator = allocator };
61 }64 }
6265
66 /// Initialize a pre-populated list of headers from a list of fields.
63 pub fn initList(allocator: Allocator, list: []const Field) !Headers {67 pub fn initList(allocator: Allocator, list: []const Field) !Headers {
64 var new = Headers.init(allocator);68 var new = Headers.init(allocator);
6569
...@@ -72,6 +76,9 @@ pub const Headers = struct {...@@ -72,6 +76,9 @@ pub const Headers = struct {
72 return new;76 return new;
73 }77 }
7478
79 /// Deallocate all memory associated with the headers.
80 ///
81 /// If the `owned` field is false, this will not free the names and values of the headers.
75 pub fn deinit(headers: *Headers) void {82 pub fn deinit(headers: *Headers) void {
76 headers.deallocateIndexListsAndFields();83 headers.deallocateIndexListsAndFields();
77 headers.index.deinit(headers.allocator);84 headers.index.deinit(headers.allocator);
...@@ -80,7 +87,9 @@ pub const Headers = struct {...@@ -80,7 +87,9 @@ pub const Headers = struct {
80 headers.* = undefined;87 headers.* = undefined;
81 }88 }
8289
83 /// Appends a header to the list. Both name and value are copied.90 /// Appends a header to the list.
91 ///
92 /// If the `owned` field is true, both name and value will be copied.
84 pub fn append(headers: *Headers, name: []const u8, value: []const u8) !void {93 pub fn append(headers: *Headers, name: []const u8, value: []const u8) !void {
85 const n = headers.list.items.len;94 const n = headers.list.items.len;
8695
...@@ -108,6 +117,7 @@ pub const Headers = struct {...@@ -108,6 +117,7 @@ pub const Headers = struct {
108 try headers.list.append(headers.allocator, entry);117 try headers.list.append(headers.allocator, entry);
109 }118 }
110119
120 /// Returns true if this list of headers contains the given name.
111 pub fn contains(headers: Headers, name: []const u8) bool {121 pub fn contains(headers: Headers, name: []const u8) bool {
112 return headers.index.contains(name);122 return headers.index.contains(name);
113 }123 }
...@@ -285,6 +295,7 @@ pub const Headers = struct {...@@ -285,6 +295,7 @@ pub const Headers = struct {
285 headers.list.clearRetainingCapacity();295 headers.list.clearRetainingCapacity();
286 }296 }
287297
298 /// Creates a copy of the headers using the provided allocator.
288 pub fn clone(headers: Headers, allocator: Allocator) !Headers {299 pub fn clone(headers: Headers, allocator: Allocator) !Headers {
289 var new = Headers.init(allocator);300 var new = Headers.init(allocator);
290301
lib/std/http/Server.zig+78-8
...@@ -1,3 +1,44 @@...@@ -1,3 +1,44 @@
1//! HTTP Server implementation.
2//!
3//! This server assumes *all* clients are well behaved and standard compliant; it can and will deadlock if a client holds a connection open without sending a request.
4//!
5//! Example usage:
6//!
7//! ```zig
8//! var server = Server.init(.{ .reuse_address = true });
9//! defer server.deinit();
10//!
11//! try server.listen(bind_addr);
12//!
13//! while (true) {
14//! var res = try server.accept(.{ .allocator = gpa });
15//! defer res.deinit();
16//!
17//! while (res.reset() != .closing) {
18//! res.wait() catch |err| switch (err) {
19//! error.HttpHeadersInvalid => break,
20//! error.HttpHeadersExceededSizeLimit => {
21//! res.status = .request_header_fields_too_large;
22//! res.send() catch break;
23//! break;
24//! },
25//! else => {
26//! res.status = .bad_request;
27//! res.send() catch break;
28//! break;
29//! },
30//! }
31//!
32//! res.status = .ok;
33//! res.transfer_encoding = .chunked;
34//!
35//! try res.send();
36//! try res.writeAll("Hello, World!\n");
37//! try res.finish();
38//! }
39//! }
40//! ```
41
1const std = @import("../std.zig");42const std = @import("../std.zig");
2const testing = std.testing;43const testing = std.testing;
3const http = std.http;44const http = std.http;
...@@ -10,8 +51,7 @@ const assert = std.debug.assert;...@@ -10,8 +51,7 @@ const assert = std.debug.assert;
10const Server = @This();51const Server = @This();
11const proto = @import("protocol.zig");52const proto = @import("protocol.zig");
1253
13allocator: Allocator,54/// The underlying server socket.
14
15socket: net.StreamServer,55socket: net.StreamServer,
1656
17/// An interface to a plain connection.57/// An interface to a plain connection.
...@@ -269,8 +309,13 @@ pub const Request = struct {...@@ -269,8 +309,13 @@ pub const Request = struct {
269 return @as(u64, @bitCast(array.*));309 return @as(u64, @bitCast(array.*));
270 }310 }
271311
312 /// The HTTP request method.
272 method: http.Method,313 method: http.Method,
314
315 /// The HTTP request target.
273 target: []const u8,316 target: []const u8,
317
318 /// The HTTP version of this request.
274 version: http.Version,319 version: http.Version,
275320
276 /// The length of the request body, if known.321 /// The length of the request body, if known.
...@@ -282,16 +327,21 @@ pub const Request = struct {...@@ -282,16 +327,21 @@ pub const Request = struct {
282 /// The compression of the request body, or .identity (no compression) if not present.327 /// The compression of the request body, or .identity (no compression) if not present.
283 transfer_compression: http.ContentEncoding = .identity,328 transfer_compression: http.ContentEncoding = .identity,
284329
330 /// The list of HTTP request headers
285 headers: http.Headers,331 headers: http.Headers,
332
286 parser: proto.HeadersParser,333 parser: proto.HeadersParser,
287 compression: Compression = .none,334 compression: Compression = .none,
288};335};
289336
290/// A HTTP response waiting to be sent.337/// A HTTP response waiting to be sent.
291///338///
292/// [/ <----------------------------------- \]339/// Order of operations:
293/// Order of operations: accept -> wait -> send [ -> write -> finish][ -> reset /]340/// ```
294/// \ -> read /341/// [/ <--------------------------------------- \]
342/// accept -> wait -> send [ -> write -> finish][ -> reset /]
343/// \ -> read /
344/// ```
295pub const Response = struct {345pub const Response = struct {
296 version: http.Version = .@"HTTP/1.1",346 version: http.Version = .@"HTTP/1.1",
297 status: http.Status = .ok,347 status: http.Status = .ok,
...@@ -299,11 +349,21 @@ pub const Response = struct {...@@ -299,11 +349,21 @@ pub const Response = struct {
299349
300 transfer_encoding: ResponseTransfer = .none,350 transfer_encoding: ResponseTransfer = .none,
301351
352 /// The allocator responsible for allocating memory for this response.
302 allocator: Allocator,353 allocator: Allocator,
354
355 /// The peer's address
303 address: net.Address,356 address: net.Address,
357
358 /// The underlying connection for this response.
304 connection: Connection,359 connection: Connection,
305360
361 /// The HTTP response headers
306 headers: http.Headers,362 headers: http.Headers,
363
364 /// The HTTP request that this response is responding to.
365 ///
366 /// This field is only valid after calling `wait`.
307 request: Request,367 request: Request,
308368
309 state: State = .first,369 state: State = .first,
...@@ -495,6 +555,17 @@ pub const Response = struct {...@@ -495,6 +555,17 @@ pub const Response = struct {
495 pub const WaitError = Connection.ReadError || proto.HeadersParser.CheckCompleteHeadError || Request.ParseError || error{ CompressionInitializationFailed, CompressionNotSupported };555 pub const WaitError = Connection.ReadError || proto.HeadersParser.CheckCompleteHeadError || Request.ParseError || error{ CompressionInitializationFailed, CompressionNotSupported };
496556
497 /// Wait for the client to send a complete request head.557 /// Wait for the client to send a complete request head.
558 ///
559 /// For correct behavior, the following rules must be followed:
560 ///
561 /// * If this returns any error in `Connection.ReadError`, you MUST immediately close the connection by calling `deinit`.
562 /// * If this returns `error.HttpHeadersInvalid`, you MAY immediately close the connection by calling `deinit`.
563 /// * If this returns `error.HttpHeadersExceededSizeLimit`, you MUST respond with a 431 status code and then call `deinit`.
564 /// * If this returns any error in `Request.ParseError`, you MUST respond with a 400 status code and then call `deinit`.
565 /// * If this returns any other error, you MUST respond with a 400 status code and then call `deinit`.
566 /// * If the request has an Expect header containing 100-continue, you MUST either:
567 /// * Respond with a 100 status code, then call `wait` again.
568 /// * Respond with a 417 status code.
498 pub fn wait(res: *Response) WaitError!void {569 pub fn wait(res: *Response) WaitError!void {
499 switch (res.state) {570 switch (res.state) {
500 .first, .start => res.state = .waited,571 .first, .start => res.state = .waited,
...@@ -664,9 +735,8 @@ pub const Response = struct {...@@ -664,9 +735,8 @@ pub const Response = struct {
664};735};
665736
666/// Create a new HTTP server.737/// Create a new HTTP server.
667pub fn init(allocator: Allocator, options: net.StreamServer.Options) Server {738pub fn init(options: net.StreamServer.Options) Server {
668 return .{739 return .{
669 .allocator = allocator,
670 .socket = net.StreamServer.init(options),740 .socket = net.StreamServer.init(options),
671 };741 };
672}742}
...@@ -748,7 +818,7 @@ test "HTTP server handles a chunked transfer coding request" {...@@ -748,7 +818,7 @@ test "HTTP server handles a chunked transfer coding request" {
748 const expect = std.testing.expect;818 const expect = std.testing.expect;
749819
750 const max_header_size = 8192;820 const max_header_size = 8192;
751 var server = std.http.Server.init(allocator, .{ .reuse_address = true });821 var server = std.http.Server.init(.{ .reuse_address = true });
752 defer server.deinit();822 defer server.deinit();
753823
754 const address = try std.net.Address.parseIp("127.0.0.1", 0);824 const address = try std.net.Address.parseIp("127.0.0.1", 0);
test/standalone/http.zig+1-1
...@@ -220,7 +220,7 @@ pub fn main() !void {...@@ -220,7 +220,7 @@ pub fn main() !void {
220220
221 defer _ = gpa_client.deinit();221 defer _ = gpa_client.deinit();
222222
223 server = Server.init(salloc, .{ .reuse_address = true });223 server = Server.init(.{ .reuse_address = true });
224224
225 const addr = std.net.Address.parseIp("127.0.0.1", 0) catch unreachable;225 const addr = std.net.Address.parseIp("127.0.0.1", 0) catch unreachable;
226 try server.listen(addr);226 try server.listen(addr);