Skip to content

Commit 63d14da

Browse files
feat: add urllib4 fetch (#5424)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
1 parent 6130bef commit 63d14da

9 files changed

Lines changed: 116 additions & 7 deletions

File tree

.github/workflows/gh-pages.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ jobs:
2525
run: npm i -g npminstall && npminstall
2626

2727
- name: Build Documents
28-
run: npm run site:build && cp vercel.json ./site/dist/vercel.json
28+
run: npm run site:build
2929

3030
- name: Deploy Documents
3131
uses: peaceiris/actions-gh-pages@v3

.github/workflows/nodejs-3.x.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ on:
55
branches: [ master ]
66
pull_request:
77
branches: [ master ]
8+
merge_group:
9+
branches: [ master ]
810

911
jobs:
1012
Job:

index.d.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ import {
2323
RequestOptions,
2424
HttpClientResponse as HttpClientResponseNext,
2525
} from 'urllib-next';
26+
import {
27+
FetchFactory,
28+
fetch,
29+
} from 'urllib4';
2630
import {
2731
EggCoreBase,
2832
FileLoaderOption,
@@ -589,6 +593,12 @@ declare module 'egg' {
589593
*/
590594
httpclient: EggHttpClient;
591595

596+
/**
597+
* node fetch
598+
*/
599+
FetchFactory: FetchFactory;
600+
fetch: typeof fetch,
601+
592602
/**
593603
* Logger for Application, wrapping app.coreLogger with context infomation
594604
*

lib/core/fetch_factory.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
const debug = require('util').debuglog('egg:lib:core:fetch_factory');
2+
3+
const mainNodejsVersion = parseInt(process.versions.node.split('.')[0]);
4+
let FetchFactory;
5+
let safeFetch;
6+
let ssrfFetchFactory;
7+
8+
if (mainNodejsVersion >= 20) {
9+
// urllib@4 only works on Node.js >= 20
10+
try {
11+
const urllib4 = require('urllib4');
12+
FetchFactory = urllib4.FetchFactory;
13+
debug('urllib4 enable');
14+
15+
safeFetch = function safeFetch(url, init) {
16+
if (!ssrfFetchFactory) {
17+
const ssrfConfig = this.config.security.ssrf;
18+
const clientOptions = {};
19+
if (ssrfConfig?.checkAddress) {
20+
clientOptions.checkAddress = ssrfConfig.checkAddress;
21+
} else {
22+
this.logger.warn('[egg-security] please configure `config.security.ssrf` first');
23+
}
24+
ssrfFetchFactory = new FetchFactory();
25+
ssrfFetchFactory.setClientOptions(clientOptions);
26+
}
27+
return ssrfFetchFactory.fetch(url, init);
28+
};
29+
} catch (err) {
30+
debug('require urllib4 error: %s', err);
31+
}
32+
}
33+
34+
module.exports = {
35+
FetchFactory,
36+
safeFetch,
37+
};

lib/core/httpclient_next.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ const SSRF_HTTPCLIENT = Symbol('SSRF_HTTPCLIENT');
55

66
const mainNodejsVersion = parseInt(process.versions.node.split('.')[0]);
77
let HttpClient;
8-
if (mainNodejsVersion >= 18) {
9-
// urllib@4 only works on Node.js >= 18
8+
if (mainNodejsVersion >= 20) {
9+
// urllib@4 only works on Node.js >= 20
1010
try {
1111
HttpClient = require('urllib4').HttpClient;
1212
debug('urllib4 enable');

lib/egg.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const Messenger = require('./core/messenger');
1414
const DNSCacheHttpClient = require('./core/dnscache_httpclient');
1515
const HttpClient = require('./core/httpclient');
1616
const HttpClientNext = require('./core/httpclient_next');
17+
const { FetchFactory, safeFetch } = require('./core/fetch_factory');
1718
const createLoggers = require('./core/logger');
1819
const Singleton = require('./core/singleton');
1920
const utils = require('./core/utils');
@@ -51,6 +52,12 @@ class EggApplication extends EggCore {
5152
this.ContextHttpClient = ContextHttpClient;
5253
this.HttpClient = HttpClient;
5354
this.HttpClientNext = HttpClientNext;
55+
this.FetchFactory = FetchFactory;
56+
if (FetchFactory) {
57+
this.FetchFactory.setClientOptions();
58+
this.fetch = FetchFactory.fetch;
59+
this.safeFetch = safeFetch.bind(this);
60+
}
5461

5562
this.loader.loadConfig();
5663

site/config/config.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ export default defineConfig({
3939
title: 'Plugins',
4040
path: 'https://github.com/search?q=topic%3Aegg-plugin&type=Repositories',
4141
},
42+
{
43+
title: 'v3.x',
44+
path: 'https://v3.eggjs.org',
45+
},
4246
{
4347
title: 'v4.x',
4448
path: 'https://eggjs.org',
@@ -50,13 +54,17 @@ export default defineConfig({
5054
title: 'GitHub',
5155
path: 'https://github.com/eggjs/egg',
5256
},
57+
{
58+
title: '发布日志',
59+
path: 'https://github.com/eggjs/egg/releases',
60+
},
5361
{
5462
title: '插件列表',
5563
path: 'https://github.com/search?q=topic%3Aegg-plugin&type=Repositories',
5664
},
5765
{
58-
title: '发布日志',
59-
path: 'https://github.com/eggjs/egg/releases',
66+
title: 'v3.x',
67+
path: 'https://v3.eggjs.org',
6068
},
6169
{
6270
title: 'v4.x',
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
const assert = require('node:assert');
2+
const mm = require('egg-mock');
3+
const { FetchFactory, safeFetch } = require('../../../lib/core/fetch_factory');
4+
const utils = require('../../utils');
5+
6+
describe('test/lib/core/fetch_factory.test.js', () => {
7+
if (!FetchFactory) return;
8+
let url;
9+
10+
before(async () => {
11+
url = await utils.startLocalServer();
12+
});
13+
14+
afterEach(mm.restore);
15+
16+
it('should fetch ok', async () => {
17+
const { status } = await FetchFactory.fetch(url);
18+
assert(status === 200);
19+
});
20+
21+
it('should support safeFetch', async () => {
22+
let ip;
23+
let family;
24+
let host;
25+
const fetch = safeFetch.bind({
26+
config: {
27+
security: {
28+
ssrf: {
29+
checkAddress(aIp, aFamily, aHost) {
30+
ip = aIp;
31+
family = aFamily;
32+
host = aHost;
33+
return true;
34+
},
35+
},
36+
},
37+
},
38+
logger: console,
39+
});
40+
await fetch(url);
41+
assert(ip);
42+
assert(family);
43+
assert(host);
44+
});
45+
});

test/lib/core/httpclient.test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ describe('test/lib/core/httpclient.test.js', () => {
340340
stream.end('hello h2!');
341341
// console.log(headers);
342342
const mainNodejsVersion = parseInt(process.versions.node.split('.')[0]);
343-
if (mainNodejsVersion >= 18) {
343+
if (mainNodejsVersion >= 20) {
344344
assert.match(headers['user-agent'], /node\-urllib\/4\.\d+\.\d+/);
345345
} else {
346346
assert.match(headers['user-agent'], /node\-urllib\/3\.\d+\.\d+/);
@@ -380,7 +380,7 @@ describe('test/lib/core/httpclient.test.js', () => {
380380

381381
it('should request http2 success', async () => {
382382
for (let i = 0; i < 10; i++) {
383-
const result = await app.httpclient.curl('https://registry.npmmirror.com', {
383+
const result = await app.httpclient.curl('https://r.cnpmjs.org', {
384384
dataType: 'json',
385385
timeout: 5000,
386386
});

0 commit comments

Comments
 (0)