Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions core/runtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ fetch = [
"dep:either",
"dep:http",
"dep:serde_json",
"dep:url",
"boa_engine/either",
]
reqwest-blocking = ["dep:reqwest", "reqwest/blocking"]
Expand Down
8 changes: 4 additions & 4 deletions core/runtime/src/fetch/response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -303,17 +303,17 @@ impl JsResponse {
if !matches!(status, 301 | 302 | 303 | 307 | 308) {
return Err(js_error!(RangeError: "Invalid redirect status: {}", status));
}
let url_str = url.to_string(context)?.to_std_string_escaped();
http::Uri::try_from(url_str.as_str())
.map_err(|_| js_error!(TypeError: "Invalid URL: {}", url_str))?;
let parsed_url = url::Url::parse(&url.to_string(context)?.to_std_string_escaped())
.map_err(|e| js_error!(TypeError: "Invalid URL: {}", e))?;
let serialized_url = parsed_url.to_string();

let status_code = StatusCode::from_u16(status)
.map_err(|_| js_error!(RangeError: "Invalid status code: {}", status))?;

let mut headers = http::header::HeaderMap::new();
headers.insert(
HeaderName::from_static("location"),
HeaderValue::try_from(url_str)
HeaderValue::try_from(serialized_url)
.map_err(|_| js_error!(TypeError: "Invalid URL for header value"))?,
);

Expand Down
70 changes: 67 additions & 3 deletions core/runtime/src/fetch/tests/response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,10 +180,74 @@ fn response_redirect_custom_status_and_coercion() {
r#"
const response = Response.redirect("http://example.com/", 301);
assertEq(response.status, 301);
"#,
),
]);
}

#[test]
fn response_redirect_rejects_non_url_after_string_coercion() {
run_test_actions([
TestAction::harness(),
TestAction::inspect_context(|ctx| register(&[], ctx)),
TestAction::run(
r#"
let threw = false;
try {
Response.redirect(12345);
} catch (e) {
threw = true;
if (!(e instanceof TypeError)) {
throw new Error("Expected TypeError, got " + e.name);
}
}
if (!threw) {
throw new Error("Expected TypeError, but no error was thrown");
}
"#,
),
]);
}

#[test]
fn response_redirect_serializes_parsed_url() {
run_test_actions([
TestAction::harness(),
TestAction::inspect_context(|ctx| register(&[], ctx)),
TestAction::run(
r#"
const response1 = Response.redirect("https://example.com");
assertEq(response1.headers.get("location"), "https://example.com/");
"#,
),
TestAction::run(
r#"
const response2 = Response.redirect("https://example.com#frag");
assertEq(response2.headers.get("location"), "https://example.com/#frag");
"#,
),
]);
}

// Tests Web IDL coercion of the URL parameter
const response2 = Response.redirect(12345);
assertEq(response2.headers.get("location"), "12345");
#[test]
fn response_redirect_rejects_relative_url() {
run_test_actions([
TestAction::harness(),
TestAction::inspect_context(|ctx| register(&[], ctx)),
TestAction::run(
r#"
let threw = false;
try {
Response.redirect("/foo");
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need adjusting (see comments in #5301. )
this should not throw.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah I'll push the commit by night was working on something else

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I rechecked this more carefully. The spec parses against the current settings objects API base URL, so I think the /foo case depends on the host environment. that should explain why it works in your browser console ss while node and boa CLI both throw here

The part this PR is fixing is narrower: Location should use the serialized parsed url

@jedel1043 what do you think about keeping this PR scoped to that part ??

} catch (e) {
threw = true;
if (!(e instanceof TypeError)) {
throw new Error("Expected TypeError, got " + e.name);
}
}
if (!threw) {
throw new Error("Expected TypeError, but no error was thrown");
}
"#,
),
]);
Expand Down
Loading