From d4423c228d4eb0a749fec2db387df8b5bf522d3e Mon Sep 17 00:00:00 2001 From: Meghan Denny Date: Tue, 3 Feb 2026 20:51:29 -0800 Subject: [PATCH] pass parsePass with base --- generate.ts | 2 - url.zig | 220 +++++++++++++++++++++++++++----------- zig-out/test.zig | 268 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 428 insertions(+), 62 deletions(-) diff --git a/generate.ts b/generate.ts index 9e1172d1fae3544e47fddee21dcbb7ebc6f6344c..afedac373eeab6c6e17acf905da4fa5525df111b 100644 --- a/generate.ts +++ b/generate.ts @@ -130,8 +130,6 @@ for (const c of cases) { } w.write(`\n`); -// prettier-ignore -if (false) for (const c of cases) { if (c.base === null) continue; if (c.failure) continue; diff --git a/url.zig b/url.zig index 6cb26477c186ccebf7d490f07a4debad75bf53c2..07e69f65e209b242e8c810a7e0922b709b47d7c0 100644 --- a/url.zig +++ b/url.zig @@ -16,6 +16,7 @@ pub const URL = struct { pathname: []const u8, search: []const u8, hash: []const u8, + has_opaque_path: bool, pub const HostKind = enum { unset, @@ -36,13 +37,13 @@ pub const URL = struct { if (base) |b| { const b_url = try parseBasic(alloc, b, null, null); defer alloc.free(b_url.href); - return parseBasic(alloc, input, b_url, null); + return parseBasic(alloc, input, &b_url, null); } - return parseBasic(alloc, input, if (base) |b| try parseBasic(alloc, b, null, null) else null, null); + return parseBasic(alloc, input, null, null); } /// https://url.spec.whatwg.org/#concept-basic-url-parser - fn parseBasic(alloc: std.mem.Allocator, input: []const u8, base: ?URL, state_override: ?BasicParserState) error{ InvalidURL, OutOfMemory }!URL { + fn parseBasic(alloc: std.mem.Allocator, input: []const u8, base: ?*const URL, state_override: ?BasicParserState) error{ InvalidURL, OutOfMemory }!URL { // input is a scalar value string if (!std.unicode.utf8ValidateSlice(input)) return error.InvalidURL; @@ -93,6 +94,7 @@ pub const URL = struct { var href = ManyArrayList(8 + 7, u8).init(alloc); defer href.deinit(); var hostname_kind: HostKind = .unset; + var has_opaque_path = false; // scheme // : // // @@ -164,9 +166,11 @@ pub const URL = struct { } // 6. Otherwise, if url is special, base is non-null, and base’s scheme is url’s scheme: else if (isSchemeSpecial(href.items(0)) and base != null and std.mem.eql(u8, base.?.scheme(), href.items(0))) { - @panic("TODO"); // 1. Assert: base is special (and therefore does not have an opaque path). + std.debug.assert(base.?.isSpecial()); + std.debug.assert(!base.?.has_opaque_path); // 2. Set state to special relative or authority state. + state = .special_relative_or_authority; } // 7. Otherwise, if url is special, set state to special authority slashes state. else if (isSchemeSpecial(href.items(0))) { @@ -182,6 +186,7 @@ pub const URL = struct { // 9. Otherwise, set url’s path to the empty string and set state to opaque path state. else { href.clear(10); + has_opaque_path = true; state = .opaque_path; } } @@ -192,6 +197,7 @@ pub const URL = struct { pointer = 0; i = 0; c = inputl.items[i]; + continue; } // 4. Otherwise, return failure. else { @@ -200,11 +206,38 @@ pub const URL = struct { }, .no_scheme => { // 1. If base is null, or base has an opaque path and c is not U+0023 (#), missing-scheme-non-relative-URL validation error, return failure. - if (base == null) return error.InvalidURL; + if (base == null or (base != null and base.?.has_opaque_path and c != '#')) { + return error.InvalidURL; + } // 2. Otherwise, if base has an opaque path and c is U+0023 (#), set url’s scheme to base’s scheme, url’s path to base’s path, url’s query to base’s query, url’s fragment to the empty string, and set state to fragment state. + else if (base.?.has_opaque_path and c == '#') { + try href.set(0, base.?.scheme()); + try href.set(1, ":"); + try href.set(7, base.?.hostname); + hostname_kind = base.?.hostname_kind; + try href.set(10, base.?.pathname); + has_opaque_path = base.?.has_opaque_path; + try href.set(12, base.?.query()); + try href.set(13, "#"); + try href.set(14, ""); + state = .fragment; + } // 3. Otherwise, if base’s scheme is not "file", set state to relative state and decrease pointer by 1. + else if (!std.mem.eql(u8, base.?.scheme(), "file")) { + state = .relative; + if (pointer == 0) continue; // pointer goes to -1 then +1'd + pointer -= 1; + i = lastcpi(inputl.items[0..i]); + c = inputl.items[i]; + } // 4. Otherwise, set state to file state and decrease pointer by 1. - return error.InvalidURL; + else { + state = .file; + if (pointer == 0) continue; // pointer goes to -1 then +1'd + pointer -= 1; + i = lastcpi(inputl.items[0..i]); + c = inputl.items[i]; + } }, .special_relative_or_authority => { // 1. If c is U+002F (/) and remaining starts with U+002F (/), then set state to special authority ignore slashes state and increase pointer by 1. @@ -217,7 +250,9 @@ pub const URL = struct { // 2. Otherwise, special-scheme-missing-following-solidus validation error, set state to relative state and decrease pointer by 1. else { state = .relative; - @panic("TODO"); + pointer -= 1; + i = lastcpi(inputl.items[0..i]); + c = inputl.items[i]; } }, .path_or_authority => { @@ -231,7 +266,6 @@ pub const URL = struct { pointer -= 1; i = lastcpi(inputl.items[0..i]); c = inputl.items[i]; - try href.appendSlice(10, "/"); } }, .relative => { @@ -251,12 +285,14 @@ pub const URL = struct { // 5. Otherwise: else { // 1. Set url’s username to base’s username, url’s password to base’s password, url’s host to base’s host, url’s port to base’s port, url’s path to a clone of base’s path, and url’s query to base’s query. - // try username.appendSlice(base.?.username); - // try password.appendSlice(base.?.password); - // host = base.?.host; - // try port.writer().print("{d}", .{base.?.port.?}); - // try path.appendSlice(base.?.path); - // try query.appendSlice(base.?.query.?); + try href.set(3, base.?.username); + try href.set(5, base.?.password); + try href.set(7, base.?.hostname); + hostname_kind = base.?.hostname_kind; + try href.set(9, base.?.port); + try href.set(10, base.?.pathname); + has_opaque_path = base.?.has_opaque_path; + try href.set(12, base.?.query()); // 2. If c is U+003F (?), then set url’s query to the empty string, and state to query state. if (c == '?') { href.clear(12); @@ -274,12 +310,13 @@ pub const URL = struct { // 1. Set url’s query to null. href.clear(12); // 2. Shorten url’s path. - @panic("TODO"); + shortenUrlPath(&href, has_opaque_path); // 3. Set state to path state and decrease pointer by 1. - // state = .path; - // pointer -= 1; - // i = lastcpi(inputl.items[0..i]); - // c = inputl.items[i]; + state = .path; + if (pointer == 0) continue; // pointer goes to -1 then +1'd + pointer -= 1; + i = lastcpi(inputl.items[0..i]); + c = inputl.items[i]; } } }, @@ -296,15 +333,16 @@ pub const URL = struct { } // 3. Otherwise, set url’s username to base’s username, url’s password to base’s password, url’s host to base’s host, url’s port to base’s port, state to path state, and then, decrease pointer by 1. else { - // try username.appendSlice(base.?.username); - // try password.appendSlice(base.?.password); - // host = base.?.host; - // try port.writer().print("{d}", .{base.?.port.?}); + try href.set(3, base.?.username); + try href.set(5, base.?.password); + try href.set(7, base.?.hostname); + hostname_kind = base.?.hostname_kind; + try href.set(9, base.?.port); state = .path; + if (pointer == 0) continue; // pointer goes to -1 then +1'd pointer -= 1; i = lastcpi(inputl.items[0..i]); c = inputl.items[i]; - @panic("TODO"); } }, .special_authority_slashes => { @@ -340,6 +378,7 @@ pub const URL = struct { // 1. If c is U+0040 (@), then: if (c == '@') { // 1. Invalid-credentials validation error. + {} // 2. If atSignSeen is true, then prepend "%40" to buffer. if (atSignSeen) try buffer.insertSlice(0, "%40"); // 3. Set atSignSeen to true. @@ -496,9 +535,11 @@ pub const URL = struct { // 4. Otherwise, if base is non-null and base’s scheme is "file": else if (base != null and std.mem.eql(u8, base.?.scheme(), "file")) { // 1. Set url’s host to base’s host, url’s path to a clone of base’s path, and url’s query to base’s query. - // host = base.?.host; - // try path.appendSlice(base.?.path.?); - // try query.appendSlice(base.?.query.?); + try href.set(7, base.?.hostname); + hostname_kind = base.?.hostname_kind; + try href.set(10, base.?.pathname); + has_opaque_path = base.?.has_opaque_path; + try href.set(12, base.?.query()); // 2. If c is U+003F (?), then set url’s query to the empty string and state to query state. if (c == '?') { href.clear(12); @@ -516,15 +557,20 @@ pub const URL = struct { // 1. Set url’s query to null. href.clear(12); // 2. If the code point substring from pointer to the end of input does not start with a Windows drive letter, then shorten url’s path. + if (!startsWithWindowsDriveLetter(inputl.items[i..])) { + shortenUrlPath(&href, has_opaque_path); + } // 3. Otherwise: // This is a (platform-independent) Windows drive letter quirk. - if (builtin.target.os.tag == .windows) @compileError("TODO"); - { + else { // 1. File-invalid-Windows-drive-letter validation error. + {} // 2. Set url’s path to « ». + href.clear(10); } // 4. Set state to path state and decrease pointer by 1. state = .path; + if (pointer == 0) continue; // pointer goes to -1 then +1'd pointer -= 1; i = lastcpi(inputl.items[0..i]); c = inputl.items[i]; @@ -536,7 +582,6 @@ pub const URL = struct { pointer -= 1; i = lastcpi(inputl.items[0..i]); c = inputl.items[i]; - try href.appendSlice(10, "/"); } }, .file_slash => { @@ -552,17 +597,24 @@ pub const URL = struct { // 1. If base is non-null and base’s scheme is "file", then: if (base != null and std.mem.eql(u8, base.?.scheme(), "file")) { // 1. Set url’s host to base’s host. - // host = base.?.host; + try href.set(7, base.?.hostname); + hostname_kind = base.?.hostname_kind; // 2. If the code point substring from pointer to the end of input does not start with a Windows drive letter and base’s path[0] is a normalized Windows drive letter, then append base’s path[0] to url’s path. // > This is a (platform-independent) Windows drive letter quirk. - if (builtin.target.os.tag == .windows) @compileError("TODO"); + if (!startsWithWindowsDriveLetter(inputl.items[i..])) { + const base_path0 = nthScalarItem(u8, base.?.pathname, '/', 1); + if (isNormalizedWindowsDriveLetter(base_path0)) { + try href.appendSlice(10, "/"); + try href.appendSlice(10, base_path0); + } + } } // 2. Set state to path state, and decrease pointer by 1. state = .path; + if (pointer == 0) continue; // pointer goes to -1 then +1'd pointer -= 1; i = lastcpi(inputl.items[0..i]); c = inputl.items[i]; - try href.appendSlice(10, "/"); } }, .file_host => { @@ -575,7 +627,6 @@ pub const URL = struct { // > This is a (platform-independent) Windows drive letter quirk. buffer is not reset here and instead used in the path state. if (state_override == null and isWindowsDriveLetter(buffer.items)) { state = .path; - try href.appendSlice(10, "/"); } // 2. Otherwise, if buffer is the empty string, then: else if (buffer.items.len == 0) { @@ -626,7 +677,6 @@ pub const URL = struct { i = lastcpi(inputl.items[0..i]); c = inputl.items[i]; } - try href.appendSlice(10, "/"); } // 2. Otherwise, if state override is not given and c is U+003F (?), set url’s query to the empty string and state to query state. else if (state_override == null and c == '?') { @@ -649,8 +699,6 @@ pub const URL = struct { pointer -= 1; i = lastcpi(inputl.items[0..i]); c = inputl.items[i]; - } else { - try href.appendSlice(10, &.{c}); } } // 5. Otherwise, if state override is given and url’s host is null, append the empty string to url’s path. @@ -671,29 +719,28 @@ pub const URL = struct { {} // 2. If buffer is a double-dot URL path segment, then: if (isDoubleDotPathSeg(buffer.items)) { - const olen = href.lengths[10]; - const idx = std.mem.lastIndexOfScalar(u8, href.items(10)[0 .. olen - 1], '/') orelse 0; - try href.replace(10, idx + 1, olen - idx - 1, ""); // 1. Shorten url’s path. + shortenUrlPath(&href, has_opaque_path); // 2. If neither c is U+002F (/), nor url is special and c is U+005C (\), append the empty string to url’s path. // > This means that for input /usr/.. the result is / and not a lack of a path. + if (!is_lsep and !is_rsep) { + try href.appendSlice(10, "/"); + } } // 3. Otherwise, if buffer is a single-dot URL path segment and if neither c is U+002F (/), nor url is special and c is U+005C (\), append the empty string to url’s path. - else if (isSingleDotPathSeg(buffer.items) and (c != '/' or !(is_rsep))) { - // =no action needed + else if (isSingleDotPathSeg(buffer.items) and !is_lsep and !is_rsep) { + try href.appendSlice(10, "/"); } // 4. Otherwise, if buffer is not a single-dot URL path segment, then: else if (!isSingleDotPathSeg(buffer.items)) { // 1. If url’s scheme is "file", url’s path is empty, and buffer is a Windows drive letter, then replace the second code point in buffer with U+003A (:). // > This is a (platform-independent) Windows drive letter quirk. - if (std.mem.eql(u8, href.items(0), "file") and href.lengths[10] <= 1 and isWindowsDriveLetter(buffer.items)) { + if (std.mem.eql(u8, href.items(0), "file") and href.lengths[10] == 0 and isWindowsDriveLetter(buffer.items)) { buffer.items[1] = ':'; } // 2. Append buffer to url’s path. - // if (!std.mem.endsWith(u8, href.items(10), "/")) try href.appendSlice(10, "/"); + try href.appendSlice(10, "/"); try href.appendSlice(10, buffer.items); - if (is_lsep) try href.appendSlice(10, "/"); - if (is_rsep) try href.appendSlice(10, "/"); } // 5. Set buffer to the empty string. buffer.clearRetainingCapacity(); @@ -766,6 +813,7 @@ pub const URL = struct { // - url is not special // - url’s scheme is "ws" or "wss" // then set encoding to UTF-8. + {} // 2. If one of the following is true: // - state override is not given and c is U+0023 (#) // - c is the EOF code point @@ -812,14 +860,10 @@ pub const URL = struct { } // If after a run pointer points to the EOF code point, go to the next step. Otherwise, increase pointer by 1 and continue with the state machine. - if (i == length) { - if (state == .fragment) break; - state = @enumFromInt(@intFromEnum(state) + 1); - } else { - i += l(c); - c = if (i < length) inputl.items[i] else 0; - pointer += 1; - } + if (i == length) break; + i += l(c); + c = if (i < length) inputl.items[i] else 0; + pointer += 1; } if (hostname_kind != .unset) { @@ -834,6 +878,12 @@ pub const URL = struct { if (href.lengths[9] > 0) { try href.set(8, ":"); } + if (href.lengths[12] > 0) { + try href.set(11, "?"); + } + if (href.lengths[14] > 0) { + try href.set(13, "#"); + } var path_offset: usize = 0; if (hostname_kind == .unset and std.mem.startsWith(u8, href.items(10), "//")) { @@ -855,6 +905,7 @@ pub const URL = struct { .pathname = _href[extras.sum(usize, href.lengths[0..10])..][0..href.lengths[10]][path_offset..], .search = if (href.lengths[12] == 0) "" else _href[extras.sum(usize, href.lengths[0..11])..][0..extras.sum(usize, href.lengths[11..][0..2])], .hash = if (href.lengths[14] == 0) "" else _href[extras.sum(usize, href.lengths[0..13])..][0..extras.sum(usize, href.lengths[13..][0..2])], + .has_opaque_path = has_opaque_path, }; return url; } @@ -890,6 +941,16 @@ pub const URL = struct { pub fn scheme(u: *const URL) []const u8 { return u.protocol[0 .. u.protocol.len - 1]; } + + pub fn query(u: *const URL) []const u8 { + if (u.search.len == 0) return ""; + return u.search[1..]; // search includes '?' + } + + pub fn fragment(u: *const URL) []const u8 { + if (u.hash.len == 0) return ""; + return u.hash[1..]; // hash includes '#' + } }; /// https://url.spec.whatwg.org/#special-scheme @@ -938,6 +999,24 @@ fn isWindowsDriveLetter(buffer: []const u8) bool { return true; } +/// https://url.spec.whatwg.org/#normalized-windows-drive-letter +fn isNormalizedWindowsDriveLetter(buffer: []const u8) bool { + if (buffer.len != 2) return false; + if (!std.ascii.isAlphabetic(buffer[0])) return false; + if (!(buffer[1] == ':')) return false; + return true; +} + +/// https://url.spec.whatwg.org/#start-with-a-windows-drive-letter +fn startsWithWindowsDriveLetter(buffer: []const u8) bool { + if (buffer.len < 2) return false; + if (!std.ascii.isAlphabetic(buffer[0])) return false; + if (!(buffer[1] == ':' or buffer[1] == '|')) return false; + if (buffer.len == 2) return true; + if (!(buffer[2] == '/' or buffer[2] == '\\' or buffer[2] == '?' or buffer[2] == '#')) return false; + return true; +} + /// https://url.spec.whatwg.org/#concept-host-parser fn parseHost(allocator: std.mem.Allocator, input: []u8, isOpaque: bool) !URL.Host { // 1. If input starts with U+005B ([), then: @@ -963,7 +1042,7 @@ fn parseHost(allocator: std.mem.Allocator, input: []u8, isOpaque: bool) !URL.Hos // 7. If asciiDomain ends in a number, then return the result of IPv4 parsing asciiDomain. if (endsInANumber(asciidomain)) { defer allocator.free(asciidomain); - const adr = try parseIPv4(input); + const adr = try parseIPv4(asciidomain); return .{ .ipv4 = adr }; } // 8. Return asciiDomain. @@ -1031,7 +1110,7 @@ fn parseIPv6(input: []const u8) !u128 { // 4. Let numbersSeen be 0. var numbersSeen: usize = 0; // 5. While c is not the EOF code point: - if (pointer < input.len) { + while (pointer < input.len) { // 1. Let ipv4Piece be null. var ipv4Piece: ?u16 = null; // 2. If numbersSeen is greater than 0, then: @@ -1046,9 +1125,9 @@ fn parseIPv6(input: []const u8) !u128 { } } // 3. If c is not an ASCII digit, IPv4-in-IPv6-invalid-code-point validation error, return failure. - if (!std.ascii.isDigit(input[pointer])) return error.InvalidURL; + if (pointer == input.len or !std.ascii.isDigit(input[pointer])) return error.InvalidURL; // 4. While c is an ASCII digit: - while (std.ascii.isDigit(input[pointer])) { + while (pointer < input.len and std.ascii.isDigit(input[pointer])) { // 1. Let number be c interpreted as decimal number. const dec_alpha = "0123456789"; const number: u8 = @intCast(std.mem.indexOfScalar(u8, dec_alpha, input[pointer]).?); @@ -1196,7 +1275,7 @@ fn endsInANumber(input: []const u8) bool { var end = input.len; var start: usize = if (std.mem.lastIndexOfScalar(u8, input[0..end], '.')) |i| i + 1 else 0; if (end - start == 0) { - if (std.mem.count(u8, input, ".") == 0) return false; + if (extras.countScalar(u8, input, '.') == 0) return false; end = start - 1; start = if (std.mem.lastIndexOfScalar(u8, input[0..end], '.')) |i| i + 1 else 0; } @@ -1210,7 +1289,7 @@ fn endsInANumber(input: []const u8) bool { fn parseIPv4(input: []const u8) !u32 { // 1. Let parts be the result of strictly splitting input on U+002E (.). var end = input.len; - var parts_size = std.mem.count(u8, input[0..end], ".") + 1; + var parts_size = extras.countScalar(u8, input[0..end], '.') + 1; // 2. If the last item in parts is the empty string, then: if (std.mem.endsWith(u8, input, ".")) { // 1. IPv4-empty-part validation error. @@ -1305,6 +1384,21 @@ fn parseIPv4Number(input_: []const u8, T: type) !T { return output; } +/// https://url.spec.whatwg.org/#shorten-a-urls-path +fn shortenUrlPath(url: *ManyArrayList(15, u8), has_opaque_path: bool) void { + // 1. Assert: url does not have an opaque path. + std.debug.assert(!has_opaque_path); + // 2. Let path be url’s path. + const path = url.items(10); + // 3. If url’s scheme is "file", path’s size is 1, and path[0] is a normalized Windows drive letter, then return. + if (std.mem.eql(u8, url.items(0), "file") and extras.countScalar(u8, path, '/') == 1 and isNormalizedWindowsDriveLetter(nthScalarItem(u8, path, '/', 1))) { + return; + } + // 4. Remove path’s last item, if any. + const new_len = std.mem.lastIndexOfScalar(u8, path, '/') orelse return; + url.replace(10, new_len, path.len - new_len, "") catch unreachable; +} + // // // @@ -1562,6 +1656,12 @@ pub fn replaceInPlace(comptime T: type, input: []T, needle: []const T, replaceme } return replacements; } +fn nthScalarItem(T: type, haystack: []const u8, needle: T, index: usize) []const T { + var it = std.mem.splitScalar(T, haystack, needle); + var idx: usize = 0; + while (idx < index) : (idx += 1) _ = it.next().?; + return it.next().?; +} pub fn ManyArrayList(N: usize, T: type) type { return struct { diff --git a/zig-out/test.zig b/zig-out/test.zig index ab9c75de824e363b1f9b73c7f2076ba1c84816ed..9dec53d3d41b4b0da0a11863416e674c969b7bc1 100644 --- a/zig-out/test.zig +++ b/zig-out/test.zig @@ -658,6 +658,274 @@ test { try parsePass("non-special:\\/opaque", null, "non-special:\\/opaque", "nu test { try parsePass("non-special:/\\path", null, "non-special:/\\path", "null", "non-special:", "", "", "", "", "", "/\\path", "", ""); } test { try parsePass("non-special://host/a\\b", null, "non-special://host/a\\b", "null", "non-special:", "", "", "host", "host", "", "/a\\b", "", ""); } +test { try parsePass("http://example\t.\norg", "http://example.org/foo/bar", "http://example.org/", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/", "", ""); } +test { try parsePass("http://user:pass@foo:21/bar;par?b#c", "http://example.org/foo/bar", "http://user:pass@foo:21/bar;par?b#c", "http://foo:21", "http:", "user", "pass", "foo:21", "foo", "21", "/bar;par", "?b", "#c"); } +test { try parsePass("http:foo.com", "http://example.org/foo/bar", "http://example.org/foo/foo.com", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/foo/foo.com", "", ""); } +test { try parsePass("\t :foo.com \n", "http://example.org/foo/bar", "http://example.org/foo/:foo.com", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/foo/:foo.com", "", ""); } +test { try parsePass(" foo.com ", "http://example.org/foo/bar", "http://example.org/foo/foo.com", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/foo/foo.com", "", ""); } +test { try parsePass("a:\t foo.com", "http://example.org/foo/bar", "a: foo.com", "null", "a:", "", "", "", "", "", " foo.com", "", ""); } +test { try parsePass("http://f:21/ b ? d # e ", "http://example.org/foo/bar", "http://f:21/%20b%20?%20d%20#%20e", "http://f:21", "http:", "", "", "f:21", "f", "21", "/%20b%20", "?%20d%20", "#%20e"); } +test { try parsePass("http://f:/c", "http://example.org/foo/bar", "http://f/c", "http://f", "http:", "", "", "f", "f", "", "/c", "", ""); } +test { try parsePass("http://f:0/c", "http://example.org/foo/bar", "http://f:0/c", "http://f:0", "http:", "", "", "f:0", "f", "0", "/c", "", ""); } +test { try parsePass("http://f:00000000000000/c", "http://example.org/foo/bar", "http://f:0/c", "http://f:0", "http:", "", "", "f:0", "f", "0", "/c", "", ""); } +test { try parsePass("http://f:00000000000000000000080/c", "http://example.org/foo/bar", "http://f/c", "http://f", "http:", "", "", "f", "f", "", "/c", "", ""); } +test { try parsePass("http://f:\n/c", "http://example.org/foo/bar", "http://f/c", "http://f", "http:", "", "", "f", "f", "", "/c", "", ""); } +test { try parsePass("", "http://example.org/foo/bar", "http://example.org/foo/bar", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/foo/bar", "", ""); } +test { try parsePass(" \t", "http://example.org/foo/bar", "http://example.org/foo/bar", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/foo/bar", "", ""); } +test { try parsePass(":foo.com/", "http://example.org/foo/bar", "http://example.org/foo/:foo.com/", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/foo/:foo.com/", "", ""); } +test { try parsePass(":foo.com\\", "http://example.org/foo/bar", "http://example.org/foo/:foo.com/", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/foo/:foo.com/", "", ""); } +test { try parsePass(":", "http://example.org/foo/bar", "http://example.org/foo/:", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/foo/:", "", ""); } +test { try parsePass(":a", "http://example.org/foo/bar", "http://example.org/foo/:a", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/foo/:a", "", ""); } +test { try parsePass(":/", "http://example.org/foo/bar", "http://example.org/foo/:/", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/foo/:/", "", ""); } +test { try parsePass(":\\", "http://example.org/foo/bar", "http://example.org/foo/:/", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/foo/:/", "", ""); } +test { try parsePass(":#", "http://example.org/foo/bar", "http://example.org/foo/:#", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/foo/:", "", ""); } +test { try parsePass("#", "http://example.org/foo/bar", "http://example.org/foo/bar#", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/foo/bar", "", ""); } +test { try parsePass("#/", "http://example.org/foo/bar", "http://example.org/foo/bar#/", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/foo/bar", "", "#/"); } +test { try parsePass("#\\", "http://example.org/foo/bar", "http://example.org/foo/bar#\\", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/foo/bar", "", "#\\"); } +test { try parsePass("#;?", "http://example.org/foo/bar", "http://example.org/foo/bar#;?", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/foo/bar", "", "#;?"); } +test { try parsePass("?", "http://example.org/foo/bar", "http://example.org/foo/bar?", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/foo/bar", "", ""); } +test { try parsePass("/", "http://example.org/foo/bar", "http://example.org/", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/", "", ""); } +test { try parsePass(":23", "http://example.org/foo/bar", "http://example.org/foo/:23", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/foo/:23", "", ""); } +test { try parsePass("/:23", "http://example.org/foo/bar", "http://example.org/:23", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/:23", "", ""); } +test { try parsePass("\\x", "http://example.org/foo/bar", "http://example.org/x", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/x", "", ""); } +test { try parsePass("\\\\x\\hello", "http://example.org/foo/bar", "http://x/hello", "http://x", "http:", "", "", "x", "x", "", "/hello", "", ""); } +test { try parsePass("::", "http://example.org/foo/bar", "http://example.org/foo/::", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/foo/::", "", ""); } +test { try parsePass("::23", "http://example.org/foo/bar", "http://example.org/foo/::23", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/foo/::23", "", ""); } +test { try parsePass("foo://", "http://example.org/foo/bar", "foo://", "null", "foo:", "", "", "", "", "", "", "", ""); } +test { try parsePass("http://a:b@c:29/d", "http://example.org/foo/bar", "http://a:b@c:29/d", "http://c:29", "http:", "a", "b", "c:29", "c", "29", "/d", "", ""); } +test { try parsePass("http::@c:29", "http://example.org/foo/bar", "http://example.org/foo/:@c:29", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/foo/:@c:29", "", ""); } +test { try parsePass("http://&a:foo(b]c@d:2/", "http://example.org/foo/bar", "http://&a:foo(b%5Dc@d:2/", "http://d:2", "http:", "&a", "foo(b%5Dc", "d:2", "d", "2", "/", "", ""); } +test { try parsePass("http://::@c@d:2", "http://example.org/foo/bar", "http://:%3A%40c@d:2/", "http://d:2", "http:", "", "%3A%40c", "d:2", "d", "2", "/", "", ""); } +test { try parsePass("http://foo.com:b@d/", "http://example.org/foo/bar", "http://foo.com:b@d/", "http://d", "http:", "foo.com", "b", "d", "d", "", "/", "", ""); } +test { try parsePass("http://foo.com/\\@", "http://example.org/foo/bar", "http://foo.com//@", "http://foo.com", "http:", "", "", "foo.com", "foo.com", "", "//@", "", ""); } +test { try parsePass("http:\\\\foo.com\\", "http://example.org/foo/bar", "http://foo.com/", "http://foo.com", "http:", "", "", "foo.com", "foo.com", "", "/", "", ""); } +test { try parsePass("http:\\\\a\\b:c\\d@foo.com\\", "http://example.org/foo/bar", "http://a/b:c/d@foo.com/", "http://a", "http:", "", "", "a", "a", "", "/b:c/d@foo.com/", "", ""); } +test { try parsePass("foo:/", "http://example.org/foo/bar", "foo:/", "null", "foo:", "", "", "", "", "", "/", "", ""); } +test { try parsePass("foo:/bar.com/", "http://example.org/foo/bar", "foo:/bar.com/", "null", "foo:", "", "", "", "", "", "/bar.com/", "", ""); } +test { try parsePass("foo://///////", "http://example.org/foo/bar", "foo://///////", "null", "foo:", "", "", "", "", "", "///////", "", ""); } +test { try parsePass("foo://///////bar.com/", "http://example.org/foo/bar", "foo://///////bar.com/", "null", "foo:", "", "", "", "", "", "///////bar.com/", "", ""); } +test { try parsePass("foo:////://///", "http://example.org/foo/bar", "foo:////://///", "null", "foo:", "", "", "", "", "", "//://///", "", ""); } +test { try parsePass("c:/foo", "http://example.org/foo/bar", "c:/foo", "null", "c:", "", "", "", "", "", "/foo", "", ""); } +test { try parsePass("//foo/bar", "http://example.org/foo/bar", "http://foo/bar", "http://foo", "http:", "", "", "foo", "foo", "", "/bar", "", ""); } +test { try parsePass("http://foo/path;a??e#f#g", "http://example.org/foo/bar", "http://foo/path;a??e#f#g", "http://foo", "http:", "", "", "foo", "foo", "", "/path;a", "??e", "#f#g"); } +test { try parsePass("http://foo/abcd?efgh?ijkl", "http://example.org/foo/bar", "http://foo/abcd?efgh?ijkl", "http://foo", "http:", "", "", "foo", "foo", "", "/abcd", "?efgh?ijkl", ""); } +test { try parsePass("http://foo/abcd#foo?bar", "http://example.org/foo/bar", "http://foo/abcd#foo?bar", "http://foo", "http:", "", "", "foo", "foo", "", "/abcd", "", "#foo?bar"); } +test { try parsePass("[61:24:74]:98", "http://example.org/foo/bar", "http://example.org/foo/[61:24:74]:98", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/foo/[61:24:74]:98", "", ""); } +test { try parsePass("http:[61:27]/:foo", "http://example.org/foo/bar", "http://example.org/foo/[61:27]/:foo", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/foo/[61:27]/:foo", "", ""); } +test { try parsePass("http://[2001::1]", "http://example.org/foo/bar", "http://[2001::1]/", "http://[2001::1]", "http:", "", "", "[2001::1]", "[2001::1]", "", "/", "", ""); } +test { try parsePass("http://[::127.0.0.1]", "http://example.org/foo/bar", "http://[::7f00:1]/", "http://[::7f00:1]", "http:", "", "", "[::7f00:1]", "[::7f00:1]", "", "/", "", ""); } +test { try parsePass("http://[0:0:0:0:0:0:13.1.68.3]", "http://example.org/foo/bar", "http://[::d01:4403]/", "http://[::d01:4403]", "http:", "", "", "[::d01:4403]", "[::d01:4403]", "", "/", "", ""); } +test { try parsePass("http://[2001::1]:80", "http://example.org/foo/bar", "http://[2001::1]/", "http://[2001::1]", "http:", "", "", "[2001::1]", "[2001::1]", "", "/", "", ""); } +test { try parsePass("http:/example.com/", "http://example.org/foo/bar", "http://example.org/example.com/", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/example.com/", "", ""); } +test { try parsePass("ftp:/example.com/", "http://example.org/foo/bar", "ftp://example.com/", "ftp://example.com", "ftp:", "", "", "example.com", "example.com", "", "/", "", ""); } +test { try parsePass("https:/example.com/", "http://example.org/foo/bar", "https://example.com/", "https://example.com", "https:", "", "", "example.com", "example.com", "", "/", "", ""); } +test { try parsePass("madeupscheme:/example.com/", "http://example.org/foo/bar", "madeupscheme:/example.com/", "null", "madeupscheme:", "", "", "", "", "", "/example.com/", "", ""); } +test { try parsePass("file:/example.com/", "http://example.org/foo/bar", "file:///example.com/", "", "file:", "", "", "", "", "", "/example.com/", "", ""); } +test { try parsePass("ftps:/example.com/", "http://example.org/foo/bar", "ftps:/example.com/", "null", "ftps:", "", "", "", "", "", "/example.com/", "", ""); } +test { try parsePass("gopher:/example.com/", "http://example.org/foo/bar", "gopher:/example.com/", "null", "gopher:", "", "", "", "", "", "/example.com/", "", ""); } +test { try parsePass("ws:/example.com/", "http://example.org/foo/bar", "ws://example.com/", "ws://example.com", "ws:", "", "", "example.com", "example.com", "", "/", "", ""); } +test { try parsePass("wss:/example.com/", "http://example.org/foo/bar", "wss://example.com/", "wss://example.com", "wss:", "", "", "example.com", "example.com", "", "/", "", ""); } +test { try parsePass("data:/example.com/", "http://example.org/foo/bar", "data:/example.com/", "null", "data:", "", "", "", "", "", "/example.com/", "", ""); } +test { try parsePass("javascript:/example.com/", "http://example.org/foo/bar", "javascript:/example.com/", "null", "javascript:", "", "", "", "", "", "/example.com/", "", ""); } +test { try parsePass("mailto:/example.com/", "http://example.org/foo/bar", "mailto:/example.com/", "null", "mailto:", "", "", "", "", "", "/example.com/", "", ""); } +test { try parsePass("http:example.com/", "http://example.org/foo/bar", "http://example.org/foo/example.com/", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/foo/example.com/", "", ""); } +test { try parsePass("ftp:example.com/", "http://example.org/foo/bar", "ftp://example.com/", "ftp://example.com", "ftp:", "", "", "example.com", "example.com", "", "/", "", ""); } +test { try parsePass("https:example.com/", "http://example.org/foo/bar", "https://example.com/", "https://example.com", "https:", "", "", "example.com", "example.com", "", "/", "", ""); } +test { try parsePass("madeupscheme:example.com/", "http://example.org/foo/bar", "madeupscheme:example.com/", "null", "madeupscheme:", "", "", "", "", "", "example.com/", "", ""); } +test { try parsePass("ftps:example.com/", "http://example.org/foo/bar", "ftps:example.com/", "null", "ftps:", "", "", "", "", "", "example.com/", "", ""); } +test { try parsePass("gopher:example.com/", "http://example.org/foo/bar", "gopher:example.com/", "null", "gopher:", "", "", "", "", "", "example.com/", "", ""); } +test { try parsePass("ws:example.com/", "http://example.org/foo/bar", "ws://example.com/", "ws://example.com", "ws:", "", "", "example.com", "example.com", "", "/", "", ""); } +test { try parsePass("wss:example.com/", "http://example.org/foo/bar", "wss://example.com/", "wss://example.com", "wss:", "", "", "example.com", "example.com", "", "/", "", ""); } +test { try parsePass("data:example.com/", "http://example.org/foo/bar", "data:example.com/", "null", "data:", "", "", "", "", "", "example.com/", "", ""); } +test { try parsePass("javascript:example.com/", "http://example.org/foo/bar", "javascript:example.com/", "null", "javascript:", "", "", "", "", "", "example.com/", "", ""); } +test { try parsePass("mailto:example.com/", "http://example.org/foo/bar", "mailto:example.com/", "null", "mailto:", "", "", "", "", "", "example.com/", "", ""); } +test { try parsePass("/a/b/c", "http://example.org/foo/bar", "http://example.org/a/b/c", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/a/b/c", "", ""); } +test { try parsePass("/a/ /c", "http://example.org/foo/bar", "http://example.org/a/%20/c", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/a/%20/c", "", ""); } +test { try parsePass("/a%2fc", "http://example.org/foo/bar", "http://example.org/a%2fc", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/a%2fc", "", ""); } +test { try parsePass("/a/%2f/c", "http://example.org/foo/bar", "http://example.org/a/%2f/c", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/a/%2f/c", "", ""); } +test { try parsePass("#\xce\xb2", "http://example.org/foo/bar", "http://example.org/foo/bar#%CE%B2", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/foo/bar", "", "#%CE%B2"); } +test { try parsePass("data:text/html,test#test", "http://example.org/foo/bar", "data:text/html,test#test", "null", "data:", "", "", "", "", "", "text/html,test", "", "#test"); } +test { try parsePass("tel:1234567890", "http://example.org/foo/bar", "tel:1234567890", "null", "tel:", "", "", "", "", "", "1234567890", "", ""); } +test { try parsePass("ssh://example.com/foo/bar.git", "http://example.org/", "ssh://example.com/foo/bar.git", "null", "ssh:", "", "", "example.com", "example.com", "", "/foo/bar.git", "", ""); } +test { try parsePass("file:c:\\foo\\bar.html", "file:///tmp/mock/path", "file:///c:/foo/bar.html", "", "file:", "", "", "", "", "", "/c:/foo/bar.html", "", ""); } +test { try parsePass(" File:c|////foo\\bar.html", "file:///tmp/mock/path", "file:///c:////foo/bar.html", "", "file:", "", "", "", "", "", "/c:////foo/bar.html", "", ""); } +test { try parsePass("C|/foo/bar", "file:///tmp/mock/path", "file:///C:/foo/bar", "", "file:", "", "", "", "", "", "/C:/foo/bar", "", ""); } +test { try parsePass("/C|\\foo\\bar", "file:///tmp/mock/path", "file:///C:/foo/bar", "", "file:", "", "", "", "", "", "/C:/foo/bar", "", ""); } +test { try parsePass("//C|/foo/bar", "file:///tmp/mock/path", "file:///C:/foo/bar", "", "file:", "", "", "", "", "", "/C:/foo/bar", "", ""); } +test { try parsePass("//server/file", "file:///tmp/mock/path", "file://server/file", "", "file:", "", "", "server", "server", "", "/file", "", ""); } +test { try parsePass("\\\\server\\file", "file:///tmp/mock/path", "file://server/file", "", "file:", "", "", "server", "server", "", "/file", "", ""); } +test { try parsePass("/\\server/file", "file:///tmp/mock/path", "file://server/file", "", "file:", "", "", "server", "server", "", "/file", "", ""); } +test { try parsePass("file:///foo/bar.txt", "file:///tmp/mock/path", "file:///foo/bar.txt", "", "file:", "", "", "", "", "", "/foo/bar.txt", "", ""); } +test { try parsePass("file:///home/me", "file:///tmp/mock/path", "file:///home/me", "", "file:", "", "", "", "", "", "/home/me", "", ""); } +test { try parsePass("//", "file:///tmp/mock/path", "file:///", "", "file:", "", "", "", "", "", "/", "", ""); } +test { try parsePass("///", "file:///tmp/mock/path", "file:///", "", "file:", "", "", "", "", "", "/", "", ""); } +test { try parsePass("///test", "file:///tmp/mock/path", "file:///test", "", "file:", "", "", "", "", "", "/test", "", ""); } +test { try parsePass("file://test", "file:///tmp/mock/path", "file://test/", "", "file:", "", "", "test", "test", "", "/", "", ""); } +test { try parsePass("file://localhost", "file:///tmp/mock/path", "file:///", "", "file:", "", "", "", "", "", "/", "", ""); } +test { try parsePass("file://localhost/", "file:///tmp/mock/path", "file:///", "", "file:", "", "", "", "", "", "/", "", ""); } +test { try parsePass("file://localhost/test", "file:///tmp/mock/path", "file:///test", "", "file:", "", "", "", "", "", "/test", "", ""); } +test { try parsePass("test", "file:///tmp/mock/path", "file:///tmp/mock/test", "", "file:", "", "", "", "", "", "/tmp/mock/test", "", ""); } +test { try parsePass("file:test", "file:///tmp/mock/path", "file:///tmp/mock/test", "", "file:", "", "", "", "", "", "/tmp/mock/test", "", ""); } +test { try parsePass("/", "http://www.example.com/test", "http://www.example.com/", "http://www.example.com", "http:", "", "", "www.example.com", "www.example.com", "", "/", "", ""); } +test { try parsePass("/test.txt", "http://www.example.com/test", "http://www.example.com/test.txt", "http://www.example.com", "http:", "", "", "www.example.com", "www.example.com", "", "/test.txt", "", ""); } +test { try parsePass(".", "http://www.example.com/test", "http://www.example.com/", "http://www.example.com", "http:", "", "", "www.example.com", "www.example.com", "", "/", "", ""); } +test { try parsePass("..", "http://www.example.com/test", "http://www.example.com/", "http://www.example.com", "http:", "", "", "www.example.com", "www.example.com", "", "/", "", ""); } +test { try parsePass("test.txt", "http://www.example.com/test", "http://www.example.com/test.txt", "http://www.example.com", "http:", "", "", "www.example.com", "www.example.com", "", "/test.txt", "", ""); } +test { try parsePass("./test.txt", "http://www.example.com/test", "http://www.example.com/test.txt", "http://www.example.com", "http:", "", "", "www.example.com", "www.example.com", "", "/test.txt", "", ""); } +test { try parsePass("../test.txt", "http://www.example.com/test", "http://www.example.com/test.txt", "http://www.example.com", "http:", "", "", "www.example.com", "www.example.com", "", "/test.txt", "", ""); } +test { try parsePass("../aaa/test.txt", "http://www.example.com/test", "http://www.example.com/aaa/test.txt", "http://www.example.com", "http:", "", "", "www.example.com", "www.example.com", "", "/aaa/test.txt", "", ""); } +test { try parsePass("../../test.txt", "http://www.example.com/test", "http://www.example.com/test.txt", "http://www.example.com", "http:", "", "", "www.example.com", "www.example.com", "", "/test.txt", "", ""); } +test { try parsePass("\xe4\xb8\xad/test.txt", "http://www.example.com/test", "http://www.example.com/%E4%B8%AD/test.txt", "http://www.example.com", "http:", "", "", "www.example.com", "www.example.com", "", "/%E4%B8%AD/test.txt", "", ""); } +test { try parsePass("http://www.example2.com", "http://www.example.com/test", "http://www.example2.com/", "http://www.example2.com", "http:", "", "", "www.example2.com", "www.example2.com", "", "/", "", ""); } +test { try parsePass("//www.example2.com", "http://www.example.com/test", "http://www.example2.com/", "http://www.example2.com", "http:", "", "", "www.example2.com", "www.example2.com", "", "/", "", ""); } +test { try parsePass("file:...", "http://www.example.com/test", "file:///...", "", "file:", "", "", "", "", "", "/...", "", ""); } +test { try parsePass("file:..", "http://www.example.com/test", "file:///", "", "file:", "", "", "", "", "", "/", "", ""); } +test { try parsePass("file:a", "http://www.example.com/test", "file:///a", "", "file:", "", "", "", "", "", "/a", "", ""); } +test { try parsePass("file:.", "http://www.example.com/test", "file:///", "", "file:", "", "", "", "", "", "/", "", ""); } +test { try parsePass("http://ExAmPlE.CoM", "http://other.com/", "http://example.com/", "http://example.com", "http:", "", "", "example.com", "example.com", "", "/", "", ""); } +test { try parsePass("http://GOO\xe2\x80\x8b\xe2\x81\xa0\xef\xbb\xbfgoo.com", "http://other.com/", "http://googoo.com/", "http://googoo.com", "http:", "", "", "googoo.com", "googoo.com", "", "/", "", ""); } +test { try parsePass("http://www.foo\xe3\x80\x82bar.com", "http://other.com/", "http://www.foo.bar.com/", "http://www.foo.bar.com", "http:", "", "", "www.foo.bar.com", "www.foo.bar.com", "", "/", "", ""); } +test { try parsePass("http://\xef\xbc\xa7\xef\xbd\x8f.com", "http://other.com/", "http://go.com/", "http://go.com", "http:", "", "", "go.com", "go.com", "", "/", "", ""); } +test { try parsePass("http://\xe4\xbd\xa0\xe5\xa5\xbd\xe4\xbd\xa0\xe5\xa5\xbd", "http://other.com/", "http://xn--6qqa088eba/", "http://xn--6qqa088eba", "http:", "", "", "xn--6qqa088eba", "xn--6qqa088eba", "", "/", "", ""); } +test { try parsePass("http://%30%78%63%30%2e%30%32%35%30.01", "http://other.com/", "http://192.168.0.1/", "http://192.168.0.1", "http:", "", "", "192.168.0.1", "192.168.0.1", "", "/", "", ""); } +test { try parsePass("http://%30%78%63%30%2e%30%32%35%30.01%2e", "http://other.com/", "http://192.168.0.1/", "http://192.168.0.1", "http:", "", "", "192.168.0.1", "192.168.0.1", "", "/", "", ""); } +test { try parsePass("http://\xef\xbc\x90\xef\xbc\xb8\xef\xbd\x83\xef\xbc\x90\xef\xbc\x8e\xef\xbc\x90\xef\xbc\x92\xef\xbc\x95\xef\xbc\x90\xef\xbc\x8e\xef\xbc\x90\xef\xbc\x91", "http://other.com/", "http://192.168.0.1/", "http://192.168.0.1", "http:", "", "", "192.168.0.1", "192.168.0.1", "", "/", "", ""); } +test { try parsePass("http://foo:\xf0\x9f\x92\xa9@example.com/bar", "http://other.com/", "http://foo:%F0%9F%92%A9@example.com/bar", "http://example.com", "http:", "foo", "%F0%9F%92%A9", "example.com", "example.com", "", "/bar", "", ""); } +test { try parsePass("#", "test:test", "test:test#", "null", "test:", "", "", "", "", "", "test", "", ""); } +test { try parsePass("#x", "mailto:x@x.com", "mailto:x@x.com#x", "null", "mailto:", "", "", "", "", "", "x@x.com", "", "#x"); } +test { try parsePass("#x", "data:,", "data:,#x", "null", "data:", "", "", "", "", "", ",", "", "#x"); } +test { try parsePass("#x", "about:blank", "about:blank#x", "null", "about:", "", "", "", "", "", "blank", "", "#x"); } +test { try parsePass("#x:y", "about:blank", "about:blank#x:y", "null", "about:", "", "", "", "", "", "blank", "", "#x:y"); } +test { try parsePass("#", "test:test?test", "test:test?test#", "null", "test:", "", "", "", "", "", "test", "?test", ""); } +test { try parsePass("https://@test@test@example:800/", "http://doesnotmatter/", "https://%40test%40test@example:800/", "https://example:800", "https:", "%40test%40test", "", "example:800", "example", "800", "/", "", ""); } +test { try parsePass("https://@@@example", "http://doesnotmatter/", "https://%40%40@example/", "https://example", "https:", "%40%40", "", "example", "example", "", "/", "", ""); } +test { try parsePass("http://`{}:`{}@h/`{}?`{}", "http://doesnotmatter/", "http://%60%7B%7D:%60%7B%7D@h/%60%7B%7D?`{}", "http://h", "http:", "%60%7B%7D", "%60%7B%7D", "h", "h", "", "/%60%7B%7D", "?`{}", ""); } +test { try parsePass("/some/path", "http://user@example.org/smth", "http://user@example.org/some/path", "http://example.org", "http:", "user", "", "example.org", "example.org", "", "/some/path", "", ""); } +test { try parsePass("", "http://user:pass@example.org:21/smth", "http://user:pass@example.org:21/smth", "http://example.org:21", "http:", "user", "pass", "example.org:21", "example.org", "21", "/smth", "", ""); } +test { try parsePass("/some/path", "http://user:pass@example.org:21/smth", "http://user:pass@example.org:21/some/path", "http://example.org:21", "http:", "user", "pass", "example.org:21", "example.org", "21", "/some/path", "", ""); } +test { try parsePass("i", "sc:/pa/pa", "sc:/pa/i", "null", "sc:", "", "", "", "", "", "/pa/i", "", ""); } +test { try parsePass("i", "sc://ho/pa", "sc://ho/i", "null", "sc:", "", "", "ho", "ho", "", "/i", "", ""); } +test { try parsePass("i", "sc:///pa/pa", "sc:///pa/i", "null", "sc:", "", "", "", "", "", "/pa/i", "", ""); } +test { try parsePass("../i", "sc:/pa/pa", "sc:/i", "null", "sc:", "", "", "", "", "", "/i", "", ""); } +test { try parsePass("../i", "sc://ho/pa", "sc://ho/i", "null", "sc:", "", "", "ho", "ho", "", "/i", "", ""); } +test { try parsePass("../i", "sc:///pa/pa", "sc:///i", "null", "sc:", "", "", "", "", "", "/i", "", ""); } +test { try parsePass("/i", "sc:/pa/pa", "sc:/i", "null", "sc:", "", "", "", "", "", "/i", "", ""); } +test { try parsePass("/i", "sc://ho/pa", "sc://ho/i", "null", "sc:", "", "", "ho", "ho", "", "/i", "", ""); } +test { try parsePass("/i", "sc:///pa/pa", "sc:///i", "null", "sc:", "", "", "", "", "", "/i", "", ""); } +test { try parsePass("?i", "sc:/pa/pa", "sc:/pa/pa?i", "null", "sc:", "", "", "", "", "", "/pa/pa", "?i", ""); } +test { try parsePass("?i", "sc://ho/pa", "sc://ho/pa?i", "null", "sc:", "", "", "ho", "ho", "", "/pa", "?i", ""); } +test { try parsePass("?i", "sc:///pa/pa", "sc:///pa/pa?i", "null", "sc:", "", "", "", "", "", "/pa/pa", "?i", ""); } +test { try parsePass("#i", "sc:sd", "sc:sd#i", "null", "sc:", "", "", "", "", "", "sd", "", "#i"); } +test { try parsePass("#i", "sc:sd/sd", "sc:sd/sd#i", "null", "sc:", "", "", "", "", "", "sd/sd", "", "#i"); } +test { try parsePass("#i", "sc:/pa/pa", "sc:/pa/pa#i", "null", "sc:", "", "", "", "", "", "/pa/pa", "", "#i"); } +test { try parsePass("#i", "sc://ho/pa", "sc://ho/pa#i", "null", "sc:", "", "", "ho", "ho", "", "/pa", "", "#i"); } +test { try parsePass("#i", "sc:///pa/pa", "sc:///pa/pa#i", "null", "sc:", "", "", "", "", "", "/pa/pa", "", "#i"); } +test { try parsePass("x", "sc://\xc3\xb1", "sc://%C3%B1/x", "null", "sc:", "", "", "%C3%B1", "%C3%B1", "", "/x", "", ""); } +test { try parsePass("?a=b&c=d", "http://example.org/foo/bar", "http://example.org/foo/bar?a=b&c=d", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/foo/bar", "?a=b&c=d", ""); } +test { try parsePass("??a=b&c=d", "http://example.org/foo/bar", "http://example.org/foo/bar??a=b&c=d", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/foo/bar", "??a=b&c=d", ""); } +test { try parsePass("http:", "http://example.org/foo/bar", "http://example.org/foo/bar", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/foo/bar", "", ""); } +test { try parsePass("sc:", "https://example.org/foo/bar", "sc:", "null", "sc:", "", "", "", "", "", "", "", ""); } +test { try parsePass("http://1.2.3.4/", "http://other.com/", "http://1.2.3.4/", "http://1.2.3.4", "http:", "", "", "1.2.3.4", "1.2.3.4", "", "/", "", ""); } +test { try parsePass("http://1.2.3.4./", "http://other.com/", "http://1.2.3.4/", "http://1.2.3.4", "http:", "", "", "1.2.3.4", "1.2.3.4", "", "/", "", ""); } +test { try parsePass("http://192.168.257", "http://other.com/", "http://192.168.1.1/", "http://192.168.1.1", "http:", "", "", "192.168.1.1", "192.168.1.1", "", "/", "", ""); } +test { try parsePass("http://192.168.257.", "http://other.com/", "http://192.168.1.1/", "http://192.168.1.1", "http:", "", "", "192.168.1.1", "192.168.1.1", "", "/", "", ""); } +test { try parsePass("http://192.168.257.com", "http://other.com/", "http://192.168.257.com/", "http://192.168.257.com", "http:", "", "", "192.168.257.com", "192.168.257.com", "", "/", "", ""); } +test { try parsePass("http://256", "http://other.com/", "http://0.0.1.0/", "http://0.0.1.0", "http:", "", "", "0.0.1.0", "0.0.1.0", "", "/", "", ""); } +test { try parsePass("http://256.com", "http://other.com/", "http://256.com/", "http://256.com", "http:", "", "", "256.com", "256.com", "", "/", "", ""); } +test { try parsePass("http://999999999", "http://other.com/", "http://59.154.201.255/", "http://59.154.201.255", "http:", "", "", "59.154.201.255", "59.154.201.255", "", "/", "", ""); } +test { try parsePass("http://999999999.", "http://other.com/", "http://59.154.201.255/", "http://59.154.201.255", "http:", "", "", "59.154.201.255", "59.154.201.255", "", "/", "", ""); } +test { try parsePass("http://999999999.com", "http://other.com/", "http://999999999.com/", "http://999999999.com", "http:", "", "", "999999999.com", "999999999.com", "", "/", "", ""); } +test { try parsePass("http://10000000000.com", "http://other.com/", "http://10000000000.com/", "http://10000000000.com", "http:", "", "", "10000000000.com", "10000000000.com", "", "/", "", ""); } +test { try parsePass("http://4294967295", "http://other.com/", "http://255.255.255.255/", "http://255.255.255.255", "http:", "", "", "255.255.255.255", "255.255.255.255", "", "/", "", ""); } +test { try parsePass("http://0xffffffff", "http://other.com/", "http://255.255.255.255/", "http://255.255.255.255", "http:", "", "", "255.255.255.255", "255.255.255.255", "", "/", "", ""); } +test { try parsePass("pix/submit.gif", "file:///C:/Users/Domenic/Dropbox/GitHub/tmpvar/jsdom/test/level2/html/files/anchor.html", "file:///C:/Users/Domenic/Dropbox/GitHub/tmpvar/jsdom/test/level2/html/files/pix/submit.gif", "", "file:", "", "", "", "", "", "/C:/Users/Domenic/Dropbox/GitHub/tmpvar/jsdom/test/level2/html/files/pix/submit.gif", "", ""); } +test { try parsePass("..", "file:///C:/", "file:///C:/", "", "file:", "", "", "", "", "", "/C:/", "", ""); } +test { try parsePass("..", "file:///", "file:///", "", "file:", "", "", "", "", "", "/", "", ""); } +test { try parsePass("/", "file:///C:/a/b", "file:///C:/", "", "file:", "", "", "", "", "", "/C:/", "", ""); } +test { try parsePass("/", "file://h/C:/a/b", "file://h/C:/", "", "file:", "", "", "h", "h", "", "/C:/", "", ""); } +test { try parsePass("/", "file://h/a/b", "file://h/", "", "file:", "", "", "h", "h", "", "/", "", ""); } +test { try parsePass("//d:", "file:///C:/a/b", "file:///d:", "", "file:", "", "", "", "", "", "/d:", "", ""); } +test { try parsePass("//d:/..", "file:///C:/a/b", "file:///d:/", "", "file:", "", "", "", "", "", "/d:/", "", ""); } +test { try parsePass("..", "file:///ab:/", "file:///", "", "file:", "", "", "", "", "", "/", "", ""); } +test { try parsePass("..", "file:///1:/", "file:///", "", "file:", "", "", "", "", "", "/", "", ""); } +test { try parsePass("", "file:///test?test#test", "file:///test?test", "", "file:", "", "", "", "", "", "/test", "?test", ""); } +test { try parsePass("file:", "file:///test?test#test", "file:///test?test", "", "file:", "", "", "", "", "", "/test", "?test", ""); } +test { try parsePass("?x", "file:///test?test#test", "file:///test?x", "", "file:", "", "", "", "", "", "/test", "?x", ""); } +test { try parsePass("file:?x", "file:///test?test#test", "file:///test?x", "", "file:", "", "", "", "", "", "/test", "?x", ""); } +test { try parsePass("#x", "file:///test?test#test", "file:///test?test#x", "", "file:", "", "", "", "", "", "/test", "?test", "#x"); } +test { try parsePass("file:#x", "file:///test?test#test", "file:///test?test#x", "", "file:", "", "", "", "", "", "/test", "?test", "#x"); } +test { try parsePass("/////mouse", "file:///elephant", "file://///mouse", "", "file:", "", "", "", "", "", "///mouse", "", ""); } +test { try parsePass("\\//pig", "file://lion/", "file:///pig", "", "file:", "", "", "", "", "", "/pig", "", ""); } +test { try parsePass("\\/localhost//pig", "file://lion/", "file:////pig", "", "file:", "", "", "", "", "", "//pig", "", ""); } +test { try parsePass("//localhost//pig", "file://lion/", "file:////pig", "", "file:", "", "", "", "", "", "//pig", "", ""); } +test { try parsePass("/..//localhost//pig", "file://lion/", "file://lion//localhost//pig", "", "file:", "", "", "lion", "lion", "", "//localhost//pig", "", ""); } +test { try parsePass("file://", "file://ape/", "file:///", "", "file:", "", "", "", "", "", "/", "", ""); } +test { try parsePass("/rooibos", "file://tea/", "file://tea/rooibos", "", "file:", "", "", "tea", "tea", "", "/rooibos", "", ""); } +test { try parsePass("/?chai", "file://tea/", "file://tea/?chai", "", "file:", "", "", "tea", "tea", "", "/", "?chai", ""); } +test { try parsePass("C|", "file://host/dir/file", "file://host/C:", "", "file:", "", "", "host", "host", "", "/C:", "", ""); } +test { try parsePass("C|", "file://host/D:/dir1/dir2/file", "file://host/C:", "", "file:", "", "", "host", "host", "", "/C:", "", ""); } +test { try parsePass("C|#", "file://host/dir/file", "file://host/C:#", "", "file:", "", "", "host", "host", "", "/C:", "", ""); } +test { try parsePass("C|?", "file://host/dir/file", "file://host/C:?", "", "file:", "", "", "host", "host", "", "/C:", "", ""); } +test { try parsePass("C|/", "file://host/dir/file", "file://host/C:/", "", "file:", "", "", "host", "host", "", "/C:/", "", ""); } +test { try parsePass("C|\n/", "file://host/dir/file", "file://host/C:/", "", "file:", "", "", "host", "host", "", "/C:/", "", ""); } +test { try parsePass("C|\\", "file://host/dir/file", "file://host/C:/", "", "file:", "", "", "host", "host", "", "/C:/", "", ""); } +test { try parsePass("C", "file://host/dir/file", "file://host/dir/C", "", "file:", "", "", "host", "host", "", "/dir/C", "", ""); } +test { try parsePass("C|a", "file://host/dir/file", "file://host/dir/C|a", "", "file:", "", "", "host", "host", "", "/dir/C|a", "", ""); } +test { try parsePass("/c:/foo/bar", "file:///c:/baz/qux", "file:///c:/foo/bar", "", "file:", "", "", "", "", "", "/c:/foo/bar", "", ""); } +test { try parsePass("/c|/foo/bar", "file:///c:/baz/qux", "file:///c:/foo/bar", "", "file:", "", "", "", "", "", "/c:/foo/bar", "", ""); } +test { try parsePass("file:\\c:\\foo\\bar", "file:///c:/baz/qux", "file:///c:/foo/bar", "", "file:", "", "", "", "", "", "/c:/foo/bar", "", ""); } +test { try parsePass("/c:/foo/bar", "file://host/path", "file://host/c:/foo/bar", "", "file:", "", "", "host", "host", "", "/c:/foo/bar", "", ""); } +test { try parsePass("C|/", "file://host/", "file://host/C:/", "", "file:", "", "", "host", "host", "", "/C:/", "", ""); } +test { try parsePass("/C:/", "file://host/", "file://host/C:/", "", "file:", "", "", "host", "host", "", "/C:/", "", ""); } +test { try parsePass("file:C:/", "file://host/", "file://host/C:/", "", "file:", "", "", "host", "host", "", "/C:/", "", ""); } +test { try parsePass("file:/C:/", "file://host/", "file://host/C:/", "", "file:", "", "", "host", "host", "", "/C:/", "", ""); } +test { try parsePass("//C:/", "file://host/", "file:///C:/", "", "file:", "", "", "", "", "", "/C:/", "", ""); } +test { try parsePass("file://C:/", "file://host/", "file:///C:/", "", "file:", "", "", "", "", "", "/C:/", "", ""); } +test { try parsePass("///C:/", "file://host/", "file:///C:/", "", "file:", "", "", "", "", "", "/C:/", "", ""); } +test { try parsePass("file:///C:/", "file://host/", "file:///C:/", "", "file:", "", "", "", "", "", "/C:/", "", ""); } +test { try parsePass("file:///one/two", "file:///", "file:///one/two", "", "file:", "", "", "", "", "", "/one/two", "", ""); } +test { try parsePass("file:////one/two", "file:///", "file:////one/two", "", "file:", "", "", "", "", "", "//one/two", "", ""); } +test { try parsePass("//one/two", "file:///", "file://one/two", "", "file:", "", "", "one", "one", "", "/two", "", ""); } +test { try parsePass("///one/two", "file:///", "file:///one/two", "", "file:", "", "", "", "", "", "/one/two", "", ""); } +test { try parsePass("////one/two", "file:///", "file:////one/two", "", "file:", "", "", "", "", "", "//one/two", "", ""); } +test { try parsePass("file:///.//", "file:////", "file:////", "", "file:", "", "", "", "", "", "//", "", ""); } +test { try parsePass("http://[1:0::]", "http://example.net/", "http://[1::]/", "http://[1::]", "http:", "", "", "[1::]", "[1::]", "", "/", "", ""); } +test { try parsePass("#x", "sc://\xc3\xb1", "sc://%C3%B1#x", "null", "sc:", "", "", "%C3%B1", "%C3%B1", "", "", "", "#x"); } +test { try parsePass("?x", "sc://\xc3\xb1", "sc://%C3%B1?x", "null", "sc:", "", "", "%C3%B1", "%C3%B1", "", "", "?x", ""); } +test { try parsePass("///", "sc://x/", "sc:///", "", "sc:", "", "", "", "", "", "/", "", ""); } +test { try parsePass("////", "sc://x/", "sc:////", "", "sc:", "", "", "", "", "", "//", "", ""); } +test { try parsePass("////x/", "sc://x/", "sc:////x/", "", "sc:", "", "", "", "", "", "//x/", "", ""); } +test { try parsePass("/.//path", "non-spec:/p", "non-spec:/.//path", "", "non-spec:", "", "", "", "", "", "//path", "", ""); } +test { try parsePass("/..//path", "non-spec:/p", "non-spec:/.//path", "", "non-spec:", "", "", "", "", "", "//path", "", ""); } +test { try parsePass("..//path", "non-spec:/p", "non-spec:/.//path", "", "non-spec:", "", "", "", "", "", "//path", "", ""); } +test { try parsePass("a/..//path", "non-spec:/p", "non-spec:/.//path", "", "non-spec:", "", "", "", "", "", "//path", "", ""); } +test { try parsePass("", "non-spec:/..//p", "non-spec:/.//p", "", "non-spec:", "", "", "", "", "", "//p", "", ""); } +test { try parsePass("path", "non-spec:/..//p", "non-spec:/.//path", "", "non-spec:", "", "", "", "", "", "//path", "", ""); } +test { try parsePass("../path", "non-spec:/.//p", "non-spec:/path", "", "non-spec:", "", "", "", "", "", "/path", "", ""); } +test { try parsePass("test-a-colon-slash.html", "a:/", "a:/test-a-colon-slash.html", "", "a:", "", "", "", "", "", "/test-a-colon-slash.html", "", ""); } +test { try parsePass("test-a-colon-slash-slash.html", "a://", "a:///test-a-colon-slash-slash.html", "", "a:", "", "", "", "", "", "/test-a-colon-slash-slash.html", "", ""); } +test { try parsePass("test-a-colon-slash-b.html", "a:/b", "a:/test-a-colon-slash-b.html", "", "a:", "", "", "", "", "", "/test-a-colon-slash-b.html", "", ""); } +test { try parsePass("test-a-colon-slash-slash-b.html", "a://b", "a://b/test-a-colon-slash-slash-b.html", "", "a:", "", "", "b", "b", "", "/test-a-colon-slash-slash-b.html", "", ""); } +test { try parsePass("10.0.0.7:8080/foo.html", "file:///some/dir/bar.html", "file:///some/dir/10.0.0.7:8080/foo.html", "", "file:", "", "", "", "", "", "/some/dir/10.0.0.7:8080/foo.html", "", ""); } +test { try parsePass("a!@$*=/foo.html", "file:///some/dir/bar.html", "file:///some/dir/a!@$*=/foo.html", "", "file:", "", "", "", "", "", "/some/dir/a!@$*=/foo.html", "", ""); } +test { try parsePass("a1234567890-+.:foo/bar", "http://example.com/dir/file", "a1234567890-+.:foo/bar", "", "a1234567890-+.:", "", "", "", "", "", "foo/bar", "", ""); } +test { try parsePass("#link", "https://example.org/##link", "https://example.org/#link", "", "https:", "", "", "example.org", "example.org", "", "/", "", "#link"); } +test { try parsePass("https://user:pass[\x7f@foo/bar", "http://example.org", "https://user:pass%5B%7F@foo/bar", "https://foo", "https:", "user", "pass%5B%7F", "foo", "foo", "", "/bar", "", ""); } +test { try parsePass("abc:rootless", "abc://host/path", "abc:rootless", "", "abc:", "", "", "", "", "", "rootless", "", ""); } +test { try parsePass("abc:rootless", "abc:/path", "abc:rootless", "", "abc:", "", "", "", "", "", "rootless", "", ""); } +test { try parsePass("abc:rootless", "abc:path", "abc:rootless", "", "abc:", "", "", "", "", "", "rootless", "", ""); } +test { try parsePass("abc:/rooted", "abc://host/path", "abc:/rooted", "", "abc:", "", "", "", "", "", "/rooted", "", ""); } +test { try parsePass("///test", "http://example.org/", "http://test/", "", "http:", "", "", "test", "test", "", "/", "", ""); } +test { try parsePass("///\\//\\//test", "http://example.org/", "http://test/", "", "http:", "", "", "test", "test", "", "/", "", ""); } +test { try parsePass("///example.org/path", "http://example.org/", "http://example.org/path", "", "http:", "", "", "example.org", "example.org", "", "/path", "", ""); } +test { try parsePass("///example.org/../path", "http://example.org/", "http://example.org/path", "", "http:", "", "", "example.org", "example.org", "", "/path", "", ""); } +test { try parsePass("///example.org/../../", "http://example.org/", "http://example.org/", "", "http:", "", "", "example.org", "example.org", "", "/", "", ""); } +test { try parsePass("///example.org/../path/../../", "http://example.org/", "http://example.org/", "", "http:", "", "", "example.org", "example.org", "", "/", "", ""); } +test { try parsePass("///example.org/../path/../../path", "http://example.org/", "http://example.org/path", "", "http:", "", "", "example.org", "example.org", "", "/path", "", ""); } +test { try parsePass("/\\/\\//example.org/../path", "http://example.org/", "http://example.org/path", "", "http:", "", "", "example.org", "example.org", "", "/path", "", ""); } +test { try parsePass("///abcdef/../", "file:///", "file:///", "", "file:", "", "", "", "", "", "/", "", ""); } +test { try parsePass("/\\//\\/a/../", "file:///", "file://////", "", "file:", "", "", "", "", "", "////", "", ""); } +test { try parsePass("//a/../", "file:///", "file://a/", "", "file:", "", "", "a", "a", "", "/", "", ""); } test { try parseIDNAFail("a\xe2\x80\x8cb"); } test { try parseIDNAFail("A\xe2\x80\x8cB"); } -- 2.54.0