From 90d782139d0d43eec01fc43a816f46ca79dc4b7d Mon Sep 17 00:00:00 2001 From: Matteo Collina Date: Wed, 25 May 2022 11:00:51 +0200 Subject: [PATCH] fix: wait a macrotick to resume without pipelining (#1465) * Wait a macrotick to resume without pipelining This change makes the Client wait for a full macrotick before executing up the next request if pipelining is disabled. This is to account for socket errors events that might be waiting to be processed in the event queue. This is the expected behavior without pipelining. This will slow us down a bit without pipelinig. Fixes: https://github.com/nodejs/undici/issues/1415 Signed-off-by: Matteo Collina * fixup Signed-off-by: Matteo Collina --- lib/client.js | 5 +++++ test/inflight-and-close.js | 31 +++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 test/inflight-and-close.js diff --git a/lib/client.js b/lib/client.js index 1e23e7aad34..fea887e92b2 100644 --- a/lib/client.js +++ b/lib/client.js @@ -873,6 +873,11 @@ class Parser { // have been queued since then. util.destroy(socket, new InformationalError('reset')) return constants.ERROR.PAUSED + } else if (client[kPipelining] === 1) { + // We must wait a full event loop cycle to reuse this socket to make sure + // that non-spec compliant servers are not closing the connection even if they + // said they won't. + setImmediate(resume, client) } else { resume(client) } diff --git a/test/inflight-and-close.js b/test/inflight-and-close.js new file mode 100644 index 00000000000..49fbb10f7fc --- /dev/null +++ b/test/inflight-and-close.js @@ -0,0 +1,31 @@ +'use strict' + +const t = require('tap') +const { request } = require('..') +const http = require('http') + +const server = http.createServer((req, res) => { + res.writeHead(200) + res.end('Response body') + res.socket.end() // Close the connection immediately with every response +}).listen(0, '127.0.0.1', function () { + const url = `http://127.0.0.1:${this.address().port}` + request(url) + .then(({ statusCode, headers, body }) => { + t.pass('first response') + body.resume() + body.on('close', function () { + t.pass('first body closed') + }) + return request(url) + .then(({ statusCode, headers, body }) => { + t.pass('second response') + body.resume() + body.on('close', function () { + server.close() + }) + }) + }).catch((err) => { + t.error(err) + }) +})