diff --git a/.changes/next-release/bugfix-retry-c648c8ae.json b/.changes/next-release/bugfix-retry-c648c8ae.json new file mode 100644 index 0000000000..69af0e3df8 --- /dev/null +++ b/.changes/next-release/bugfix-retry-c648c8ae.json @@ -0,0 +1,5 @@ +{ + "type": "bugfix", + "category": "retry", + "description": "Retry http request on ECONNRESET error" +} \ No newline at end of file diff --git a/lib/http/node.js b/lib/http/node.js index a4c4a34fd7..009824680d 100644 --- a/lib/http/node.js +++ b/lib/http/node.js @@ -87,13 +87,17 @@ AWS.NodeHttpClient = AWS.util.inherit({ stream.abort(); }); - stream.on('error', function() { + stream.on('error', function(err) { if (connectTimeoutId) { clearTimeout(connectTimeoutId); connectTimeoutId = null; } if (stream.didCallback) return; stream.didCallback = true; - errCallback.apply(stream, arguments); + if ('ECONNRESET' === err.code || 'EPIPE' === err.code || 'ETIMEDOUT' === err.code) { + errCallback(AWS.util.error(err, {code: 'TimeoutError'})); + } else { + errCallback(err); + } }); var expect = httpRequest.headers.Expect || httpRequest.headers.expect; diff --git a/test/node_http_client.spec.js b/test/node_http_client.spec.js index c858e672b6..05de8f42de 100644 --- a/test/node_http_client.spec.js +++ b/test/node_http_client.spec.js @@ -155,6 +155,45 @@ return expect(numCalls).to.equal(1); }); }); + + describe('ECONNRESET', function() { + var mockClientRequest = null; + var oldRequest = httpModule.request; + + beforeEach(function() { + mockClientRequest = new EventEmitter(); + mockClientRequest.setTimeout = function() { + return {}; + }; + mockClientRequest.abort = function() { + return {}; + }; + mockClientRequest.end = function() { + return {}; + }; + return requestSpy = helpers.spyOn(httpModule, 'request').andReturn(mockClientRequest); + }); + + afterEach(function() { + httpModule.request = oldRequest; + }); + + it('retries ECONNRESET', function() { + var req = new AWS.HttpRequest('http://10.255.255.255'); + var numCalls = 0; + + http.handleRequest(req, {}, null, function(err) { + numCalls += 1; + + expect(err.code).to.equal('TimeoutError'); + expect(err.message).to.equal('test ECONNRESET'); + return expect(numCalls).to.equal(1); + }); + + mockClientRequest.emit('error', { code: 'ECONNRESET', message: 'test ECONNRESET' }); + }); + }); + describe('timeout', function() { it('is obeyed even after response headers are recieved', function(done) { // a mock server with 'ShakyStream' allows us to simulate a period of socket inactivity