summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDimitris Halatsis <dimitris_ha@hotmail.com>2021-01-10 18:35:12 +0200
committerBeth Griggs <bgriggs@redhat.com>2021-01-28 17:56:03 +0000
commitc03cddb46f34b653482e20db343a605d4aa40d1b (patch)
tree1cd0f5db69fad47fc84eaf127a18fa37220576f0
parent2a1e4e9244a1183cd2155a4d2261a3c24dcacc34 (diff)
downloadnode-new-c03cddb46f34b653482e20db343a605d4aa40d1b.tar.gz
test: http complete response after socket double end
PR-URL: https://github.com/nodejs/node/pull/36633 Backport-PR-URL: https://github.com/nodejs/node/pull/36940 Fixes: https://github.com/nodejs/node/issues/36620 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Danielle Adams <adamzdanielle@gmail.com>
-rw-r--r--test/parallel/test-http-outgoing-end-cork.js95
-rw-r--r--test/parallel/test-http-outgoing-end-multiple.js3
2 files changed, 98 insertions, 0 deletions
diff --git a/test/parallel/test-http-outgoing-end-cork.js b/test/parallel/test-http-outgoing-end-cork.js
new file mode 100644
index 0000000000..6a217238c4
--- /dev/null
+++ b/test/parallel/test-http-outgoing-end-cork.js
@@ -0,0 +1,95 @@
+'use strict';
+const common = require('../common');
+const assert = require('assert');
+const http = require('http');
+
+const REQ_TIMEOUT = 500; // Set max ms of request time before abort
+
+// Set total allowed test timeout to avoid infinite loop
+// that will hang test suite
+const TOTAL_TEST_TIMEOUT = 1000;
+
+// Placeholder for sockets handled, to make sure that we
+// will reach a socket re-use case.
+const handledSockets = new Set();
+
+let metReusedSocket = false; // Flag for request loop termination.
+
+const doubleEndResponse = (res) => {
+ // First end the request while sending some normal data
+ res.end('regular end of request', 'utf8', common.mustCall());
+ // Make sure the response socket is uncorked after first call of end
+ assert.strictEqual(res.writableCorked, 0);
+ res.end(); // Double end the response to prep for next socket re-use.
+};
+
+const sendDrainNeedingData = (res) => {
+ // Send data to socket more than the high watermark so that
+ // it definitely needs drain
+ const highWaterMark = res.socket.writableHighWaterMark;
+ const bufferToSend = Buffer.alloc(highWaterMark + 100);
+ const ret = res.write(bufferToSend); // Write the request data.
+ // Make sure that we had back pressure on response stream.
+ assert.strictEqual(ret, false);
+ res.once('drain', () => res.end()); // End on drain.
+};
+
+const server = http.createServer((req, res) => {
+ const { socket: responseSocket } = res;
+ if (handledSockets.has(responseSocket)) { // re-used socket, send big data!
+ metReusedSocket = true; // stop request loop
+ console.debug('FOUND REUSED SOCKET!');
+ sendDrainNeedingData(res);
+ } else { // not used again
+ // add to make sure we recognise it when we meet socket again
+ handledSockets.add(responseSocket);
+ doubleEndResponse(res);
+ }
+});
+
+server.listen(0); // Start the server on a random port.
+
+const sendRequest = (agent) => new Promise((resolve, reject) => {
+ const timeout = setTimeout(common.mustNotCall(() => {
+ reject(new Error('Request timed out'));
+ }), REQ_TIMEOUT);
+ http.get({
+ port: server.address().port,
+ path: '/',
+ agent
+ }, common.mustCall((res) => {
+ const resData = [];
+ res.on('data', (data) => resData.push(data));
+ res.on('end', common.mustCall(() => {
+ const totalData = resData.reduce((total, elem) => total + elem.length, 0);
+ clearTimeout(timeout); // Cancel rejection timeout.
+ resolve(totalData); // fulfill promise
+ }));
+ }));
+});
+
+server.once('listening', async () => {
+ const testTimeout = setTimeout(common.mustNotCall(() => {
+ console.error('Test running for a while but could not met re-used socket');
+ process.exit(1);
+ }), TOTAL_TEST_TIMEOUT);
+ // Explicitly start agent to force socket reuse.
+ const agent = new http.Agent({ keepAlive: true });
+ // Start the request loop
+ let reqNo = 0;
+ while (!metReusedSocket) {
+ try {
+ console.log(`Sending req no ${++reqNo}`);
+ const totalData = await sendRequest(agent);
+ console.log(`${totalData} bytes were received for request ${reqNo}`);
+ } catch (err) {
+ console.error(err);
+ process.exit(1);
+ }
+ }
+ // Successfully tested conditions and ended loop
+ clearTimeout(testTimeout);
+ console.log('Closing server');
+ agent.destroy();
+ server.close();
+});
diff --git a/test/parallel/test-http-outgoing-end-multiple.js b/test/parallel/test-http-outgoing-end-multiple.js
index 7c43e1f59d..2297f68cfe 100644
--- a/test/parallel/test-http-outgoing-end-multiple.js
+++ b/test/parallel/test-http-outgoing-end-multiple.js
@@ -5,12 +5,15 @@ const http = require('http');
const server = http.createServer(common.mustCall(function(req, res) {
res.end('testing ended state', common.mustCall());
+ assert.strictEqual(res.writableCorked, 0);
res.end(common.mustCall());
+ assert.strictEqual(res.writableCorked, 0);
res.on('finish', common.mustCall(() => {
res.end(common.mustCall((err) => {
assert.strictEqual(err.code, 'ERR_STREAM_ALREADY_FINISHED');
server.close();
}));
+ assert.strictEqual(res.writableCorked, 0);
}));
}));