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
26 changes: 22 additions & 4 deletions core/runtime/src/fetch/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ pub struct RequestInit {
signal: Option<JsObject>,
}

#[derive(Clone, Copy)]
struct HasBody;

impl RequestInit {
/// Takes the abort signal from the options, if present.
pub fn take_signal(&mut self) -> Option<JsObject> {
Expand All @@ -41,9 +44,13 @@ impl RequestInit {
request: Option<HttpRequest<Vec<u8>>>,
) -> JsResult<HttpRequest<Vec<u8>>> {
let mut builder = HttpRequest::builder();
let mut is_get_or_head_method = true;
let mut has_inherited_body = false;
let mut request_body = Vec::new();
if let Some(r) = request {
let (parts, body) = r.into_parts();
is_get_or_head_method = matches!(parts.method, http::Method::GET | http::Method::HEAD);
has_inherited_body = parts.extensions.get::<HasBody>().is_some() || !body.is_empty();
builder = builder
.method(parts.method)
.uri(parts.uri)
Expand All @@ -62,9 +69,16 @@ impl RequestInit {
}

if let Some(Convert(ref method)) = self.method.take() {
builder = builder.method(method.to_std_string().map_err(
let method = method.to_std_string().map_err(
|_| js_error!(TypeError: "Request constructor: {} is an invalid method", method.to_std_string_escaped()),
)?.as_str());
)?;
is_get_or_head_method =
method.eq_ignore_ascii_case("GET") || method.eq_ignore_ascii_case("HEAD");
builder = builder.method(method.as_str());
}

if is_get_or_head_method && (self.body.is_some() || has_inherited_body) {
return Err(js_error!(TypeError: "Request with GET/HEAD method cannot have body."));
}

if let Some(body) = &self.body {
Expand All @@ -81,9 +95,13 @@ impl RequestInit {
}
}

builder
let mut request = builder
.body(request_body)
.map_err(|_| js_error!(Error: "Cannot construct request"))
.map_err(|_| js_error!(Error: "Cannot construct request"))?;
if self.body.is_some() || has_inherited_body {
request.extensions_mut().insert(HasBody);
}
Ok(request)
}
}

Expand Down
83 changes: 83 additions & 0 deletions core/runtime/src/fetch/tests/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crate::test::{TestAction, run_test_actions};
use boa_engine::{js_str, js_string};
use either::Either;
use http::{Response, Uri};
use indoc::indoc;

#[test]
fn request_constructor() {
Expand Down Expand Up @@ -48,6 +49,88 @@ fn request_constructor() {
]);
}

#[test]
fn request_constructor_get_with_body_throws() {
run_test_actions([
TestAction::inspect_context(|ctx| {
let fetcher = TestFetcher::default();
crate::fetch::register(fetcher, None, ctx).expect("failed to register fetch");
}),
TestAction::run(indoc! {r#"
try {
new Request("http://unit.test", { method: "GET", body: "x" });
throw Error("expected the call above to throw");
} catch (e) {
if (!(e instanceof TypeError)) {
throw e;
}
}
"#}),
]);
}

#[test]
fn request_constructor_head_with_body_throws() {
run_test_actions([
TestAction::inspect_context(|ctx| {
let fetcher = TestFetcher::default();
crate::fetch::register(fetcher, None, ctx).expect("failed to register fetch");
}),
TestAction::run(indoc! {r#"
try {
new Request("http://unit.test", { method: "HEAD", body: "x" });
throw Error("expected the call above to throw");
} catch (e) {
if (!(e instanceof TypeError)) {
throw e;
}
}
"#}),
]);
}

#[test]
fn request_constructor_get_with_inherited_empty_body_throws() {
run_test_actions([
TestAction::inspect_context(|ctx| {
let fetcher = TestFetcher::default();
crate::fetch::register(fetcher, None, ctx).expect("failed to register fetch");
}),
TestAction::run(indoc! {r#"
const request = new Request("http://unit.test", { method: "POST", body: "" });
try {
new Request(request, { method: "GET" });
throw Error("expected the call above to throw");
} catch (e) {
if (!(e instanceof TypeError)) {
throw e;
}
}
"#}),
]);
}

#[test]
fn request_constructor_head_with_inherited_empty_body_throws() {
run_test_actions([
TestAction::inspect_context(|ctx| {
let fetcher = TestFetcher::default();
crate::fetch::register(fetcher, None, ctx).expect("failed to register fetch");
}),
TestAction::run(indoc! {r#"
const request = new Request("http://unit.test", { method: "POST", body: "" });
try {
new Request(request, { method: "HEAD" });
throw Error("expected the call above to throw");
} catch (e) {
if (!(e instanceof TypeError)) {
throw e;
}
}
"#}),
]);
}

#[test]
fn request_clone_preserves_body_without_override() {
run_test_actions([
Expand Down
Loading