From 5194222e02d54158c47240ef78f7d3379a274eeb Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Sat, 28 Dec 2024 12:44:37 +0530 Subject: [PATCH] fix(ext/node): convert brotli chunks with proper byte offset (#27455) Fixes https://github.com/denoland/deno/issues/27029 Fixes https://github.com/denoland/deno/issues/26086 --- ext/node/polyfills/_brotli.js | 19 +++++++++--- tests/unit_node/http_test.ts | 58 +++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 4 deletions(-) diff --git a/ext/node/polyfills/_brotli.js b/ext/node/polyfills/_brotli.js index ebd0351561..108e5319a9 100644 --- a/ext/node/polyfills/_brotli.js +++ b/ext/node/polyfills/_brotli.js @@ -10,9 +10,12 @@ const { ArrayPrototypeMap, TypedArrayPrototypeSlice, TypedArrayPrototypeSubarray, - TypedArrayPrototypeGetByteLength, - DataViewPrototypeGetBuffer, TypedArrayPrototypeGetBuffer, + TypedArrayPrototypeGetByteLength, + TypedArrayPrototypeGetByteOffset, + DataViewPrototypeGetBuffer, + DataViewPrototypeGetByteLength, + DataViewPrototypeGetByteOffset, } = primordials; const { isTypedArray, isDataView, close } = core; import { @@ -40,9 +43,17 @@ const toU8 = (input) => { } if (isTypedArray(input)) { - return new Uint8Array(TypedArrayPrototypeGetBuffer(input)); + return new Uint8Array( + TypedArrayPrototypeGetBuffer(input), + TypedArrayPrototypeGetByteOffset(input), + TypedArrayPrototypeGetByteLength(input), + ); } else if (isDataView(input)) { - return new Uint8Array(DataViewPrototypeGetBuffer(input)); + return new Uint8Array( + DataViewPrototypeGetBuffer(input), + DataViewPrototypeGetByteOffset(input), + DataViewPrototypeGetByteLength(input), + ); } return input; diff --git a/tests/unit_node/http_test.ts b/tests/unit_node/http_test.ts index e6c36eea19..f30a4a20a3 100644 --- a/tests/unit_node/http_test.ts +++ b/tests/unit_node/http_test.ts @@ -10,6 +10,7 @@ import http, { } from "node:http"; import url from "node:url"; import https from "node:https"; +import zlib from "node:zlib"; import net, { Socket } from "node:net"; import fs from "node:fs"; import { text } from "node:stream/consumers"; @@ -1823,3 +1824,60 @@ Deno.test("[node/http] ServerResponse socket", async () => { await promise; }); + +Deno.test("[node/http] decompress brotli response", { + permissions: { net: true }, +}, async () => { + let received = false; + const ac = new AbortController(); + const server = Deno.serve({ port: 5928, signal: ac.signal }, (_req) => { + received = true; + return Response.json([ + ["accept-language", "*"], + ["host", "localhost:3000"], + ["user-agent", "Deno/2.1.1"], + ], {}); + }); + const { promise, resolve, reject } = Promise.withResolvers(); + let body = ""; + + const request = http.get( + "http://localhost:5928/", + { + headers: { + "accept-encoding": "gzip, deflate, br, zstd", + }, + }, + (resp) => { + const decompress = zlib.createBrotliDecompress(); + resp.on("data", (chunk) => { + decompress.write(chunk); + }); + + resp.on("end", () => { + decompress.end(); + }); + + decompress.on("data", (chunk) => { + body += chunk; + }); + + decompress.on("end", () => { + resolve(); + }); + }, + ); + request.on("error", reject); + request.end(() => { + assert(received); + }); + + await promise; + ac.abort(); + await server.finished; + + assertEquals(JSON.parse(body), [["accept-language", "*"], [ + "host", + "localhost:3000", + ], ["user-agent", "Deno/2.1.1"]]); +});