Skip to content

Commit 94e5b92

Browse files
authored
Remove timeout option (node-fetch#831)
1 parent 4824abe commit 94e5b92

File tree

10 files changed

+36
-134
lines changed

10 files changed

+36
-134
lines changed

@types/index.d.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,6 @@ interface RequestInit {
7979
port?: number;
8080
protocol?: string;
8181
size?: number;
82-
timeout?: number;
8382
highWaterMark?: number;
8483
}
8584

@@ -99,7 +98,6 @@ interface Body {
9998
readonly body: NodeJS.ReadableStream | null;
10099
readonly bodyUsed: boolean;
101100
readonly size: number;
102-
readonly timeout: number;
103101
buffer: () => Promise<Buffer>;
104102
arrayBuffer: () => Promise<ArrayBuffer>;
105103
blob: () => Promise<Blob>;
@@ -108,7 +106,7 @@ interface Body {
108106
}
109107
declare var Body: {
110108
prototype: Body;
111-
new (body?: BodyInit, opts?: {size?: number; timeout?: number}): Body;
109+
new (body?: BodyInit, opts?: {size?: number}): Body;
112110
};
113111

114112
type RequestRedirect = 'error' | 'follow' | 'manual';

@types/index.test-d.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ async function run() {
3434
const request = new Request('http://byjka.com/buka');
3535
expectType<string>(request.url);
3636
expectType<Headers>(request.headers);
37-
expectType<number>(request.timeout);
3837

3938
const headers = new Headers({byaka: 'buke'});
4039
expectType<(a: string, b: string) => void>(headers.append);

README.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ See Jason Miller's [isomorphic-unfetch](https://www.npmjs.com/package/isomorphic
8888
- Use native promise, but allow substituting it with [insert your favorite promise library].
8989
- Use native Node streams for body, on both request and response.
9090
- Decode content encoding (gzip/deflate) properly, and convert string output (such as `res.text()` and `res.json()`) to UTF-8 automatically.
91-
- Useful extensions such as timeout, redirect limit, response size limit, [explicit errors][error-handling.md] for troubleshooting.
91+
- Useful extensions such as redirect limit, response size limit, [explicit errors][error-handling.md] for troubleshooting.
9292

9393
## Difference from client-side fetch
9494

@@ -416,7 +416,6 @@ The default values are shown after each option key.
416416

417417
// The following properties are node-fetch extensions
418418
follow: 20, // maximum redirect count. 0 to not follow redirect
419-
timeout: 0, // req/res timeout in ms, it resets on redirect. 0 to disable (OS limit applies). Signal is recommended instead.
420419
compress: true, // support gzip/deflate content encoding. false to disable
421420
size: 0, // maximum response body size in bytes. 0 to disable
422421
agent: null, // http(s).Agent instance or function that returns an instance (see below)

docs/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ Changelog
88
**Work in progress!**
99

1010
- **Breaking:** minimum supported Node.js version is now 10.16.
11+
- **Breaking:** removed `timeout` option.
1112
- **Breaking:** revamp TypeScript declarations.
1213
- Enhance: improve coverage.
1314
- Enhance: drop Babel (while keeping ESM) (#805).

docs/v3-UPGRADE-GUIDE.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,27 @@ other comparatively minor modifications.
2323

2424
Since Node.js will deprecate version 8 at the end of 2019, we decided that node-fetch v3.x will not only drop support for Node.js 4 and 6 (which were supported in v2.x), but also for Node.js 8. We strongly encourage you to upgrade, if you still haven't done so. Check out Node.js' official [LTS plan] for more information on Node.js' support lifetime.
2525

26+
## The `timeout` option was removed.
27+
28+
Since this was never part of the fetch specification, it was removed. AbortSignal offers a more finegrained control of request timeouts, and is standardized in the Fetch spec. For convenience, you can use [timeout-signal](https://github.com/Richienb/timeout-signal) as a workaround:
29+
30+
```js
31+
const timeoutSignal = require('timeout-signal');
32+
const fetch = require('node-fetch');
33+
34+
const {AbortError} = fetch
35+
36+
fetch('https://www.google.com', { signal: timeoutSignal(5000) })
37+
.then(response => {
38+
// Handle response
39+
})
40+
.catch(error => {
41+
if (error instanceof AbortError) {
42+
// Handle timeout
43+
}
44+
})
45+
```
46+
2647
## `Response.statusText` no longer sets a default message derived from the HTTP status code
2748

2849
If the server didn't respond with status text, node-fetch would set a default message derived from the HTTP status code. This behavior was not spec-compliant and now the `statusText` will remain blank instead.

src/body.js

Lines changed: 10 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -25,30 +25,29 @@ const INTERNALS = Symbol('Body internals');
2525
*/
2626
export default class Body {
2727
constructor(body, {
28-
size = 0,
29-
timeout = 0
28+
size = 0
3029
} = {}) {
3130
if (body === null) {
32-
// Body is undefined or null
31+
// Body is undefined or null
3332
body = null;
3433
} else if (isURLSearchParams(body)) {
35-
// Body is a URLSearchParams
34+
// Body is a URLSearchParams
3635
body = Buffer.from(body.toString());
3736
} else if (isBlob(body)) {
38-
// Body is blob
37+
// Body is blob
3938
} else if (Buffer.isBuffer(body)) {
40-
// Body is Buffer
39+
// Body is Buffer
4140
} else if (types.isAnyArrayBuffer(body)) {
42-
// Body is ArrayBuffer
41+
// Body is ArrayBuffer
4342
body = Buffer.from(body);
4443
} else if (ArrayBuffer.isView(body)) {
45-
// Body is ArrayBufferView
44+
// Body is ArrayBufferView
4645
body = Buffer.from(body.buffer, body.byteOffset, body.byteLength);
4746
} else if (body instanceof Stream) {
48-
// Body is stream
47+
// Body is stream
4948
} else {
50-
// None of the above
51-
// coerce to string then buffer
49+
// None of the above
50+
// coerce to string then buffer
5251
body = Buffer.from(String(body));
5352
}
5453

@@ -58,7 +57,6 @@ export default class Body {
5857
error: null
5958
};
6059
this.size = size;
61-
this.timeout = timeout;
6260

6361
if (body instanceof Stream) {
6462
body.on('error', err => {
@@ -185,18 +183,6 @@ function consumeBody() {
185183
let abort = false;
186184

187185
return new Body.Promise((resolve, reject) => {
188-
let resTimeout;
189-
190-
// Allow timeout on slow response body
191-
if (this.timeout) {
192-
resTimeout = setTimeout(() => {
193-
abort = true;
194-
const err = new FetchError(`Response timeout while trying to fetch ${this.url} (over ${this.timeout}ms)`, 'body-timeout');
195-
reject(err);
196-
body.destroy(err);
197-
}, this.timeout);
198-
}
199-
200186
body.on('data', chunk => {
201187
if (abort || chunk === null) {
202188
return;
@@ -213,7 +199,6 @@ function consumeBody() {
213199
});
214200

215201
finished(body, {writable: false}, err => {
216-
clearTimeout(resTimeout);
217202
if (err) {
218203
if (isAbortError(err)) {
219204
// If the request was aborted, reject with this Error

src/index.js

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -101,13 +101,6 @@ export default function fetch(url, options_) {
101101
}
102102
}
103103

104-
if (request.timeout) {
105-
request_.setTimeout(request.timeout, () => {
106-
finalize();
107-
reject(new FetchError(`network timeout at: ${request.url}`, 'request-timeout'));
108-
});
109-
}
110-
111104
request_.on('error', err => {
112105
reject(new FetchError(`request to ${request.url} failed, reason: ${err.message}`, 'system', err));
113106
finalize();
@@ -168,8 +161,7 @@ export default function fetch(url, options_) {
168161
compress: request.compress,
169162
method: request.method,
170163
body: request.body,
171-
signal: request.signal,
172-
timeout: request.timeout
164+
signal: request.signal
173165
};
174166

175167
// HTTP-redirect fetch step 9
@@ -214,7 +206,6 @@ export default function fetch(url, options_) {
214206
statusText: res.statusMessage,
215207
headers,
216208
size: request.size,
217-
timeout: request.timeout,
218209
counter: request.counter,
219210
highWaterMark: request.highWaterMark
220211
};

src/request.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,6 @@ export default class Request extends Body {
9393
null);
9494

9595
super(inputBody, {
96-
timeout: init.timeout || input.timeout || 0,
9796
size: init.size || input.size || 0
9897
});
9998

src/response.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,7 @@ export default class Response extends Body {
8585
headers: this.headers,
8686
ok: this.ok,
8787
redirected: this.redirected,
88-
size: this.size,
89-
timeout: this.timeout
88+
size: this.size
9089
});
9190
}
9291

test/main.js

Lines changed: 0 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// Test tools
22
import zlib from 'zlib';
33
import crypto from 'crypto';
4-
import {spawn} from 'child_process';
54
import http from 'http';
65
import fs from 'fs';
76
import stream from 'stream';
@@ -844,78 +843,6 @@ describe('node-fetch', () => {
844843
});
845844
});
846845

847-
it('should allow custom timeout', () => {
848-
const url = `${base}timeout`;
849-
const options = {
850-
timeout: 20
851-
};
852-
return expect(fetch(url, options)).to.eventually.be.rejected
853-
.and.be.an.instanceOf(FetchError)
854-
.and.have.property('type', 'request-timeout');
855-
});
856-
857-
it('should allow custom timeout on response body', () => {
858-
const url = `${base}slow`;
859-
const options = {
860-
timeout: 20
861-
};
862-
return fetch(url, options).then(res => {
863-
expect(res.ok).to.be.true;
864-
return expect(res.text()).to.eventually.be.rejected
865-
.and.be.an.instanceOf(FetchError)
866-
.and.have.property('type', 'body-timeout');
867-
});
868-
});
869-
870-
it('should not allow socket timeout before body is read', () => {
871-
const url = `${base}slow`;
872-
const options = {
873-
timeout: 100
874-
};
875-
// Await the response, then delay, allowing enough time for the timeout
876-
// to be created just before the socket timeout
877-
return fetch(url, options).then(delay(75)).then(res => {
878-
expect(res.ok).to.be.true;
879-
return expect(res.text()).to.eventually.be.rejected
880-
.and.be.an.instanceOf(FetchError)
881-
.and.have.property('type', 'body-timeout');
882-
});
883-
});
884-
885-
it('should allow custom timeout on redirected requests', () => {
886-
const url = `${base}redirect/slow-chain`;
887-
const options = {
888-
timeout: 20
889-
};
890-
return expect(fetch(url, options)).to.eventually.be.rejected
891-
.and.be.an.instanceOf(FetchError)
892-
.and.have.property('type', 'request-timeout');
893-
});
894-
895-
it('should clear internal timeout on fetch response', function (done) {
896-
this.timeout(2000);
897-
spawn('node', ['-e', `require(’./’)(’${base}hello’, { timeout: 10000 })`])
898-
.on('exit', () => {
899-
done();
900-
});
901-
});
902-
903-
it('should clear internal timeout on fetch redirect', function (done) {
904-
this.timeout(2000);
905-
spawn('node', ['-e', `require(’./’)(’${base}redirect/301’, { timeout: 10000 })`])
906-
.on('exit', () => {
907-
done();
908-
});
909-
});
910-
911-
it('should clear internal timeout on fetch error', function (done) {
912-
this.timeout(2000);
913-
spawn('node', ['-e', `require(’./’)(’${base}error/reset’, { timeout: 10000 })`])
914-
.on('exit', () => {
915-
done();
916-
});
917-
});
918-
919846
it('should support request cancellation with signal', function () {
920847
this.timeout(500);
921848
const controller = new AbortController();
@@ -967,23 +894,6 @@ describe('node-fetch', () => {
967894
});
968895
});
969896

970-
it('should clear internal timeout when request is cancelled with an AbortSignal', function (done) {
971-
this.timeout(2000);
972-
const script = `
973-
var AbortController = require(’abortcontroller-polyfill/dist/cjs-ponyfill’).AbortController;
974-
var controller = new AbortController();
975-
require(’./’)(
976-
${base}timeout’,
977-
{ signal: controller.signal, timeout: 10000 }
978-
);
979-
setTimeout(function () { controller.abort(); }, 20);
980-
`;
981-
spawn('node', ['-e', script])
982-
.on('exit', () => {
983-
done();
984-
});
985-
});
986-
987897
it('should remove internal AbortSignal event listener after request is aborted', () => {
988898
const controller = new AbortController();
989899
const {signal} = controller;

0 commit comments

Comments
 (0)