| author | |
| committer | |
| log | b529a94e14725ea5f56ab4f48de0801c9f7e0b8b |
| tree | 3eac13091ac5d26efca1902609a572ecf8c0a2a0 |
| parent | a6f7722b32bd9e0c1b86a978adcb5db270a23d59 |
Before this commit, Uri was sometimes using HostName.validate for `host` (in resolveInPlace) and sometimes not (in parseAfterScheme). On its own, this was a problem, but the bigger problem is that RFC3986 (Uri) has a much different idea of what a valid host name is than RFC1123 (HostName), and so just making Uri consistently use HostName.validate would make it less useful overall.
Instead, all `HostName`-related stuff has been removed from `Uri`. `Uri.getHost` has been moved to `HostName.fromUri` (without a graceful deprecation, since the semantics are different enough for users to need to evaluate usage sites), while `Uri.getHostAlloc` has been removed entirely.3 files changed, 30 insertions(+), 46 deletions(-)
lib/std/Io/net/HostName.zig+12| ... | ... | @@ -19,6 +19,18 @@ bytes: []const u8, |
| 19 | 19 | |
| 20 | 20 | pub const max_len = 255; |
| 21 | 21 | |
| 22 | pub const FromUriError = error{UriMissingHost} || ValidateError; | |
| 23 | ||
| 24 | /// Returned `HostName.bytes` may point into `buffer` or `uri.host`. | |
| 25 | pub fn fromUri(uri: std.Uri, buffer: *[HostName.max_len]u8) FromUriError!HostName { | |
| 26 | const component = uri.host orelse return error.UriMissingHost; | |
| 27 | const bytes = component.toRaw(buffer) catch |err| switch (err) { | |
| 28 | error.NoSpaceLeft => return error.NameTooLong, | |
| 29 | }; | |
| 30 | try validate(bytes); | |
| 31 | return .{ .bytes = bytes }; | |
| 32 | } | |
| 33 | ||
| 22 | 34 | pub const ValidateError = error{ |
| 23 | 35 | NameTooLong, |
| 24 | 36 | InvalidHostName, |
lib/std/Uri.zig+5-42| ... | ... | @@ -7,43 +7,18 @@ const testing = std.testing; |
| 7 | 7 | const Uri = @This(); |
| 8 | 8 | const Allocator = std.mem.Allocator; |
| 9 | 9 | const Writer = std.Io.Writer; |
| 10 | const HostName = std.Io.net.HostName; | |
| 11 | 10 | |
| 12 | 11 | scheme: []const u8, |
| 13 | 12 | user: ?Component = null, |
| 14 | 13 | password: ?Component = null, |
| 15 | /// If non-null, already validated. | |
| 16 | 14 | host: ?Component = null, |
| 17 | 15 | port: ?u16 = null, |
| 18 | 16 | path: Component = Component.empty, |
| 19 | 17 | query: ?Component = null, |
| 20 | 18 | fragment: ?Component = null, |
| 21 | 19 | |
| 22 | pub const GetHostError = error{UriMissingHost}; | |
| 23 | ||
| 24 | /// Returned value may point into `buffer` or be the original string. | |
| 25 | /// | |
| 26 | /// See also: | |
| 27 | /// * `getHostAlloc` | |
| 28 | pub fn getHost(uri: Uri, buffer: *[HostName.max_len]u8) GetHostError!HostName { | |
| 29 | const component = uri.host orelse return error.UriMissingHost; | |
| 30 | const bytes = component.toRaw(buffer) catch |err| switch (err) { | |
| 31 | error.NoSpaceLeft => unreachable, // `host` already validated. | |
| 32 | }; | |
| 33 | return .{ .bytes = bytes }; | |
| 34 | } | |
| 35 | ||
| 36 | pub const GetHostAllocError = GetHostError || error{OutOfMemory}; | |
| 37 | ||
| 38 | /// Returned value may point into `buffer` or be the original string. | |
| 39 | /// | |
| 40 | /// See also: | |
| 41 | /// * `getHost` | |
| 42 | pub fn getHostAlloc(uri: Uri, arena: Allocator) GetHostAllocError!HostName { | |
| 43 | const component = uri.host orelse return error.UriMissingHost; | |
| 44 | const bytes = try component.toRawMaybeAlloc(arena); | |
| 45 | return .{ .bytes = bytes }; | |
| 46 | } | |
| 20 | pub const getHost = @compileError("This function has been moved to std.Io.net.HostName.fromUri"); | |
| 21 | pub const getHostAlloc = @compileError("This function has been deleted. See std.Io.net.HostName.fromUri instead"); | |
| 47 | 22 | |
| 48 | 23 | pub const Component = union(enum) { |
| 49 | 24 | /// Invalid characters in this component must be percent encoded |
| ... | ... | @@ -403,7 +378,7 @@ pub fn resolveInPlace(base: Uri, new_len: usize, aux_buf: *[]u8) ResolveInPlaceE |
| 403 | 378 | .scheme = new_parsed.scheme, |
| 404 | 379 | .user = new_parsed.user, |
| 405 | 380 | .password = new_parsed.password, |
| 406 | .host = try validateHostComponent(new_parsed.host), | |
| 381 | .host = new_parsed.host, | |
| 407 | 382 | .port = new_parsed.port, |
| 408 | 383 | .path = remove_dot_segments(new_path), |
| 409 | 384 | .query = new_parsed.query, |
| ... | ... | @@ -414,7 +389,7 @@ pub fn resolveInPlace(base: Uri, new_len: usize, aux_buf: *[]u8) ResolveInPlaceE |
| 414 | 389 | .scheme = base.scheme, |
| 415 | 390 | .user = new_parsed.user, |
| 416 | 391 | .password = new_parsed.password, |
| 417 | .host = try validateHostComponent(host), | |
| 392 | .host = host, | |
| 418 | 393 | .port = new_parsed.port, |
| 419 | 394 | .path = remove_dot_segments(new_path), |
| 420 | 395 | .query = new_parsed.query, |
| ... | ... | @@ -436,7 +411,7 @@ pub fn resolveInPlace(base: Uri, new_len: usize, aux_buf: *[]u8) ResolveInPlaceE |
| 436 | 411 | .scheme = base.scheme, |
| 437 | 412 | .user = base.user, |
| 438 | 413 | .password = base.password, |
| 439 | .host = try validateHostComponent(base.host), | |
| 414 | .host = base.host, | |
| 440 | 415 | .port = base.port, |
| 441 | 416 | .path = path, |
| 442 | 417 | .query = query, |
| ... | ... | @@ -444,18 +419,6 @@ pub fn resolveInPlace(base: Uri, new_len: usize, aux_buf: *[]u8) ResolveInPlaceE |
| 444 | 419 | }; |
| 445 | 420 | } |
| 446 | 421 | |
| 447 | fn validateHostComponent(optional_component: ?Component) error{InvalidHostName}!?Component { | |
| 448 | const component = optional_component orelse return null; | |
| 449 | switch (component) { | |
| 450 | .raw => |raw| HostName.validate(raw) catch return error.InvalidHostName, | |
| 451 | .percent_encoded => |encoded| { | |
| 452 | // TODO validate decoded name instead | |
| 453 | HostName.validate(encoded) catch return error.InvalidHostName; | |
| 454 | }, | |
| 455 | } | |
| 456 | return component; | |
| 457 | } | |
| 458 | ||
| 459 | 422 | /// In-place implementation of RFC 3986, Section 5.2.4. |
| 460 | 423 | fn remove_dot_segments(path: []u8) Component { |
| 461 | 424 | var in_i: usize = 0; |
lib/std/http/Client.zig+13-4| ... | ... | @@ -1229,7 +1229,11 @@ pub const Request = struct { |
| 1229 | 1229 | const old_connection = r.connection.?; |
| 1230 | 1230 | const old_host = old_connection.host(); |
| 1231 | 1231 | var new_host_name_buffer: [HostName.max_len]u8 = undefined; |
| 1232 | const new_host = try new_uri.getHost(&new_host_name_buffer); | |
| 1232 | const new_host = HostName.fromUri(new_uri, &new_host_name_buffer) catch |err| switch (err) { | |
| 1233 | error.UriMissingHost => return error.HttpRedirectLocationInvalid, | |
| 1234 | error.InvalidHostName => return error.HttpRedirectLocationInvalid, | |
| 1235 | error.NameTooLong => return error.HttpRedirectLocationOversize, | |
| 1236 | }; | |
| 1233 | 1237 | const keep_privileged_headers = |
| 1234 | 1238 | std.ascii.eqlIgnoreCase(r.uri.scheme, new_uri.scheme) and |
| 1235 | 1239 | old_host.sameParentDomain(new_host); |
| ... | ... | @@ -1349,7 +1353,8 @@ fn createProxyFromEnvVar( |
| 1349 | 1353 | |
| 1350 | 1354 | const uri = Uri.parse(content) catch try Uri.parseAfterScheme("http", content); |
| 1351 | 1355 | const protocol = Protocol.fromUri(uri) orelse return null; |
| 1352 | const raw_host = try uri.getHostAlloc(arena); | |
| 1356 | var host_buf: [HostName.max_len]u8 = undefined; | |
| 1357 | const raw_host = try HostName.fromUri(uri, &host_buf); | |
| 1353 | 1358 | |
| 1354 | 1359 | const authorization: ?[]const u8 = if (uri.user != null or uri.password != null) a: { |
| 1355 | 1360 | const authorization = try arena.alloc(u8, basic_authorization.valueLengthFromUri(uri)); |
| ... | ... | @@ -1360,7 +1365,7 @@ fn createProxyFromEnvVar( |
| 1360 | 1365 | const proxy = try arena.create(Proxy); |
| 1361 | 1366 | proxy.* = .{ |
| 1362 | 1367 | .protocol = protocol, |
| 1363 | .host = raw_host, | |
| 1368 | .host = .{ .bytes = try arena.dupe(u8, raw_host.bytes) }, | |
| 1364 | 1369 | .authorization = authorization, |
| 1365 | 1370 | .port = uriPort(uri, protocol), |
| 1366 | 1371 | .supports_connect = true, |
| ... | ... | @@ -1624,6 +1629,7 @@ pub fn connect( |
| 1624 | 1629 | pub const RequestError = ConnectTcpError || error{ |
| 1625 | 1630 | UnsupportedUriScheme, |
| 1626 | 1631 | UriMissingHost, |
| 1632 | InvalidHostName, | |
| 1627 | 1633 | CertificateBundleLoadFailure, |
| 1628 | 1634 | }; |
| 1629 | 1635 | |
| ... | ... | @@ -1721,7 +1727,10 @@ pub fn request( |
| 1721 | 1727 | |
| 1722 | 1728 | const connection = options.connection orelse c: { |
| 1723 | 1729 | var host_name_buffer: [HostName.max_len]u8 = undefined; |
| 1724 | const host_name = try uri.getHost(&host_name_buffer); | |
| 1730 | const host_name = HostName.fromUri(uri, &host_name_buffer) catch |err| switch (err) { | |
| 1731 | error.UriMissingHost => |e| return e, | |
| 1732 | error.NameTooLong, error.InvalidHostName => return error.InvalidHostName, | |
| 1733 | }; | |
| 1725 | 1734 | break :c try client.connect(host_name, uriPort(uri, protocol), protocol); |
| 1726 | 1735 | }; |
| 1727 | 1736 |