Repository: axios/axios (js)

Issue:
Z_BUF_ERROR when content-encoding is set but the response body is empty
### Describe the bug

I get a `Z_BUF_ERROR` when requesting an endpoint that sets the `content-encoding` but does not return any response body.  See the snippet for server and client. 

The error
```
AxiosError: unexpected end of file
    at Function.AxiosError.from (/Users/joona.paakkonen/git/test-server/node_modules/axios/dist/node/axios.cjs:725:14)
    at Unzip.handleStreamError (/Users/joona.paakkonen/git/test-server/node_modules/axios/dist/node/axios.cjs:2430:29)
    at Unzip.emit (node:events:532:35)
    at emitErrorNT (node:internal/streams/destroy:157:8)
    at emitErrorCloseNT (node:internal/streams/destroy:122:3)
    at processTicksAndRejections (node:internal/process/task_queues:83:21)
```

I checked that the logic [here](https://github.com/lejahmie/axios/blob/master/lib/adapters/http.js#L275) does not seem to make much sense, the `data` is not the response data but rather the request data. And we don't seem to have request data available since it's still left to be streamed. Perhaps the decompression should happen after the response has been received. The old logic where `HTTP 204` response code was used to make the decision was probably better.

### To Reproduce

Test server
```
const express = require('express');
const compression = require('compression');
const app = express();
const port = 3000;

app.use(express.json())
app.use(compression({ threshold: 0 }));

app.get('/success-1', (request, response) => {
  response.end();
});

app.get('/success-2', (request, response) => {
  response.json({foo: 'bar'});
  response.end();
});

app.get('/fail-1', (request, response) => {
  response.setHeader('content-encoding', 'gzip');
  response.end();
});

const server = app.listen(port, () => {
  console.log(`Listening at http://localhost:${port}`);
});
```

Test client
```
const axios = require('axios');
const PATHS = ['success-1', 'success-2', 'fail-1'];

(async () => {
  for (const path of PATHS) {
    try {
      const response = await axios.request({
        method: 'GET',
        url: `http://localhost:3000/${path}`,
      });
      console.log(path, response.data, 'SUCCESS!');
    } catch (error) {
      console.log(path, 'FAIL!');
    }
  }
})();
```

### Code snippet

_No response_

### Expected behavior

I would expect axios request to not fail when even when the response body is missing. This was the behaviour for 0.27.2

### Axios Version

1.1.3

### Adapter Version

XHR/HTTP

### Browser

_No response_

### Browser Version

_No response_

### Node.js Version

v16.14.0

### OS

OSX

### Additional Library Versions

_No response_

### Additional context/Screenshots

_No response_

Pull request:
Fixed Z_BUF_ERROR when content-encoding is set but the response body is empty
- Fixed Z_BUF_ERROR when content-encoding is set but the response body is empty;
- Fixed download progress capturing for compressed responses;

Closes #5246;

Patch:
diff --git a/lib/adapters/http.js b/lib/adapters/http.js
index b561645..5dbd97c 100755
--- a/lib/adapters/http.js
+++ b/lib/adapters/http.js
@@ -373,6 +373,23 @@ export default function httpAdapter(config) {
 
       const streams = [res];
 
+      const responseLength = +res.headers['content-length'];
+
+      if (onDownloadProgress) {
+        const transformStream = new AxiosTransformStream({
+          length: utils.toFiniteNumber(responseLength),
+          maxRate: utils.toFiniteNumber(maxDownloadRate)
+        });
+
+        onDownloadProgress && transformStream.on('progress', progress => {
+          onDownloadProgress(Object.assign(progress, {
+            download: true
+          }));
+        });
+
+        streams.push(transformStream);
+      }
+
       // uncompress the response body transparently if required
       let responseStream = res;
 
@@ -383,7 +400,7 @@ export default function httpAdapter(config) {
       if (config.decompress !== false) {
         // if no content, but headers still say that it is encoded,
         // remove the header not confuse downstream operations
-        if (data && data.length === 0 && res.headers['content-encoding']) {
+        if ((!responseLength || res.statusCode === 204) && res.headers['content-encoding']) {
           delete res.headers['content-encoding'];
         }
 
@@ -406,23 +423,6 @@ export default function httpAdapter(config) {
         }
       }
 
-      if (onDownloadProgress) {
-        const responseLength = +res.headers['content-length'];
-
-        const transformStream = new AxiosTransformStream({
-          length: utils.toFiniteNumber(responseLength),
-          maxRate: utils.toFiniteNumber(maxDownloadRate)
-        });
-
-        onDownloadProgress && transformStream.on('progress', progress => {
-          onDownloadProgress(Object.assign(progress, {
-            download: true
-          }));
-        });
-
-        streams.push(transformStream);
-      }
-
       responseStream = streams.length > 1 ? stream.pipeline(streams, utils.noop) : streams[0];
 
       const offListeners = stream.finished(responseStream, () => {


Test patch:
diff --git a/test/unit/adapters/http.js b/test/unit/adapters/http.js
index fba276d..92a11a7 100644
--- a/test/unit/adapters/http.js
+++ b/test/unit/adapters/http.js
@@ -47,9 +47,14 @@ var noop = ()=> {};
 
 const LOCAL_SERVER_URL = 'http://localhost:4444';
 
-function startHTTPServer({useBuffering= false, rate = undefined, port = 4444} = {}) {
+function startHTTPServer(options) {
+
+  const {handler, useBuffering = false, rate = undefined, port = 4444} = typeof options === 'function' ? {
+    handler: options
+  } : options || {};
+
   return new Promise((resolve, reject) => {
-    http.createServer(async function (req, res) {
+    http.createServer(handler || async function (req, res) {
       try {
         req.headers['content-length'] && res.setHeader('content-length', req.headers['content-length']);
 
@@ -426,58 +431,71 @@ describe('supports http with nodejs', function () {
     });
   });
 
-  it('should support transparent gunzip', function (done) {
-    var data = {
-      firstName: 'Fred',
-      lastName: 'Flintstone',
-      emailAddr: 'fred@example.com'
-    };
+  describe('compression', () => {
+    it('should support transparent gunzip', function (done) {
+      var data = {
+        firstName: 'Fred',
+        lastName: 'Flintstone',
+        emailAddr: 'fred@example.com'
+      };
 
-    zlib.gzip(JSON.stringify(data), function (err, zipped) {
+      zlib.gzip(JSON.stringify(data), function (err, zipped) {
 
-      server = http.createServer(function (req, res) {
-        res.setHeader('Content-Type', 'application/json');
-        res.setHeader('Content-Encoding', 'gzip');
-        res.end(zipped);
-      }).listen(4444, function () {
-        axios.get('http://localhost:4444/').then(function (res) {
-          assert.deepEqual(res.data, data);
-          done();
-        }).catch(done);
+        server = http.createServer(function (req, res) {
+          res.setHeader('Content-Type', 'application/json');
+          res.setHeader('Content-Encoding', 'gzip');
+          res.end(zipped);
+        }).listen(4444, function () {
+          axios.get('http://localhost:4444/').then(function (res) {
+            assert.deepEqual(res.data, data);
+            done();
+          }).catch(done);
+        });
       });
     });
-  });
 
-  it('should support gunzip error handling', function (done) {
-    server = http.createServer(function (req, res) {
+  it('should support gunzip error handling', async () => {
+    server = await startHTTPServer((req, res) => {
       res.setHeader('Content-Type', 'application/json');
       res.setHeader('Content-Encoding', 'gzip');
       res.end('invalid response');
-    }).listen(4444, function () {
-      axios.get('http://localhost:4444/').catch(function (error) {
-        done();
-      }).catch(done);
     });
+
+    await assert.rejects(async ()=> {
+      await axios.get(LOCAL_SERVER_URL);
+    })
   });
 
-  it('should support disabling automatic decompression of response data', function(done) {
-    var data = 'Test data';
+    it('should support disabling automatic decompression of response data', function(done) {
+      var data = 'Test data';
+
+      zlib.gzip(data, function(err, zipped) {
+        server = http.createServer(function(req, res) {
+          res.setHeader('Content-Type', 'text/html;charset=utf-8');
+          res.setHeader('Content-Encoding', 'gzip');
+          res.end(zipped);
+        }).listen(4444, function() {
+          axios.get('http://localhost:4444/', {
+            decompress: false,
+            responseType: 'arraybuffer'
+
+          }).then(function(res) {
+            assert.equal(res.data.toString('base64'), zipped.toString('base64'));
+            done();
+          }).catch(done);
+        });
+      });
+    });
+
+    it('should properly handle empty responses without Z_BUF_ERROR throwing', async () => {
+      this.timeout(10000);
 
-    zlib.gzip(data, function(err, zipped) {
-      server = http.createServer(function(req, res) {
-        res.setHeader('Content-Type', 'text/html;charset=utf-8');
+      server = await startHTTPServer((req, res) => {
         res.setHeader('Content-Encoding', 'gzip');
-        res.end(zipped);
-      }).listen(4444, function() {
-        axios.get('http://localhost:4444/', {
-          decompress: false,
-          responseType: 'arraybuffer'
-
-        }).then(function(res) {
-          assert.equal(res.data.toString('base64'), zipped.toString('base64'));
-          done();
-        }).catch(done);
+        res.end();
       });
+
+      await axios.get(LOCAL_SERVER_URL);
     });
   });
 
@@ -1887,7 +1905,6 @@ describe('supports http with nodejs', function () {
     });
   });
 
-
   describe('request aborting', function() {
     it('should be able to abort the response stream', async function () {
       server = await startHTTPServer({