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: #4543 PR-URL: https://github.com/nodejs/node/pull/4557 Reviewed-By: Brian White <mscdex@mscdex.net>
This commit is contained in:
parent
1ab6b21360
commit
5f76b24e5e
@ -421,6 +421,11 @@ not be emitted.
|
||||
`function (exception, socket) { }`
|
||||
|
||||
If a client connection emits an `'error'` event, it will be forwarded here.
|
||||
Listener of this event is responsible for closing/destroying the underlying
|
||||
socket. For example, one may wish to more gracefully close the socket with an
|
||||
HTTP '400 Bad Request' response instead of abruptly severing the connection.
|
||||
|
||||
Default behavior is to destroy the socket immediately on malformed request.
|
||||
|
||||
`socket` is the [`net.Socket`][] object that the error originated from.
|
||||
|
||||
|
@ -237,10 +237,6 @@ function Server(requestListener) {
|
||||
|
||||
this.addListener('connection', connectionListener);
|
||||
|
||||
this.addListener('clientError', function(err, conn) {
|
||||
conn.destroy(err);
|
||||
});
|
||||
|
||||
this.timeout = 2 * 60 * 1000;
|
||||
|
||||
this._pendingResponseData = 0;
|
||||
@ -353,7 +349,12 @@ function connectionListener(socket) {
|
||||
|
||||
// TODO(isaacs): Move all these functions out of here
|
||||
function socketOnError(e) {
|
||||
self.emit('clientError', e, this);
|
||||
// Ignore further errors
|
||||
this.removeListener('error', socketOnError);
|
||||
this.on('error', () => {});
|
||||
|
||||
if (!self.emit('clientError', e, this))
|
||||
this.destroy(e);
|
||||
}
|
||||
|
||||
function socketOnData(d) {
|
||||
@ -372,7 +373,7 @@ function connectionListener(socket) {
|
||||
function onParserExecuteCommon(ret, d) {
|
||||
if (ret instanceof Error) {
|
||||
debug('parse error');
|
||||
socket.destroy(ret);
|
||||
socketOnError.call(socket, ret);
|
||||
} else if (parser.incoming && parser.incoming.upgrade) {
|
||||
// Upgrade or CONNECT
|
||||
var bytesParsed = ret;
|
||||
@ -418,7 +419,7 @@ function connectionListener(socket) {
|
||||
|
||||
if (ret instanceof Error) {
|
||||
debug('parse error');
|
||||
socket.destroy(ret);
|
||||
socketOnError.call(socket, ret);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -29,8 +29,9 @@ function Server(opts, requestListener) {
|
||||
this.addListener('request', requestListener);
|
||||
}
|
||||
|
||||
this.addListener('clientError', function(err, conn) {
|
||||
conn.destroy();
|
||||
this.addListener('tlsClientError', function(err, conn) {
|
||||
if (!this.emit('clientError', err, conn))
|
||||
conn.destroy(err);
|
||||
});
|
||||
|
||||
this.timeout = 2 * 60 * 1000;
|
||||
|
@ -21,12 +21,6 @@ var server = http.Server(function(req, res) {
|
||||
server.close();
|
||||
}
|
||||
});
|
||||
|
||||
// since there is already clientError, maybe that would be appropriate,
|
||||
// since "error" is magical
|
||||
req.on('clientError', function() {
|
||||
console.log('Got clientError');
|
||||
});
|
||||
});
|
||||
|
||||
var responses = 0;
|
||||
|
39
test/parallel/test-http-server-client-error.js
Normal file
39
test/parallel/test-http-server-client-error.js
Normal file
@ -0,0 +1,39 @@
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
const assert = require('assert');
|
||||
|
||||
const http = require('http');
|
||||
const net = require('net');
|
||||
|
||||
const server = http.createServer(common.mustCall(function(req, res) {
|
||||
res.end();
|
||||
}));
|
||||
|
||||
server.on('clientError', common.mustCall(function(err, socket) {
|
||||
socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
|
||||
|
||||
server.close();
|
||||
}));
|
||||
|
||||
server.listen(common.PORT, function() {
|
||||
function next() {
|
||||
// Invalid request
|
||||
const client = net.connect(common.PORT);
|
||||
client.end('Oopsie-doopsie\r\n');
|
||||
|
||||
var chunks = '';
|
||||
client.on('data', function(chunk) {
|
||||
chunks += chunk;
|
||||
});
|
||||
client.once('end', function() {
|
||||
assert.equal(chunks, 'HTTP/1.1 400 Bad Request\r\n\r\n');
|
||||
});
|
||||
}
|
||||
|
||||
// Normal request
|
||||
http.get({ port: common.PORT, path: '/' }, function(res) {
|
||||
assert.equal(res.statusCode, 200);
|
||||
res.resume();
|
||||
res.once('end', next);
|
||||
});
|
||||
});
|
@ -32,6 +32,7 @@ server.on('clientError', function(err, conn) {
|
||||
assert.equal(conn._secureEstablished, false);
|
||||
server.close();
|
||||
clientErrors++;
|
||||
conn.destroy();
|
||||
});
|
||||
|
||||
server.listen(common.PORT, function() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user