From a2663770d619fa9610acbf1705126eea50bb0056 Mon Sep 17 00:00:00 2001 From: Yefis <39944043+SofianD@users.noreply.github.com> Date: Thu, 25 Jan 2024 23:11:42 +0100 Subject: [PATCH] fix(utils): now parseUndiciResponse handles compressed data before to converting it (#223) --- src/utils.ts | 17 +++++++++++++--- test/utils.spec.ts | 49 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 3 deletions(-) diff --git a/src/utils.ts b/src/utils.ts index 9aa0be4..fb5cb6c 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,7 +1,9 @@ /* eslint-disable no-redeclare */ // Import Node.js Dependencies -import { IncomingHttpHeaders } from "http"; +import { gunzip } from "node:zlib"; +import { promisify } from "node:util"; +import { IncomingHttpHeaders } from "node:http"; // Import Third-party Dependencies import * as contentType from "content-type"; @@ -17,6 +19,7 @@ const kDefaultEncodingCharset = "utf-8"; const kCharsetConversionTable = { "ISO-8859-1": "latin1" }; +const kAsyncGunzip = promisify(gunzip); export const DEFAULT_HEADER = { "user-agent": kDefaultUserAgent }; @@ -41,13 +44,21 @@ export function getEncodingCharset(charset = kDefaultEncodingCharset): BufferEnc * If the response as a content type equal to 'application/json' we automatically parse it with JSON.parse(). */ export async function parseUndiciResponse(response: Dispatcher.ResponseData): Promise { - const body = await response.body.text(); const contentTypeHeader = response.headers["content-type"] as string | undefined; - const { type } = contentType.parse( + const { type, parameters } = contentType.parse( contentTypeHeader ?? kDefaultMimeType ); + let body; try { + if (response.headers["content-encoding"] === "gzip") { + const buf = await response.body.arrayBuffer(); + body = (await kAsyncGunzip(buf)).toString(getEncodingCharset(parameters.charset)); + } + else { + body = await response.body.text(); + } + return type === "application/json" && body ? JSON.parse(body) : body; } catch (error) { diff --git a/test/utils.spec.ts b/test/utils.spec.ts index db818ae..faba545 100644 --- a/test/utils.spec.ts +++ b/test/utils.spec.ts @@ -1,5 +1,6 @@ // Import Node.js Dependencies import { IncomingHttpHeaders } from "http2"; +import { gzipSync } from "zlib"; import stream from "stream"; // Import Internal Dependencies @@ -217,6 +218,54 @@ describe("parseUndiciResponse", () => { expect(data).toStrictEqual(payload); }); + + it("must unzip data when there is a 'content-encoding' header set with 'gzip' before to converting it to a string", async() => { + const payload = "hello world!"; + const body: any = { + async arrayBuffer() { + return gzipSync(payload); + } + }; + const data = await Utils.parseUndiciResponse({ + ...defaultUndiciResponseMeta, body, headers: { + "content-encoding": "gzip" + } + }); + + expect(data).toStrictEqual(payload); + }); + + it("must unzip data when there is a 'content-encoding' header set with 'gzip' before to converting it to JSON", async() => { + const payload = { foo: "hello world!" }; + const body: any = { + async arrayBuffer() { + return gzipSync(JSON.stringify(payload)); + } + }; + + const data = await Utils.parseUndiciResponse({ + ...defaultUndiciResponseMeta, body, headers: { + "content-encoding": "gzip", + "content-type": "application/json; charset=utf-8" + } + }); + + expect(data).toStrictEqual(payload); + }); + + it("should not unzip data when 'content-encoding' header is not set", async() => { + const payload = "hello world!"; + const buf = gzipSync(payload); + const body: any = { + text: () => buf.toString() + }; + + const data = await Utils.parseUndiciResponse({ + ...defaultUndiciResponseMeta, body, headers: {} + }); + + expect(data).toStrictEqual(buf.toString()); + }); }); describe("getCurrentEnv", () => {