aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/http_client_async.zig26
-rw-r--r--test/js/web/fetch/fetch.test.ts94
2 files changed, 106 insertions, 14 deletions
diff --git a/src/http_client_async.zig b/src/http_client_async.zig
index 89cedaa1b..3cc044003 100644
--- a/src/http_client_async.zig
+++ b/src/http_client_async.zig
@@ -3479,22 +3479,20 @@ pub fn handleResponseMetadata(
this.redirect = url_buf;
} else {
var url_buf = URLBufferPool.get(default_allocator);
+ var fba = std.heap.FixedBufferAllocator.init(&url_buf.data);
const original_url = this.url;
- const port = original_url.getPortAuto();
-
- if (port == original_url.getDefaultPort()) {
- this.url = URL.parse(std.fmt.bufPrint(
- &url_buf.data,
- "{s}://{s}{s}",
- .{ original_url.displayProtocol(), original_url.displayHostname(), location },
- ) catch return error.RedirectURLTooLong);
- } else {
- this.url = URL.parse(std.fmt.bufPrint(
- &url_buf.data,
- "{s}://{s}:{d}{s}",
- .{ original_url.displayProtocol(), original_url.displayHostname(), port, location },
- ) catch return error.RedirectURLTooLong);
+
+ const new_url_ = bun.JSC.URL.join(
+ bun.String.fromUTF8(original_url.href),
+ bun.String.fromUTF8(location),
+ );
+ defer new_url_.deref();
+
+ if (new_url_.utf8ByteLength() > url_buf.data.len) {
+ return error.RedirectURLTooLong;
}
+ const new_url = new_url_.toUTF8(fba.allocator());
+ this.url = URL.parse(new_url.slice());
is_same_origin = strings.eqlCaseInsensitiveASCII(strings.withoutTrailingSlash(this.url.origin), strings.withoutTrailingSlash(original_url.origin), true);
deferred_redirect.* = this.redirect;
diff --git a/test/js/web/fetch/fetch.test.ts b/test/js/web/fetch/fetch.test.ts
index 23e78ac48..100f6e79e 100644
--- a/test/js/web/fetch/fetch.test.ts
+++ b/test/js/web/fetch/fetch.test.ts
@@ -1596,3 +1596,97 @@ it("same-origin status code 302 should not strip headers", async () => {
expect(redirected).toBe(true);
server.stop(true);
});
+
+describe("should handle relative location in the redirect, issue#5635", () => {
+ var server: Server;
+ beforeAll(async () => {
+ server = Bun.serve({
+ port: 0,
+ async fetch(request: Request) {
+ return new Response("Not Found", {
+ status: 404,
+ });
+ },
+ });
+ });
+ afterAll(() => {
+ server.stop(true);
+ });
+
+ it.each([
+ ["/a/b", "/c", "/c"],
+ ["/a/b", "c", "/a/c"],
+ ["/a/b", "/c/d", "/c/d"],
+ ["/a/b", "c/d", "/a/c/d"],
+ ["/a/b", "../c", "/c"],
+ ["/a/b", "../c/d", "/c/d"],
+ ["/a/b", "../../../c", "/c"],
+ // slash
+ ["/a/b/", "/c", "/c"],
+ ["/a/b/", "c", "/a/b/c"],
+ ["/a/b/", "/c/d", "/c/d"],
+ ["/a/b/", "c/d", "/a/b/c/d"],
+ ["/a/b/", "../c", "/a/c"],
+ ["/a/b/", "../c/d", "/a/c/d"],
+ ["/a/b/", "../../../c", "/c"],
+ ])("('%s', '%s')", async (pathname, location, expected) => {
+ server.reload({
+ async fetch(request: Request) {
+ const url = new URL(request.url);
+ if (url.pathname == pathname) {
+ return new Response("redirecting", {
+ headers: {
+ "Location": location,
+ },
+ status: 302,
+ });
+ } else if (url.pathname == expected) {
+ return new Response("Fine.");
+ }
+ return new Response("Not Found", {
+ status: 404,
+ });
+ },
+ });
+
+ const resp = await fetch(`http://${server.hostname}:${server.port}${pathname}`);
+ expect(resp.redirected).toBe(true);
+ expect(new URL(resp.url).pathname).toStrictEqual(expected);
+ expect(resp.status).toBe(200);
+ expect(await resp.text()).toBe("Fine.");
+ });
+});
+
+it("should throw RedirectURLTooLong when location is too long", async () => {
+ const server = Bun.serve({
+ port: 0,
+ async fetch(request: Request) {
+ gc();
+ const url = new URL(request.url);
+ if (url.pathname == "/redirect") {
+ return new Response("redirecting", {
+ headers: {
+ "Location": "B".repeat(8193),
+ },
+ status: 302,
+ });
+ }
+ return new Response("Not Found", {
+ status: 404,
+ });
+ },
+ });
+
+ let err = undefined;
+ try {
+ gc();
+ const resp = await fetch(`http://${server.hostname}:${server.port}/redirect`);
+ } catch (error) {
+ gc();
+ err = error;
+ }
+ expect(err).not.toBeUndefined();
+ expect(err).toBeInstanceOf(Error);
+ expect(err.code).toStrictEqual("RedirectURLTooLong");
+ server.stop(true);
+});