authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-04-12 15:19:40-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-04-12 22:37:07-07:00
log6deb3e39869ec9d75be20ea2685f66239baebf39
treee01a1b61486fcebfa7d1a0220a995aac0ca22bf0
parent419753f45ea4de7b6de090f7ad574f5ccaad5669

std.http.Client: always omit port when it matches default

This makes the host http header have the port if and only if it differs from the defaults based on the protocol. This is an alternate implementation that closes #19624.

2 files changed, 33 insertions(+), 15 deletions(-)

lib/std/Uri.zig+6-1
...@@ -242,6 +242,9 @@ pub const WriteToStreamOptions = struct {...@@ -242,6 +242,9 @@ pub const WriteToStreamOptions = struct {
242242
243 /// When true, include the fragment part of the URI. Ignored when `path` is false.243 /// When true, include the fragment part of the URI. Ignored when `path` is false.
244 fragment: bool = false,244 fragment: bool = false,
245
246 /// When true, include the port part of the URI. Ignored when `port` is null.
247 port: bool = true,
245};248};
246249
247pub fn writeToStream(250pub fn writeToStream(
...@@ -267,7 +270,9 @@ pub fn writeToStream(...@@ -267,7 +270,9 @@ pub fn writeToStream(
267 }270 }
268 if (uri.host) |host| {271 if (uri.host) |host| {
269 try writer.print("{host}", .{host});272 try writer.print("{host}", .{host});
270 if (uri.port) |port| try writer.print(":{d}", .{port});273 if (options.port) {
274 if (uri.port) |port| try writer.print(":{d}", .{port});
275 }
271 }276 }
272 }277 }
273 if (options.path) {278 if (options.path) {
lib/std/http/Client.zig+27-14
...@@ -218,7 +218,17 @@ pub const Connection = struct {...@@ -218,7 +218,17 @@ pub const Connection = struct {
218 pub const buffer_size = std.crypto.tls.max_ciphertext_record_len;218 pub const buffer_size = std.crypto.tls.max_ciphertext_record_len;
219 const BufferSize = std.math.IntFittingRange(0, buffer_size);219 const BufferSize = std.math.IntFittingRange(0, buffer_size);
220220
221 pub const Protocol = enum { plain, tls };221 pub const Protocol = enum {
222 plain,
223 tls,
224
225 pub fn port(p: Protocol) u16 {
226 return switch (p) {
227 .plain => 80,
228 .tls => 443,
229 };
230 }
231 };
222232
223 pub fn readvDirectTls(conn: *Connection, buffers: []std.posix.iovec) ReadError!usize {233 pub fn readvDirectTls(conn: *Connection, buffers: []std.posix.iovec) ReadError!usize {
224 return conn.tls_client.readv(conn.stream, buffers) catch |err| {234 return conn.tls_client.readv(conn.stream, buffers) catch |err| {
...@@ -805,7 +815,7 @@ pub const Request = struct {...@@ -805,7 +815,7 @@ pub const Request = struct {
805 }815 }
806816
807 req.uri = valid_uri;817 req.uri = valid_uri;
808 req.connection = try req.client.connect(new_host, uriPort(valid_uri, protocol), protocol);818 req.connection = try req.client.connect(new_host, valid_uri.port.?, protocol);
809 req.redirect_behavior.subtractOne();819 req.redirect_behavior.subtractOne();
810 req.response.parser.reset();820 req.response.parser.reset();
811821
...@@ -847,8 +857,13 @@ pub const Request = struct {...@@ -847,8 +857,13 @@ pub const Request = struct {
847 try w.writeAll("\r\n");857 try w.writeAll("\r\n");
848858
849 if (try emitOverridableHeader("host: ", req.headers.host, w)) {859 if (try emitOverridableHeader("host: ", req.headers.host, w)) {
860 // URI has already been validated so this cannot fail.
861 const default_port = (uriProtocol(req.uri) catch unreachable).port();
850 try w.writeAll("host: ");862 try w.writeAll("host: ");
851 try req.uri.writeToStream(.{ .authority = true }, w);863 try req.uri.writeToStream(.{
864 .authority = true,
865 .port = req.uri.port.? != default_port,
866 }, w);
852 try w.writeAll("\r\n");867 try w.writeAll("\r\n");
853 }868 }
854869
...@@ -1264,7 +1279,7 @@ fn createProxyFromEnvVar(arena: Allocator, env_var_names: []const []const u8) !?...@@ -1264,7 +1279,7 @@ fn createProxyFromEnvVar(arena: Allocator, env_var_names: []const []const u8) !?
1264 .protocol = protocol,1279 .protocol = protocol,
1265 .host = valid_uri.host.?.raw,1280 .host = valid_uri.host.?.raw,
1266 .authorization = authorization,1281 .authorization = authorization,
1267 .port = uriPort(valid_uri, protocol),1282 .port = valid_uri.port.?,
1268 .supports_connect = true,1283 .supports_connect = true,
1269 };1284 };
1270 return proxy;1285 return proxy;
...@@ -1569,29 +1584,27 @@ pub const RequestOptions = struct {...@@ -1569,29 +1584,27 @@ pub const RequestOptions = struct {
1569 privileged_headers: []const http.Header = &.{},1584 privileged_headers: []const http.Header = &.{},
1570};1585};
15711586
1572fn validateUri(uri: Uri, arena: Allocator) !struct { Connection.Protocol, Uri } {1587fn uriProtocol(uri: Uri) !Connection.Protocol {
1573 const protocol_map = std.ComptimeStringMap(Connection.Protocol, .{1588 const protocol_map = std.ComptimeStringMap(Connection.Protocol, .{
1574 .{ "http", .plain },1589 .{ "http", .plain },
1575 .{ "ws", .plain },1590 .{ "ws", .plain },
1576 .{ "https", .tls },1591 .{ "https", .tls },
1577 .{ "wss", .tls },1592 .{ "wss", .tls },
1578 });1593 });
1579 const protocol = protocol_map.get(uri.scheme) orelse return error.UnsupportedUriScheme;1594 return protocol_map.get(uri.scheme) orelse return error.UnsupportedUriScheme;
1595}
1596
1597fn validateUri(uri: Uri, arena: Allocator) !struct { Connection.Protocol, Uri } {
1598 const protocol = try uriProtocol(uri);
1580 var valid_uri = uri;1599 var valid_uri = uri;
1581 // The host is always going to be needed as a raw string for hostname resolution anyway.1600 // The host is always going to be needed as a raw string for hostname resolution anyway.
1582 valid_uri.host = .{1601 valid_uri.host = .{
1583 .raw = try (uri.host orelse return error.UriMissingHost).toRawMaybeAlloc(arena),1602 .raw = try (uri.host orelse return error.UriMissingHost).toRawMaybeAlloc(arena),
1584 };1603 };
1604 valid_uri.port = uri.port orelse protocol.port();
1585 return .{ protocol, valid_uri };1605 return .{ protocol, valid_uri };
1586}1606}
15871607
1588fn uriPort(uri: Uri, protocol: Connection.Protocol) u16 {
1589 return uri.port orelse switch (protocol) {
1590 .plain => 80,
1591 .tls => 443,
1592 };
1593}
1594
1595/// Open a connection to the host specified by `uri` and prepare to send a HTTP request.1608/// Open a connection to the host specified by `uri` and prepare to send a HTTP request.
1596///1609///
1597/// `uri` must remain alive during the entire request.1610/// `uri` must remain alive during the entire request.
...@@ -1637,7 +1650,7 @@ pub fn open(...@@ -1637,7 +1650,7 @@ pub fn open(
1637 }1650 }
16381651
1639 const conn = options.connection orelse1652 const conn = options.connection orelse
1640 try client.connect(valid_uri.host.?.raw, uriPort(valid_uri, protocol), protocol);1653 try client.connect(valid_uri.host.?.raw, valid_uri.port.?, protocol);
16411654
1642 var req: Request = .{1655 var req: Request = .{
1643 .uri = valid_uri,1656 .uri = valid_uri,