diff --git a/lib/adapters/http.js b/lib/adapters/http.js index 32ef61b988..8e39f8ad7a 100755 --- a/lib/adapters/http.js +++ b/lib/adapters/http.js @@ -19,10 +19,14 @@ var isHttps = /https:?/; /*eslint consistent-return:0*/ module.exports = function httpAdapter(config) { return new Promise(function dispatchHttpRequest(resolvePromise, rejectPromise) { + var timer; + var resolve = function resolve(value) { + clearTimeout(timer); resolvePromise(value); }; var reject = function reject(value) { + clearTimeout(timer); rejectPromise(value); }; var data = config.data; @@ -250,16 +254,22 @@ module.exports = function httpAdapter(config) { }); // Handle request timeout + // + // There are many types of timeouts: + // DNS Timeout, Connect Timeout, Read/Write Timeout (idle timeout), and Call Timeout. + // Call Timeout measures from DNS Lookup to response body receiving completion. + // + // Currently, `timeout` option in Browser XHR indicates "Call Timeout". + // Create internal timer to match browser-side behavior. if (config.timeout) { - // Sometime, the response will be very slow, and does not respond, the connect event will be block by event loop system. - // And timer callback will be fired, and abort() will be invoked before connection, then get "socket hang up" and code ECONNRESET. - // At this time, if we have a large number of request, nodejs will hang up some socket on background. and the number will up and up. - // And then these socket which be hang up will devoring CPU little by little. - // ClientRequest.setTimeout will be fired on the specify milliseconds, and can make sure that abort() will be fired after connect. - req.setTimeout(config.timeout, function handleRequestTimeout() { + // Set internal timer to detect "Call Timeout" + // @see https://github.com/axios/axios/issues/2710#issuecomment-605693456 + timer = setTimeout(function handleReadTimeout() { + // Stop receiving remaining bytes and close socket req.abort(); - reject(createError('timeout of ' + config.timeout + 'ms exceeded', config, 'ECONNABORTED', req)); - }); + + reject(createError('timeout of ' + config.timeout + 'ms exceeded', config, 'ETIMEDOUT', req)); + }, config.timeout); } if (config.cancelToken) {