Merge pull request #2583 from jschaf/codex/fix-relative-url-fragment

browser/URL: ignore query and fragment slashes in URL resolve
This commit is contained in:
Karl Seguin
2026-06-01 13:07:06 +08:00
committed by GitHub
3 changed files with 93 additions and 3 deletions

View File

@@ -121,7 +121,8 @@ pub fn resolve(allocator: Allocator, base: [:0]const u8, source_path: anytype, o
const scheme_end = std.mem.indexOf(u8, base, "://");
const authority_start = if (scheme_end) |end| end + 3 else 0;
const path_start = std.mem.indexOfScalarPos(u8, base, authority_start, '/') orelse base.len;
const path_start = std.mem.indexOfAnyPos(u8, base, authority_start, "/?#") orelse base.len;
const path_end = std.mem.indexOfAnyPos(u8, base, path_start, "?#") orelse base.len;
if (path[0] == '/') {
const result = try std.mem.joinZ(allocator, "", &.{ base[0..path_start], path });
@@ -129,8 +130,8 @@ pub fn resolve(allocator: Allocator, base: [:0]const u8, source_path: anytype, o
}
var normalized_base: []const u8 = base[0..path_start];
if (path_start < base.len) {
if (std.mem.lastIndexOfScalar(u8, base[path_start + 1 ..], '/')) |pos| {
if (path_start < path_end) {
if (std.mem.lastIndexOfScalar(u8, base[path_start + 1 .. path_end], '/')) |pos| {
normalized_base = base[0 .. path_start + 1 + pos];
}
}
@@ -973,6 +974,66 @@ test "URL: resolve" {
.path = "something.js",
.expected = "https://example/xyz/abc/something.js",
},
.{
.base = "http://127.0.0.1:8123/#/login",
.path = "api/users/login",
.expected = "http://127.0.0.1:8123/api/users/login",
},
.{
.base = "https://example/app/page?next=/foo/bar",
.path = "api/users/login",
.expected = "https://example/app/api/users/login",
},
.{
.base = "https://example/app/page#/foo/bar",
.path = "api/users/login",
.expected = "https://example/app/api/users/login",
},
.{
.base = "https://example?next=/foo/bar",
.path = "api/users/login",
.expected = "https://example/api/users/login",
},
.{
.base = "https://example#/foo/bar",
.path = "api/users/login",
.expected = "https://example/api/users/login",
},
.{
.base = "https://example?next=/foo/bar",
.path = "/api/users/login",
.expected = "https://example/api/users/login",
},
.{
.base = "https://example/app/page?next=/foo/bar",
.path = "../api/users/login",
.expected = "https://example/api/users/login",
},
.{
.base = "https://example/app/page#/foo/bar",
.path = "../api/users/login",
.expected = "https://example/api/users/login",
},
.{
.base = "https://example/app/dir/?next=/foo/bar",
.path = "../api/users/login",
.expected = "https://example/app/api/users/login",
},
.{
.base = "https://example/app/dir/#/foo/bar",
.path = "../api/users/login",
.expected = "https://example/app/api/users/login",
},
.{
.base = "https://example/app/page?next=/foo/bar",
.path = "?q=/api/users/login",
.expected = "https://example/app/page?q=/api/users/login",
},
.{
.base = "https://example/app/page#/foo/bar",
.path = "#/api/users/login",
.expected = "https://example/app/page#/api/users/login",
},
.{
.base = "https://example/xyz/abc/123",
.path = "/something.js",

View File

@@ -0,0 +1,28 @@
<!DOCTYPE html>
<script src="../testing.js"></script>
<script id=fetch_relative_url_ignores_hash_route type=module>
{
const state = await testing.async();
const originalUrl = location.href;
history.replaceState(null, '', '/#/login');
const response = await fetch('xhr', { method: 'POST', body: 'foo' });
const text = await response.text();
history.replaceState(null, '', originalUrl);
state.resolve({
ok: response.ok,
status: response.status,
url: response.url,
length: text.length,
});
await state.done((data) => {
testing.expectEqual(true, data.ok);
testing.expectEqual(200, data.status);
testing.expectEqual('http://127.0.0.1:9582/xhr', data.url);
testing.expectEqual(100, data.length);
});
}
</script>

View File

@@ -270,4 +270,5 @@ fn httpShutdownCallback(ctx: *anyopaque) void {
const testing = @import("../../../testing.zig");
test "WebApi: fetch" {
try testing.htmlRunner("net/fetch.html", .{});
try testing.htmlRunner("net/fetch_hash_route.html", .{});
}