Skip to content

Commit 822a3c3

Browse files
fix: Remove content-encoding header from already decompressed responses (#54)
When the `compress` flag is set to true, the fetch implementation sends an Accept-Encoding header and decompresses the response. However, the decompressed body is put into a `Response` that still has a `Content-Encoding` that no longer matches the actual encoding of the body. This causes issues if this `Response` object is used downstream (i.e. sent back to a browser), and there is another attempt to decode the body based on the content-encoding header. Co-authored-by: Jacob Ebey <[email protected]>
1 parent 6ce472d commit 822a3c3

File tree

3 files changed

+14
-2
lines changed

3 files changed

+14
-2
lines changed

.changeset/dirty-moles-exercise.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@remix-run/web-fetch": patch
3+
---
4+
5+
fix: Remove content-encoding header from already decompressed responses. This eases the use of fetch in senarios where you wish to use it as a sort of makeshift proxy.

packages/fetch/src/fetch.js

+3
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,7 @@ async function fetch(url, options_ = {}) {
277277

278278
// For gzip
279279
if (codings === 'gzip' || codings === 'x-gzip') {
280+
responseOptions.headers.delete("Content-Encoding");
280281
body = pump(body, zlib.createGunzip(zlibOptions), reject);
281282
response = new Response(fromAsyncIterable(body), responseOptions);
282283
resolve(response);
@@ -285,6 +286,7 @@ async function fetch(url, options_ = {}) {
285286

286287
// For deflate
287288
if (codings === 'deflate' || codings === 'x-deflate') {
289+
responseOptions.headers.delete("Content-Encoding");
288290
// Handle the infamous raw deflate response from old servers
289291
// a hack for old IIS and Apache servers
290292
const raw = pump(response_, new PassThrough(), reject);
@@ -304,6 +306,7 @@ async function fetch(url, options_ = {}) {
304306

305307
// For br
306308
if (codings === 'br') {
309+
responseOptions.headers.delete("Content-Encoding");
307310
body = pump(body, zlib.createBrotliDecompress(), reject);
308311
response = new Response(fromAsyncIterable(body), responseOptions);
309312
resolve(response);

packages/fetch/test/main.js

+6-2
Original file line numberDiff line numberDiff line change
@@ -818,6 +818,7 @@ describe("node-fetch", () => {
818818
const url = `${base}gzip`;
819819
return fetch(url).then((res) => {
820820
expect(res.headers.get("content-type")).to.equal("text/plain");
821+
expect(res.headers.get("content-encoding")).to.be.null;
821822
return res.text().then((result) => {
822823
expect(result).to.be.a("string");
823824
expect(result).to.equal("hello world");
@@ -836,10 +837,9 @@ describe("node-fetch", () => {
836837
});
837838
});
838839

839-
it("should make capitalised Content-Encoding lowercase", () => {
840+
it("should decompress capitalised Content-Encoding", () => {
840841
const url = `${base}gzip-capital`;
841842
return fetch(url).then((res) => {
842-
expect(res.headers.get("content-encoding")).to.equal("gzip");
843843
return res.text().then((result) => {
844844
expect(result).to.be.a("string");
845845
expect(result).to.equal("hello world");
@@ -851,6 +851,7 @@ describe("node-fetch", () => {
851851
const url = `${base}deflate`;
852852
return fetch(url).then((res) => {
853853
expect(res.headers.get("content-type")).to.equal("text/plain");
854+
expect(res.headers.get("content-encoding")).to.be.null;
854855
return res.text().then((result) => {
855856
expect(result).to.be.a("string");
856857
expect(result).to.equal("hello world");
@@ -877,6 +878,7 @@ describe("node-fetch", () => {
877878
const url = `${base}brotli`;
878879
return fetch(url).then((res) => {
879880
expect(res.headers.get("content-type")).to.equal("text/plain");
881+
expect(res.headers.get("content-encoding")).to.be.null;
880882
return res.text().then((result) => {
881883
expect(result).to.be.a("string");
882884
expect(result).to.equal("hello world");
@@ -906,6 +908,7 @@ describe("node-fetch", () => {
906908
const url = `${base}sdch`;
907909
return fetch(url).then((res) => {
908910
expect(res.headers.get("content-type")).to.equal("text/plain");
911+
expect(res.headers.get("content-encoding")).to.equal("sdch");
909912
return res.text().then((result) => {
910913
expect(result).to.be.a("string");
911914
expect(result).to.equal("fake sdch string");
@@ -957,6 +960,7 @@ describe("node-fetch", () => {
957960
};
958961
return fetch(url, options).then((res) => {
959962
expect(res.headers.get("content-type")).to.equal("text/plain");
963+
expect(res.headers.get("content-encoding")).to.equal("gzip");
960964
return res.text().then((result) => {
961965
expect(result).to.be.a("string");
962966
expect(result).to.not.equal("hello world");

0 commit comments

Comments
 (0)