Skip to content

Commit e89cc76

Browse files
make request timeout errors eligible for retry (stripe#1104)
1 parent 337ff6c commit e89cc76

3 files changed

Lines changed: 83 additions & 17 deletions

File tree

lib/StripeResource.js

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -98,17 +98,7 @@ StripeResource.prototype = {
9898
const timeoutErr = new TypeError('ETIMEDOUT');
9999
timeoutErr.code = 'ETIMEDOUT';
100100

101-
req._isAborted = true;
102-
req.abort();
103-
104-
callback.call(
105-
this,
106-
new StripeConnectionError({
107-
message: `Request aborted due to timeout being reached (${timeout}ms)`,
108-
detail: timeoutErr,
109-
}),
110-
null
111-
);
101+
req.destroy(timeoutErr);
112102
};
113103
},
114104

@@ -224,11 +214,7 @@ StripeResource.prototype = {
224214
},
225215

226216
_errorHandler(req, requestRetries, callback) {
227-
return (error) => {
228-
if (req._isAborted) {
229-
// already handled
230-
return;
231-
}
217+
return (message, detail) => {
232218
callback.call(
233219
this,
234220
new StripeConnectionError({
@@ -486,7 +472,23 @@ StripeResource.prototype = {
486472
null
487473
);
488474
} else {
489-
return this._errorHandler(req, requestRetries, callback)(error);
475+
if (error.code === 'ETIMEDOUT') {
476+
return callback.call(
477+
this,
478+
new StripeConnectionError({
479+
message: `Request aborted due to timeout being reached (${timeout}ms)`,
480+
detail: error,
481+
})
482+
);
483+
}
484+
return callback.call(
485+
this,
486+
new StripeConnectionError({
487+
message: this._generateConnectionErrorMessage(requestRetries),
488+
detail: error,
489+
}),
490+
null
491+
);
490492
}
491493
});
492494

test/StripeResource.spec.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,46 @@ describe('StripeResource', () => {
185185
});
186186
});
187187

188+
it('throws an error on connection timeout', (done) => {
189+
return utils.getTestServerStripe(
190+
{timeout: 10},
191+
(req, res) => {
192+
// Do nothing. This will trigger a timeout.
193+
},
194+
(err, stripe) => {
195+
if (err) {
196+
return done(err);
197+
}
198+
stripe.charges.create(options.data, (err, result) => {
199+
expect(err.detail.message).to.deep.equal('ETIMEDOUT');
200+
done();
201+
});
202+
}
203+
);
204+
});
205+
206+
it('retries connection timeout errors', (done) => {
207+
let nRequestsReceived = 0;
208+
return utils.getTestServerStripe(
209+
{timeout: 10, maxNetworkRetries: 2},
210+
(req, res) => {
211+
nRequestsReceived += 1;
212+
// Do nothing. This will trigger a timeout.
213+
return {shouldStayOpen: nRequestsReceived < 3};
214+
},
215+
(err, stripe) => {
216+
if (err) {
217+
return done(err);
218+
}
219+
stripe.charges.create(options.data, (err, result) => {
220+
expect(err.detail.message).to.deep.equal('ETIMEDOUT');
221+
expect(nRequestsReceived).to.equal(3);
222+
done();
223+
});
224+
}
225+
);
226+
});
227+
188228
it('should retry the request if max retries are set', (done) => {
189229
nock(`https://${options.host}`)
190230
.post(options.path, options.params)

testUtils/index.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,33 @@ require('mocha');
66
// Ensure we are using the 'as promised' libs before any tests are run:
77
require('chai').use(require('chai-as-promised'));
88

9+
const http = require('http');
10+
911
const ResourceNamespace = require('../lib/ResourceNamespace').ResourceNamespace;
1012

1113
const utils = (module.exports = {
14+
getTestServerStripe: (clientOptions, handler, callback) => {
15+
const server = http.createServer((req, res) => {
16+
const {shouldStayOpen} = handler(req, res) || {};
17+
if (!shouldStayOpen) {
18+
res.on('close', () => server.close());
19+
}
20+
});
21+
server.listen(0, () => {
22+
const {port} = server.address();
23+
const stripe = require('../lib/stripe')(
24+
module.exports.getUserStripeKey(),
25+
{
26+
host: 'localhost',
27+
port,
28+
protocol: 'http',
29+
...clientOptions,
30+
}
31+
);
32+
return callback(null, stripe);
33+
});
34+
},
35+
1236
getUserStripeKey: () => {
1337
const key =
1438
process.env.STRIPE_TEST_API_KEY || 'tGN0bIwXnHdwOa85VABjPdSn8nWY7G7I';

0 commit comments

Comments
 (0)