diff --git a/cli/tests/unit/http_test.ts b/cli/tests/unit/http_test.ts index 68500006f3..65ee555770 100644 --- a/cli/tests/unit/http_test.ts +++ b/cli/tests/unit/http_test.ts @@ -1672,6 +1672,72 @@ Deno.test({ }, }); +Deno.test({ + name: "http server updates content-length header if compression is applied", + permissions: { net: true }, + async fn() { + const hostname = "localhost"; + const port = 4501; + let contentLength: string; + + async function server() { + const listener = Deno.listen({ hostname, port }); + const tcpConn = await listener.accept(); + const httpConn = Deno.serveHttp(tcpConn); + const e = await httpConn.nextRequest(); + assert(e); + const { request, respondWith } = e; + assertEquals(request.headers.get("Accept-Encoding"), "gzip, deflate, br"); + const body = JSON.stringify({ + hello: "deno", + now: "with", + compressed: "body", + }); + contentLength = String(body.length); + const response = new Response( + body, + { + headers: { + "content-type": "application/json", + "content-length": contentLength, + }, + }, + ); + await respondWith(response); + httpConn.close(); + listener.close(); + } + + async function client() { + const url = `http://${hostname}:${port}/`; + const cmd = [ + "curl", + "-I", + "--request", + "GET", + "--url", + url, + "--header", + "Accept-Encoding: gzip, deflate, br", + ]; + const proc = Deno.run({ cmd, stdout: "piped", stderr: "null" }); + const status = await proc.status(); + assert(status.success); + const output = decoder.decode(await proc.output()); + assert(output.includes("vary: Accept-Encoding\r\n")); + assert(output.includes("content-encoding: gzip\r\n")); + // Ensure the content-length header is updated. + assert(!output.includes(`content-length: ${contentLength}\r\n`)); + assert(output.includes("content-length: 72\r\n")); + console.log(output); + + proc.close(); + } + + await Promise.all([server(), client()]); + }, +}); + function chunkedBodyReader(h: Headers, r: BufReader): Deno.Reader { // Based on https://tools.ietf.org/html/rfc2616#section-19.4.6 const tp = new TextProtoReader(r); diff --git a/ext/http/lib.rs b/ext/http/lib.rs index b70bed4644..ae5ddb9c3a 100644 --- a/ext/http/lib.rs +++ b/ext/http/lib.rs @@ -610,6 +610,10 @@ async fn op_http_write_headers( body_compressible && data.len() > 20 && accepts_compression; if should_compress { + // Drop 'content-length' header. Hyper will update it using compressed body. + if let Some(headers) = builder.headers_mut() { + headers.remove("content-length"); + } // If user provided a ETag header for uncompressed data, we need to // ensure it is a Weak Etag header ("W/"). if let Some(value) = etag_header { @@ -646,7 +650,7 @@ async fn op_http_write_headers( // https://nginx.org/en/docs/http/ngx_http_gzip_module.html#gzip_comp_level let mut writer = GzEncoder::new(Vec::new(), Compression::new(1)); writer.write_all(&data.into_bytes())?; - body = builder.body(writer.finish().unwrap().into())?; + body = builder.body(writer.finish()?.into())?; } } } else {