Compare commits

...

2 Commits

Author SHA1 Message Date
Ashcon Partovi
940c5f018b fix test case 2025-03-25 13:12:37 -07:00
Ashcon Partovi
58e244060b fix: #18414 2025-03-25 12:23:20 -07:00
3 changed files with 90 additions and 7 deletions

View File

@@ -481,6 +481,11 @@ pub const Response = struct {
did_succeed = true;
return bun.new(Response, response).toJS(globalThis);
}
fn isValidRedirectStatus(status: u16) bool {
return status == 301 or status == 302 or status == 303 or status == 307 or status == 308;
}
pub fn constructRedirect(
globalThis: *JSC.JSGlobalObject,
callframe: *JSC.CallFrame,
@@ -505,9 +510,15 @@ pub const Response = struct {
const url_string_value = args.nextEat() orelse JSC.JSValue.zero;
var url_string = ZigString.init("");
if (@intFromEnum(url_string_value) != 0) {
url_string = try url_string_value.getZigString(globalThis);
if (url_string_value.isEmptyOrUndefinedOrNull() or url_string_value.isNumber()) {
return globalThis.throwInvalidArgumentTypeValue("url", "string", url_string_value);
}
if (url_string_value.isObject() and !url_string_value.isString() and !url_string_value.implementsToString(globalThis)) {
return globalThis.throwInvalidArgumentTypeValue("url", "string", url_string_value);
}
url_string = try url_string_value.getZigString(globalThis);
url_string_slice = url_string.toSlice(getAllocator(globalThis));
var did_succeed = false;
defer {
@@ -519,13 +530,21 @@ pub const Response = struct {
if (args.nextEat()) |init| {
if (init.isUndefinedOrNull()) {} else if (init.isNumber()) {
response.init.status_code = @as(u16, @intCast(@min(@max(0, init.toInt32()), std.math.maxInt(u16))));
const status_code = init.toInt32();
const status = @as(u16, @intCast(@min(@max(0, status_code), std.math.maxInt(u16))));
if (isValidRedirectStatus(status)) {
response.init.status_code = status;
} else {
return globalThis.throwTypeError("Invalid redirect status code: {d}. Must be one of 301, 302, 303, 307, or 308", .{status});
}
} else {
if (Response.Init.init(globalThis, init) catch |err|
if (err == error.JSError) return .zero else null) |_init|
{
if (!isValidRedirectStatus(_init.status_code)) {
return globalThis.throwTypeError("Invalid redirect status code: {d}. Must be one of 301, 302, 303, 307, or 308", .{_init.status_code});
}
response.init = _init;
response.init.status_code = 302;
}
}
}

View File

@@ -1092,11 +1092,11 @@ describe("Response", () => {
"content-type": "potato",
"x-hello": "world",
},
status: 408,
status: 301,
});
expect(response.headers.get("x-hello")).toBe("world");
expect(response.status).toBe(408);
expect(response.status).toBe(301);
});
});
describe("Response.redirect", () => {
@@ -1121,7 +1121,7 @@ describe("Response", () => {
"x-hello": "world",
Location: "https://wrong.com",
},
status: 408,
status: 302,
});
expect(response.headers.get("x-hello")).toBe("world");
expect(response.headers.get("Location")).toBe("https://example.com");

View File

@@ -0,0 +1,64 @@
import { expect, test, describe } from "bun:test";
describe("Response.redirect", () => {
test("should validate URL parameter type - number", () => {
expect(() => {
Response.redirect(420, "blaze it");
}).toThrow('The "url" argument must be of type string');
});
test("should validate URL parameter type - null", () => {
expect(() => {
Response.redirect(null);
}).toThrow('The "url" argument must be of type string');
});
test("should validate URL parameter type - undefined", () => {
expect(() => {
Response.redirect(undefined);
}).toThrow('The "url" argument must be of type string');
});
test("accepts arrays with toString", () => {
const response = Response.redirect(["not", "a", "url"]);
expect(response.headers.get("Location")).toBe("not,a,url");
});
test("rejects a plain object as URL", () => {
expect(() => {
Response.redirect({ not: "a url" });
}).toThrow('The "url" argument must be of type string');
});
test("accepts a string URL with default status code", () => {
const response = Response.redirect("https://example.com");
expect(response.status).toBe(302);
expect(response.headers.get("Location")).toBe("https://example.com");
});
test("accepts a string URL with valid redirect status code", () => {
const response = Response.redirect("https://example.com", 301);
expect(response.status).toBe(301);
expect(response.headers.get("Location")).toBe("https://example.com");
});
test("accepts a URL object as first parameter", () => {
const url = new URL("https://example.com");
const response = Response.redirect(url, 301);
expect(response.status).toBe(301);
expect(response.headers.get("Location")).toBe("https://example.com/");
});
test("accepts objects with toString method as first parameter", () => {
const urlLike = { toString: () => "https://example.com" };
const response = Response.redirect(urlLike, 301);
expect(response.status).toBe(301);
expect(response.headers.get("Location")).toBe("https://example.com");
});
test("rejects an invalid redirect status code", () => {
expect(() => {
Response.redirect("https://example.com", 420);
}).toThrow();
});
});