diff --git a/lib/std/Uri.zig b/lib/std/Uri.zig index 2f28402b0a..ee0c602125 100644 --- a/lib/std/Uri.zig +++ b/lib/std/Uri.zig @@ -242,6 +242,9 @@ pub const WriteToStreamOptions = struct { /// When true, include the fragment part of the URI. Ignored when `path` is false. fragment: bool = false, + + /// When true, include the port part of the URI. Ignored when `port` is null. + port: bool = true, }; pub fn writeToStream( @@ -267,7 +270,9 @@ pub fn writeToStream( } if (uri.host) |host| { try writer.print("{host}", .{host}); - if (uri.port) |port| try writer.print(":{d}", .{port}); + if (options.port) { + if (uri.port) |port| try writer.print(":{d}", .{port}); + } } } if (options.path) { diff --git a/lib/std/http/Client.zig b/lib/std/http/Client.zig index 837bdc63c7..4110a31037 100644 --- a/lib/std/http/Client.zig +++ b/lib/std/http/Client.zig @@ -218,7 +218,17 @@ pub const Connection = struct { pub const buffer_size = std.crypto.tls.max_ciphertext_record_len; const BufferSize = std.math.IntFittingRange(0, buffer_size); - pub const Protocol = enum { plain, tls }; + pub const Protocol = enum { + plain, + tls, + + pub fn port(p: Protocol) u16 { + return switch (p) { + .plain => 80, + .tls => 443, + }; + } + }; pub fn readvDirectTls(conn: *Connection, buffers: []std.posix.iovec) ReadError!usize { return conn.tls_client.readv(conn.stream, buffers) catch |err| { @@ -805,7 +815,7 @@ pub const Request = struct { } req.uri = valid_uri; - req.connection = try req.client.connect(new_host, uriPort(valid_uri, protocol), protocol); + req.connection = try req.client.connect(new_host, valid_uri.port.?, protocol); req.redirect_behavior.subtractOne(); req.response.parser.reset(); @@ -847,8 +857,13 @@ pub const Request = struct { try w.writeAll("\r\n"); if (try emitOverridableHeader("host: ", req.headers.host, w)) { + // URI has already been validated so this cannot fail. + const default_port = (uriProtocol(req.uri) catch unreachable).port(); try w.writeAll("host: "); - try req.uri.writeToStream(.{ .authority = true }, w); + try req.uri.writeToStream(.{ + .authority = true, + .port = req.uri.port.? != default_port, + }, w); try w.writeAll("\r\n"); } @@ -1264,7 +1279,7 @@ fn createProxyFromEnvVar(arena: Allocator, env_var_names: []const []const u8) !? .protocol = protocol, .host = valid_uri.host.?.raw, .authorization = authorization, - .port = uriPort(valid_uri, protocol), + .port = valid_uri.port.?, .supports_connect = true, }; return proxy; @@ -1569,29 +1584,27 @@ pub const RequestOptions = struct { privileged_headers: []const http.Header = &.{}, }; -fn validateUri(uri: Uri, arena: Allocator) !struct { Connection.Protocol, Uri } { +fn uriProtocol(uri: Uri) !Connection.Protocol { const protocol_map = std.ComptimeStringMap(Connection.Protocol, .{ .{ "http", .plain }, .{ "ws", .plain }, .{ "https", .tls }, .{ "wss", .tls }, }); - const protocol = protocol_map.get(uri.scheme) orelse return error.UnsupportedUriScheme; + return protocol_map.get(uri.scheme) orelse return error.UnsupportedUriScheme; +} + +fn validateUri(uri: Uri, arena: Allocator) !struct { Connection.Protocol, Uri } { + const protocol = try uriProtocol(uri); var valid_uri = uri; // The host is always going to be needed as a raw string for hostname resolution anyway. valid_uri.host = .{ .raw = try (uri.host orelse return error.UriMissingHost).toRawMaybeAlloc(arena), }; + valid_uri.port = uri.port orelse protocol.port(); return .{ protocol, valid_uri }; } -fn uriPort(uri: Uri, protocol: Connection.Protocol) u16 { - return uri.port orelse switch (protocol) { - .plain => 80, - .tls => 443, - }; -} - /// Open a connection to the host specified by `uri` and prepare to send a HTTP request. /// /// `uri` must remain alive during the entire request. @@ -1637,7 +1650,7 @@ pub fn open( } const conn = options.connection orelse - try client.connect(valid_uri.host.?.raw, uriPort(valid_uri, protocol), protocol); + try client.connect(valid_uri.host.?.raw, valid_uri.port.?, protocol); var req: Request = .{ .uri = valid_uri,