Skip to content

Commit 10706c9

Browse files
indutnyMichael Scovetta
authored and
Michael Scovetta
committed
http: overridable clientError
Make default `clientError` behavior (close socket immediately) overridable. With this APIs it is possible to write a custom error handler, and to send, for example, a 400 HTTP response. http.createServer(...).on('clientError', function(err, socket) { socket.end('HTTP/1.1 400 Bad Request\r\n\r\n'); socket.destroy(); }); Fix: nodejs#4543 PR-URL: nodejs#4557 Reviewed-By: Brian White <[email protected]>
1 parent f8f9f6d commit 10706c9

6 files changed

+56
-15
lines changed

doc/api/http.markdown

+5
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,11 @@ not be emitted.
421421
`function (exception, socket) { }`
422422

423423
If a client connection emits an `'error'` event, it will be forwarded here.
424+
Listener of this event is responsible for closing/destroying the underlying
425+
socket. For example, one may wish to more gracefully close the socket with an
426+
HTTP '400 Bad Request' response instead of abruptly severing the connection.
427+
428+
Default behavior is to destroy the socket immediately on malformed request.
424429

425430
`socket` is the [`net.Socket`][] object that the error originated from.
426431

lib/_http_server.js

+8-7
Original file line numberDiff line numberDiff line change
@@ -237,10 +237,6 @@ function Server(requestListener) {
237237

238238
this.addListener('connection', connectionListener);
239239

240-
this.addListener('clientError', function(err, conn) {
241-
conn.destroy(err);
242-
});
243-
244240
this.timeout = 2 * 60 * 1000;
245241

246242
this._pendingResponseData = 0;
@@ -353,7 +349,12 @@ function connectionListener(socket) {
353349

354350
// TODO(isaacs): Move all these functions out of here
355351
function socketOnError(e) {
356-
self.emit('clientError', e, this);
352+
// Ignore further errors
353+
this.removeListener('error', socketOnError);
354+
this.on('error', () => {});
355+
356+
if (!self.emit('clientError', e, this))
357+
this.destroy(e);
357358
}
358359

359360
function socketOnData(d) {
@@ -372,7 +373,7 @@ function connectionListener(socket) {
372373
function onParserExecuteCommon(ret, d) {
373374
if (ret instanceof Error) {
374375
debug('parse error');
375-
socket.destroy(ret);
376+
socketOnError.call(socket, ret);
376377
} else if (parser.incoming && parser.incoming.upgrade) {
377378
// Upgrade or CONNECT
378379
var bytesParsed = ret;
@@ -418,7 +419,7 @@ function connectionListener(socket) {
418419

419420
if (ret instanceof Error) {
420421
debug('parse error');
421-
socket.destroy(ret);
422+
socketOnError.call(socket, ret);
422423
return;
423424
}
424425

lib/https.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,9 @@ function Server(opts, requestListener) {
2929
this.addListener('request', requestListener);
3030
}
3131

32-
this.addListener('clientError', function(err, conn) {
33-
conn.destroy();
32+
this.addListener('tlsClientError', function(err, conn) {
33+
if (!this.emit('clientError', err, conn))
34+
conn.destroy(err);
3435
});
3536

3637
this.timeout = 2 * 60 * 1000;

test/parallel/test-http-client-abort.js

-6
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,6 @@ var server = http.Server(function(req, res) {
2121
server.close();
2222
}
2323
});
24-
25-
// since there is already clientError, maybe that would be appropriate,
26-
// since "error" is magical
27-
req.on('clientError', function() {
28-
console.log('Got clientError');
29-
});
3024
});
3125

3226
var responses = 0;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
'use strict';
2+
const common = require('../common');
3+
const assert = require('assert');
4+
5+
const http = require('http');
6+
const net = require('net');
7+
8+
const server = http.createServer(common.mustCall(function(req, res) {
9+
res.end();
10+
}));
11+
12+
server.on('clientError', common.mustCall(function(err, socket) {
13+
socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
14+
15+
server.close();
16+
}));
17+
18+
server.listen(common.PORT, function() {
19+
function next() {
20+
// Invalid request
21+
const client = net.connect(common.PORT);
22+
client.end('Oopsie-doopsie\r\n');
23+
24+
var chunks = '';
25+
client.on('data', function(chunk) {
26+
chunks += chunk;
27+
});
28+
client.once('end', function() {
29+
assert.equal(chunks, 'HTTP/1.1 400 Bad Request\r\n\r\n');
30+
});
31+
}
32+
33+
// Normal request
34+
http.get({ port: common.PORT, path: '/' }, function(res) {
35+
assert.equal(res.statusCode, 200);
36+
res.resume();
37+
res.once('end', next);
38+
});
39+
});

test/parallel/test-https-timeout-server.js

+1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ server.on('clientError', function(err, conn) {
3232
assert.equal(conn._secureEstablished, false);
3333
server.close();
3434
clientErrors++;
35+
conn.destroy();
3536
});
3637

3738
server.listen(common.PORT, function() {

0 commit comments

Comments
 (0)