diff --git a/cli/tests/unit/fetch_test.ts b/cli/tests/unit/fetch_test.ts index 98134728e4..8fe8db17e7 100644 --- a/cli/tests/unit/fetch_test.ts +++ b/cli/tests/unit/fetch_test.ts @@ -640,6 +640,7 @@ unitTest( const actual = new TextDecoder().decode((await bufPromise).bytes()); const expected = [ "POST /blah HTTP/1.1\r\n", + "content-length: 0\r\n", "hello: World\r\n", "foo: Bar\r\n", "accept: */*\r\n", @@ -1416,3 +1417,60 @@ unitTest( assertEquals(await res.text(), fixture); }, ); + +unitTest( + { permissions: { net: true } }, + async function fetchContentLengthPost() { + const response = await fetch("http://localhost:4545/content_length", { + method: "POST", + }); + const length = await response.text(); + assertEquals(length, 'Some("0")'); + }, +); + +unitTest( + { permissions: { net: true } }, + async function fetchContentLengthPut() { + const response = await fetch("http://localhost:4545/content_length", { + method: "PUT", + }); + const length = await response.text(); + assertEquals(length, 'Some("0")'); + }, +); + +unitTest( + { permissions: { net: true } }, + async function fetchContentLengthPatch() { + const response = await fetch("http://localhost:4545/content_length", { + method: "PATCH", + }); + const length = await response.text(); + assertEquals(length, "None"); + }, +); + +unitTest( + { permissions: { net: true } }, + async function fetchContentLengthPostWithStringBody() { + const response = await fetch("http://localhost:4545/content_length", { + method: "POST", + body: "Hey!", + }); + const length = await response.text(); + assertEquals(length, 'Some("4")'); + }, +); + +unitTest( + { permissions: { net: true } }, + async function fetchContentLengthPostWithBufferBody() { + const response = await fetch("http://localhost:4545/content_length", { + method: "POST", + body: new TextEncoder().encode("Hey!"), + }); + const length = await response.text(); + assertEquals(length, 'Some("4")'); + }, +); diff --git a/ext/fetch/lib.rs b/ext/fetch/lib.rs index b4bffb6de4..4bd62cd7cc 100644 --- a/ext/fetch/lib.rs +++ b/ext/fetch/lib.rs @@ -246,7 +246,7 @@ where let permissions = state.borrow_mut::(); permissions.check_net_url(&url)?; - let mut request = client.request(method, url); + let mut request = client.request(method.clone(), url); let request_body_rid = if args.has_body { match data { @@ -278,6 +278,11 @@ where } } } else { + // POST and PUT requests should always have a 0 length content-length, + // if there is no body. https://fetch.spec.whatwg.org/#http-network-or-cache-fetch + if matches!(method, Method::POST | Method::PUT) { + request = request.header(CONTENT_LENGTH, HeaderValue::from(0)); + } None }; diff --git a/test_util/src/lib.rs b/test_util/src/lib.rs index e57f55f1ee..06ca6464d0 100644 --- a/test_util/src/lib.rs +++ b/test_util/src/lib.rs @@ -851,6 +851,10 @@ async fn main_server( let version = format!("{:?}", req.version()); Ok(Response::new(version.into())) } + (_, "/content_length") => { + let content_length = format!("{:?}", req.headers().get("content-length")); + Ok(Response::new(content_length.into())) + } (_, "/jsx/jsx-runtime") | (_, "/jsx/jsx-dev-runtime") => { let mut res = Response::new(Body::from( r#"export function jsx(