http: guard against failed sockets creation
PR-URL: https://github.com/nodejs/node/pull/13839 Fixes: https://github.com/nodejs/node/issues/13045 Fixes: https://github.com/nodejs/node/issues/13831 Refs: https://github.com/nodejs/node/issues/13352 Refs: https://github.com/nodejs/node/issues/13548#issuecomment-307177400 Reviewed-By: Trevor Norris <trev.norris@gmail.com>
This commit is contained in:
parent
380929ec0c
commit
4b276e985f
@ -181,15 +181,7 @@ Agent.prototype.addRequest = function addRequest(req, options, port/*legacy*/,
|
|||||||
} else if (sockLen < this.maxSockets) {
|
} else if (sockLen < this.maxSockets) {
|
||||||
debug('call onSocket', sockLen, freeLen);
|
debug('call onSocket', sockLen, freeLen);
|
||||||
// If we are under maxSockets create a new one.
|
// If we are under maxSockets create a new one.
|
||||||
this.createSocket(req, options, function(err, newSocket) {
|
this.createSocket(req, options, handleSocketCreation(req, true));
|
||||||
if (err) {
|
|
||||||
nextTick(newSocket._handle.getAsyncId(), function() {
|
|
||||||
req.emit('error', err);
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
req.onSocket(newSocket);
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
debug('wait for socket');
|
debug('wait for socket');
|
||||||
// We are over limit so we'll add it to the queue.
|
// We are over limit so we'll add it to the queue.
|
||||||
@ -222,6 +214,7 @@ Agent.prototype.createSocket = function createSocket(req, options, cb) {
|
|||||||
const newSocket = self.createConnection(options, oncreate);
|
const newSocket = self.createConnection(options, oncreate);
|
||||||
if (newSocket)
|
if (newSocket)
|
||||||
oncreate(null, newSocket);
|
oncreate(null, newSocket);
|
||||||
|
|
||||||
function oncreate(err, s) {
|
function oncreate(err, s) {
|
||||||
if (called)
|
if (called)
|
||||||
return;
|
return;
|
||||||
@ -294,15 +287,7 @@ Agent.prototype.removeSocket = function removeSocket(s, options) {
|
|||||||
debug('removeSocket, have a request, make a socket');
|
debug('removeSocket, have a request, make a socket');
|
||||||
var req = this.requests[name][0];
|
var req = this.requests[name][0];
|
||||||
// If we have pending requests and a socket gets closed make a new one
|
// If we have pending requests and a socket gets closed make a new one
|
||||||
this.createSocket(req, options, function(err, newSocket) {
|
this.createSocket(req, options, handleSocketCreation(req, false));
|
||||||
if (err) {
|
|
||||||
nextTick(newSocket._handle.getAsyncId(), function() {
|
|
||||||
req.emit('error', err);
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
newSocket.emit('free');
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -332,6 +317,22 @@ Agent.prototype.destroy = function destroy() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function handleSocketCreation(request, informRequest) {
|
||||||
|
return function handleSocketCreation_Inner(err, socket) {
|
||||||
|
if (err) {
|
||||||
|
const asyncId = (socket && socket._handle && socket._handle.getAsyncId) ?
|
||||||
|
socket._handle.getAsyncId() :
|
||||||
|
null;
|
||||||
|
nextTick(asyncId, () => request.emit('error', err));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (informRequest)
|
||||||
|
request.onSocket(socket);
|
||||||
|
else
|
||||||
|
socket.emit('free');
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
Agent,
|
Agent,
|
||||||
globalAgent: new Agent()
|
globalAgent: new Agent()
|
||||||
|
@ -20,41 +20,65 @@
|
|||||||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
require('../common');
|
const common = require('../common');
|
||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
const http = require('http');
|
const http = require('http');
|
||||||
|
|
||||||
const server = http.Server(function(req, res) {
|
|
||||||
res.writeHead(200);
|
|
||||||
res.end('hello world\n');
|
|
||||||
});
|
|
||||||
|
|
||||||
let responses = 0;
|
|
||||||
const N = 4;
|
const N = 4;
|
||||||
const M = 4;
|
const M = 4;
|
||||||
|
const server = http.Server(common.mustCall(function(req, res) {
|
||||||
|
res.writeHead(200);
|
||||||
|
res.end('hello world\n');
|
||||||
|
}, (N * M))); // N * M = good requests (the errors will not be counted)
|
||||||
|
|
||||||
server.listen(0, function() {
|
function makeRequests(outCount, inCount, shouldFail) {
|
||||||
const port = this.address().port;
|
let responseCount = outCount * inCount;
|
||||||
for (let i = 0; i < N; i++) {
|
let onRequest = common.mustNotCall(); // Temporary
|
||||||
setTimeout(function() {
|
const p = new Promise((resolve) => {
|
||||||
for (let j = 0; j < M; j++) {
|
onRequest = common.mustCall((res) => {
|
||||||
http.get({ port: port, path: '/' }, function(res) {
|
if (--responseCount === 0) {
|
||||||
console.log('%d %d', responses, res.statusCode);
|
server.close();
|
||||||
if (++responses === N * M) {
|
resolve();
|
||||||
console.error('Received all responses, closing server');
|
|
||||||
server.close();
|
|
||||||
}
|
|
||||||
res.resume();
|
|
||||||
}).on('error', function(e) {
|
|
||||||
console.log('Error!', e);
|
|
||||||
process.exit(1);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}, i);
|
if (!shouldFail)
|
||||||
|
res.resume();
|
||||||
|
}, outCount * inCount);
|
||||||
|
});
|
||||||
|
|
||||||
|
server.listen(0, () => {
|
||||||
|
const port = server.address().port;
|
||||||
|
for (let i = 0; i < outCount; i++) {
|
||||||
|
setTimeout(() => {
|
||||||
|
for (let j = 0; j < inCount; j++) {
|
||||||
|
const req = http.get({ port: port, path: '/' }, onRequest);
|
||||||
|
if (shouldFail)
|
||||||
|
req.on('error', common.mustCall(onRequest));
|
||||||
|
else
|
||||||
|
req.on('error', (e) => assert.fail(e));
|
||||||
|
}
|
||||||
|
}, i);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
const test1 = makeRequests(N, M);
|
||||||
|
|
||||||
|
const test2 = () => {
|
||||||
|
// Should not explode if can not create sockets.
|
||||||
|
// Ref: https://github.com/nodejs/node/issues/13045
|
||||||
|
// Ref: https://github.com/nodejs/node/issues/13831
|
||||||
|
http.Agent.prototype.createConnection = function createConnection(_, cb) {
|
||||||
|
process.nextTick(cb, new Error('nothing'));
|
||||||
|
};
|
||||||
|
return makeRequests(N, M, true);
|
||||||
|
};
|
||||||
|
|
||||||
|
test1
|
||||||
|
.then(test2)
|
||||||
|
.catch((e) => {
|
||||||
|
// This is currently the way to fail a test with a Promise.
|
||||||
|
console.error(e);
|
||||||
|
process.exit(1);
|
||||||
}
|
}
|
||||||
});
|
);
|
||||||
|
|
||||||
|
|
||||||
process.on('exit', function() {
|
|
||||||
assert.strictEqual(N * M, responses);
|
|
||||||
});
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user