summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndy Kurnia <andy-k@users.noreply.github.com>2022-05-13 20:10:05 +0800
committerGitHub <noreply@github.com>2022-05-13 14:10:05 +0200
commit42fec5150ecbd80f110d2bb32f1dcd2b2a344dd3 (patch)
tree414d12de7ae1b9776a7a5ab2fa6245be7d19631f
parenteed7afc3aa07f52668c99f608d044e2f1902cca8 (diff)
fix(ext/http): make serveHttp compress for Accept-Encoding: deflate, gzip (#14525)
-rw-r--r--cli/tests/unit/http_test.ts60
-rw-r--r--ext/http/lib.rs26
2 files changed, 82 insertions, 4 deletions
diff --git a/cli/tests/unit/http_test.ts b/cli/tests/unit/http_test.ts
index e9fb61dc4..3f9141470 100644
--- a/cli/tests/unit/http_test.ts
+++ b/cli/tests/unit/http_test.ts
@@ -1918,6 +1918,66 @@ Deno.test({
},
});
+Deno.test({
+ name: "http server compresses when accept-encoding is deflate, gzip",
+ permissions: { net: true, run: 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"), "deflate, gzip");
+ const body = "x".repeat(10000);
+ contentLength = String(body.length);
+ const response = new Response(
+ body,
+ {
+ headers: {
+ "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,
+ // "--compressed", // Windows curl does not support --compressed
+ "--header",
+ "Accept-Encoding: deflate, gzip",
+ ];
+ 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: 80\r\n"));
+ proc.close();
+ }
+
+ await Promise.all([server(), client()]);
+ },
+});
+
Deno.test("upgradeHttp tcp", async () => {
async function client() {
const tcpConn = await Deno.connect({ port: 4501 });
diff --git a/ext/http/lib.rs b/ext/http/lib.rs
index d1d884457..edc4c1e83 100644
--- a/ext/http/lib.rs
+++ b/ext/http/lib.rs
@@ -392,10 +392,28 @@ async fn op_http_accept(
{
let mut accept_encoding = stream.accept_encoding.borrow_mut();
- *accept_encoding = fly_accept_encoding::parse(request.headers())
- .ok()
- .flatten()
- .unwrap_or(Encoding::Identity);
+
+ // curl --compressed sends "Accept-Encoding: deflate, gzip".
+ // fly_accept_encoding::parse() returns Encoding::Deflate.
+ // Deno does not support Encoding::Deflate.
+ // So, Deno used no compression, although gzip was possible.
+ // This patch makes Deno use gzip instead in this case.
+ *accept_encoding = Encoding::Identity;
+ let mut max_qval = 0.0;
+ if let Ok(encodings) = fly_accept_encoding::encodings(request.headers()) {
+ for (encoding, qval) in encodings {
+ if let Some(enc @ (Encoding::Brotli | Encoding::Gzip)) = encoding {
+ // this logic came from fly_accept_encoding.
+ if (qval - 1.0f32).abs() < 0.01 {
+ *accept_encoding = enc;
+ break;
+ } else if qval > max_qval {
+ *accept_encoding = enc;
+ max_qval = qval;
+ }
+ }
+ }
+ }
}
let method = request.method().to_string();