Skip to content

Commit 4fe3148

Browse files
committed
implement using ipv4 for fetch
1 parent 2fbd472 commit 4fe3148

File tree

2 files changed

+74
-15
lines changed

2 files changed

+74
-15
lines changed

lib/fetch.js

+69-13
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import { h1NoCache, noCache, createUrl, AbortController, AbortError, FetchError
33
import { serialize as serializeCookie, parse as parseCookie } from 'cookie';
44
import log from '../logging.js';
55

6+
// TODO: review, use keepAliveNoCache, review keepAlive: true (default in many options).
7+
68
const fetchKeepAlive = noCache({
79
h1: {
810
keepAlive: true
@@ -20,13 +22,29 @@ const fetchH1KeepAlive = h1NoCache({
2022
rejectUnauthorized: false // By default skip auth check for all.
2123
}).fetch;
2224

25+
const fetchIPv4H1KeepAlive = h1NoCache({
26+
h1: {
27+
keepAlive: true,
28+
family: 4
29+
},
30+
rejectUnauthorized: false // By default skip auth check for all.
31+
}).fetch;
32+
2333
const fetchAuthorized = noCache({
2434
h2: {
2535
enablePush: false
2636
}
2737
}).fetch; // `rejectUnauthorized: true` - by `fetch` default.
38+
2839
const fetchH1Authorized = h1NoCache().fetch; // `rejectUnauthorized: true` - by `fetch` default.
2940

41+
const fetchIPv4H1Authorized = h1NoCache({
42+
h1: {
43+
keepAlive: true,
44+
family: 4
45+
}
46+
}).fetch; // `rejectUnauthorized: true` - by `fetch` default.
47+
3048
const { fetch } = noCache({
3149
h2: {
3250
enablePush: false
@@ -38,7 +56,15 @@ const fetchH1 = h1NoCache({
3856
rejectUnauthorized: false // By default skip auth check for all.
3957
}).fetch;
4058

41-
function doFetch(fetch_func, h1_fetch_func, options) {
59+
const fetchIPv4H1 = h1NoCache({
60+
h1: {
61+
keepAlive: false, // Default from `h1NoCache`.
62+
family: 4
63+
},
64+
rejectUnauthorized: false // By default skip auth check for all.
65+
}).fetch;
66+
67+
function doFetch(fetch_func, h1_fetch_func, h1_ipv4_fetch_func, options) {
4268

4369
const fetch_options = Object.assign({}, options);
4470

@@ -56,7 +82,23 @@ function doFetch(fetch_func, h1_fetch_func, options) {
5682
abortController.abort();
5783
}, options.timeout || CONFIG.RESPONSE_TIMEOUT);
5884

59-
const a_fetch_func = options.disable_http2 ? h1_fetch_func: fetch_func;
85+
var http2_used = false;
86+
var a_fetch_func;
87+
88+
if (options.use_ipv4) {
89+
90+
a_fetch_func = h1_ipv4_fetch_func;
91+
92+
} else if (options.disable_http2) {
93+
94+
a_fetch_func = h1_fetch_func;
95+
96+
} else {
97+
98+
a_fetch_func = fetch_func;
99+
http2_used = true;
100+
}
101+
60102
return new Promise((resolve, reject) => {
61103
a_fetch_func(uri, fetch_options)
62104
.then(response => {
@@ -67,11 +109,11 @@ function doFetch(fetch_func, h1_fetch_func, options) {
67109
});
68110
abortController.onResponse(stream);
69111

70-
if (response.status !== 200 && !options.disable_http2 && response.httpVersion === '2.0'
112+
if (response.status !== 200 && http2_used && response.httpVersion === '2.0'
71113
&& CONFIG.DISABLE_HTTP2_CHECKS?.some(check => typeof check === 'function'
72114
&& check(response.status, headers))) {
73115
log(' -- doFetch check disabled h2', uri);
74-
resolve(doFetch(fetch_func, h1_fetch_func, Object.assign({}, options, {disable_http2: true})));
116+
resolve(doFetch(fetch_func, h1_fetch_func, h1_ipv4_fetch_func, Object.assign({}, options, {disable_http2: true})));
75117
} else {
76118
stream.status = response.status;
77119
if (response.url && response.url !== uri) { // Set as final destination url if Fetch follows 301/302 re-directs
@@ -85,25 +127,25 @@ function doFetch(fetch_func, h1_fetch_func, options) {
85127
})
86128
.catch(error => {
87129
clearTimeout(timeoutTimerId);
88-
if (!options.disable_http2 && error.code && /^ERR_HTTP2/.test(error.code)) {
130+
if (http2_used && error.code && /^ERR_HTTP2/.test(error.code)) {
89131

90132
log(' -- doFetch http2 error', error.code, uri);
91-
resolve(doFetch(fetch_func, h1_fetch_func, Object.assign({}, options, {disable_http2: true})));
133+
resolve(doFetch(fetch_func, h1_fetch_func, h1_ipv4_fetch_func, Object.assign({}, options, {disable_http2: true})));
92134

93-
} else if (!options.disable_http2 && error.code && error instanceof FetchError && error.code === 'ABORT_ERR') {
135+
} else if (http2_used && error.code && error instanceof FetchError && error.code === 'ABORT_ERR') {
94136

95137
// Special case, when shared session request aborted by htmlparser logic.
96138
/**
97139
* https://polldaddy.com/poll/7451882/?s=twitter
98140
* https://app.everviz.com/show/O0Cy7Dyt
99141
*/
100142
log(' -- doFetch h2 aborted error', uri);
101-
resolve(doFetch(fetch_func, h1_fetch_func, Object.assign({}, options, {disable_http2: true})));
143+
resolve(doFetch(fetch_func, h1_fetch_func, h1_ipv4_fetch_func, Object.assign({}, options, {disable_http2: true})));
102144

103145
} else if (!options.stopRecursion && CONFIG.ERRORS_TO_RETRY?.some(code => error.code?.indexOf(code) > -1)) {
104146

105147
log(' -- doFetch ECONNRESET retry', error.code, uri);
106-
resolve(doFetch(fetch_func, h1_fetch_func, Object.assign({}, options, {stopRecursion: true, disable_http2: true})));
148+
resolve(doFetch(fetch_func, h1_fetch_func, h1_ipv4_fetch_func, Object.assign({}, options, {stopRecursion: true, disable_http2: true})));
107149

108150
} else {
109151
if (error instanceof AbortError) {
@@ -117,15 +159,15 @@ function doFetch(fetch_func, h1_fetch_func, options) {
117159
}
118160

119161
export function fetchStreamKeepAlive(options) {
120-
return doFetch(fetchKeepAlive, fetchH1KeepAlive, options);
162+
return doFetch(fetchKeepAlive, fetchH1KeepAlive, fetchIPv4H1KeepAlive, options);
121163
}
122164

123165
export function fetchStream(options) {
124-
return doFetch(fetch, fetchH1, options);
166+
return doFetch(fetch, fetchH1, fetchIPv4H1, options);
125167
};
126168

127169
export function fetchStreamAuthorized(options) {
128-
return doFetch(fetchAuthorized, fetchH1Authorized, options);
170+
return doFetch(fetchAuthorized, fetchH1Authorized, fetchIPv4H1Authorized, options);
129171
};
130172

131173
export function fetchData(options) {
@@ -144,7 +186,21 @@ export function fetchData(options) {
144186
abortController.abort();
145187
}, options.timeout || CONFIG.RESPONSE_TIMEOUT);
146188

147-
const a_fetch_func = options.disable_http2 ? fetchH1: fetch;
189+
var a_fetch_func;
190+
191+
if (options.use_ipv4) {
192+
193+
a_fetch_func = fetchIPv4H1;
194+
195+
} else if (options.disable_http2) {
196+
197+
a_fetch_func = fetchH1;
198+
199+
} else {
200+
201+
a_fetch_func = fetch;
202+
}
203+
148204
return new Promise((resolve, reject) => {
149205
a_fetch_func(uri, fetch_options)
150206
.then(response => {

lib/utils.js

+5-2
Original file line numberDiff line numberDiff line change
@@ -124,9 +124,12 @@ export function prepareRequestOptions(request_options, options) {
124124
if (options && proxy.maxredirects && (!options.redirectsHistory || options.redirectsHistory.length === 0)) {
125125
options.maxRedirects = proxy.maxredirects;
126126
}
127-
if (proxy && proxy.timeout > 0) {
127+
if (proxy.timeout > 0) {
128128
request_options.timeout = proxy.timeout > 100 ? proxy.timeout : proxy.timeout * 1000;
129129
}
130+
if (proxy.ipv4) {
131+
request_options.use_ipv4 = true;
132+
}
130133
}
131134
}
132135

@@ -1393,4 +1396,4 @@ export function isBlocked(url, options, cb) {
13931396
return re.test(domain);
13941397
}
13951398
});
1396-
}
1399+
}

0 commit comments

Comments
 (0)