node/test/parallel/test-http-client-spurious-aborted.js
Robert Nagy 8a6fab02ad http: emit 'error' on aborted server request
Server requests aka. IncomingMessage emits 'aborted'
instead of 'error' which causes confusion when
the object is used as a regular stream, i.e. if
functions working on streams are passed a
server request object they might not work properly
unless they take this into account.

Refs: https://github.com/nodejs/web-server-frameworks/issues/41

PR-URL: https://github.com/nodejs/node/pull/33172
Fixes: https://github.com/nodejs/node/issues/28172
Refs: https://github.com/nodejs/node/pull/28677
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Rich Trott <rtrott@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
2020-05-10 22:11:21 +02:00

85 lines
2.2 KiB
JavaScript

'use strict';
const common = require('../common');
const http = require('http');
const assert = require('assert');
const { Writable } = require('stream');
const Countdown = require('../common/countdown');
const N = 2;
let abortRequest = true;
const server = http.Server(common.mustCall((req, res) => {
const headers = { 'Content-Type': 'text/plain' };
headers['Content-Length'] = 50;
const socket = res.socket;
res.writeHead(200, headers);
res.write('aaaaaaaaaabbbbbbbbbbccccccccccdddddddddd');
if (abortRequest) {
process.nextTick(() => socket.destroy());
} else {
process.nextTick(() => res.end('eeeeeeeeee'));
}
}, N));
server.listen(0, common.mustCall(() => {
download();
}));
const finishCountdown = new Countdown(N, common.mustCall(() => {
server.close();
}));
const reqCountdown = new Countdown(N, common.mustCall());
function download() {
const opts = {
port: server.address().port,
path: '/',
};
const req = http.get(opts);
req.on('error', common.mustNotCall());
req.on('response', (res) => {
assert.strictEqual(res.statusCode, 200);
assert.strictEqual(res.headers.connection, 'close');
let aborted = false;
const writable = new Writable({
write(chunk, encoding, callback) {
callback();
}
});
res.pipe(writable);
const _handle = res.socket._handle;
_handle._close = res.socket._handle.close;
_handle.close = function(callback) {
_handle._close();
// Set readable to true even though request is complete
if (res.complete) res.readable = true;
callback();
};
if (!abortRequest) {
res.on('end', common.mustCall(() => {
reqCountdown.dec();
}));
res.on('error', common.mustNotCall());
} else {
res.on('aborted', common.mustCall(() => {
aborted = true;
reqCountdown.dec();
writable.end();
}));
res.on('error', common.expectsError({
code: 'ECONNRESET'
}));
}
writable.on('finish', () => {
assert.strictEqual(aborted, abortRequest);
finishCountdown.dec();
if (finishCountdown.remaining === 0) return;
abortRequest = false; // Next one should be a good response
download();
});
});
req.end();
}