Skip to content
This repository was archived by the owner on Aug 11, 2021. It is now read-only.

Commit 5a91aab

Browse files
committed
Ensure late errors on requests are passed through properly
1 parent 2244b9e commit 5a91aab

File tree

3 files changed

+105
-2
lines changed

3 files changed

+105
-2
lines changed

lib/fetch.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,19 @@ function fetch (uri, params, cb) {
1818
makeRequest.call(client, uri, params, function (er, req) {
1919
if (er) return cb(er)
2020

21-
req.on('error', function (er) {
21+
req.once('error', retryOnError)
22+
23+
function retryOnError (er) {
2224
if (operation.retry(er)) {
2325
client.log.info('retry', 'will retry, error on last attempt: ' + er)
2426
} else {
2527
cb(er)
2628
}
27-
})
29+
}
2830

2931
req.on('response', function (res) {
3032
client.log.http('fetch', '' + res.statusCode, uri)
33+
req.removeListener('error', retryOnError)
3134

3235
var er
3336
var statusCode = res && res.statusCode
@@ -37,6 +40,10 @@ function fetch (uri, params, cb) {
3740
res.resume()
3841
if (process.version === 'v0.10.0') unstick(res)
3942

43+
req.once('error', function (er) {
44+
res.emit('error', er)
45+
})
46+
4047
return cb(null, res)
4148
// Only retry on 408, 5xx or no `response`.
4249
} else if (statusCode === 408) {

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
"negotiator": "^0.6.1",
2626
"nock": "^8.0.0",
2727
"readable-stream": "^2.1.5",
28+
"require-inject": "^1.4.0",
2829
"rimraf": "^2.5.4",
2930
"standard": "~8.5.0",
3031
"tap": "^7.0.0"

test/econnreset.js

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
'use strict'
2+
var requireInject = require('require-inject')
3+
var test = require('tap').test
4+
var EventEmitter = require('events').EventEmitter
5+
var PassThrough = require('readable-stream').PassThrough
6+
7+
var content = [
8+
'first chunk',
9+
'second chunk'
10+
]
11+
var requests = 0
12+
var fetch = requireInject('../lib/fetch.js', {
13+
request: function (opts) {
14+
var req = new EventEmitter()
15+
++requests
16+
setTimeout(function () {
17+
var res = new PassThrough()
18+
res.statusCode = 200
19+
20+
setTimeout(function () {
21+
res.write(content[0])
22+
}, 50)
23+
24+
if (requests === 1) {
25+
setTimeout(function () {
26+
var err = new Error('read ECONNRESET')
27+
err.code = 'ECONNRESET'
28+
req.emit('error', err)
29+
}, 100)
30+
} else {
31+
// we allow success on retries, though in practice we won't be
32+
// retrying.
33+
setTimeout(function () {
34+
res.end(content[1])
35+
}, 100)
36+
}
37+
req.emit('response', res)
38+
}, 50)
39+
return req
40+
}
41+
})
42+
43+
function clientMock (t) {
44+
return {
45+
log: {
46+
info: function (section, message) {
47+
t.comment('[info] ' + section + ': ' + [].slice.call(arguments, 1).join(' '))
48+
},
49+
http: function (section, message) {
50+
t.comment('[http] ' + section + ': ' + [].slice.call(arguments, 1).join(' '))
51+
}
52+
},
53+
authify: function (alwaysAuth, parsed, headers, auth) {
54+
return
55+
},
56+
initialize: function (parsed, method, accept, headers) {
57+
return {}
58+
},
59+
attempt: require('../lib/attempt.js'),
60+
config: {
61+
retry: {
62+
retries: 2,
63+
factor: 10,
64+
minTimeout: 10000,
65+
maxTimeout: 60000
66+
}
67+
}
68+
}
69+
}
70+
71+
/*
72+
This tests that errors that occur in the REQUEST object AFTER a `response`
73+
event has been emitted will be passed through to the `response` stream.
74+
This means that we won't try to retry these sorts of errors ourselves.
75+
*/
76+
77+
test('econnreset', function (t) {
78+
var client = clientMock(t)
79+
fetch.call(client, 'http://example.com/', {}, function (err, res) {
80+
t.ifError(err, 'initial fetch ok')
81+
var data = ''
82+
res.on('data', function (chunk) {
83+
data += chunk
84+
})
85+
res.on('error', function (err) {
86+
t.comment('ERROR:', err.stack)
87+
t.pass("errored and that's ok")
88+
t.done()
89+
})
90+
res.on('end', function () {
91+
t.is(data, content.join(''), 'succeeded and got the right data')
92+
t.done()
93+
})
94+
})
95+
})

0 commit comments

Comments
 (0)