egg 3.33.0 → 3.34.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.d.ts +5 -1
- package/lib/core/fetch_factory.js +21 -5
- package/lib/core/utils.js +116 -0
- package/lib/egg.js +3 -32
- package/package.json +1 -1
package/index.d.ts
CHANGED
|
@@ -295,6 +295,10 @@ declare module 'egg' {
|
|
|
295
295
|
maxFreeSockets?: number;
|
|
296
296
|
}
|
|
297
297
|
|
|
298
|
+
type Dispatcher = FetchFactory['getDispatcher'] extends () => infer R
|
|
299
|
+
? R
|
|
300
|
+
: never;
|
|
301
|
+
|
|
298
302
|
/** HttpClient config */
|
|
299
303
|
export interface HttpClientConfig extends HttpClientBaseConfig {
|
|
300
304
|
/** http.Agent */
|
|
@@ -319,8 +323,8 @@ declare module 'egg' {
|
|
|
319
323
|
allowH2?: boolean;
|
|
320
324
|
/** Custom lookup function for DNS resolution */
|
|
321
325
|
lookup?: LookupFunction;
|
|
326
|
+
interceptors?: Parameters<Dispatcher['compose']>;
|
|
322
327
|
}
|
|
323
|
-
|
|
324
328
|
export interface EggAppConfig {
|
|
325
329
|
workerStartTimeout: number;
|
|
326
330
|
baseDir: string;
|
|
@@ -3,7 +3,8 @@ const debug = require('util').debuglog('egg:lib:core:fetch_factory');
|
|
|
3
3
|
const mainNodejsVersion = parseInt(process.versions.node.split('.')[0]);
|
|
4
4
|
let FetchFactory;
|
|
5
5
|
let fetch;
|
|
6
|
-
|
|
6
|
+
// Track initialization per app instance by storing a WeakMap
|
|
7
|
+
const fetchInitializedMap = new WeakMap();
|
|
7
8
|
let safeFetch;
|
|
8
9
|
let ssrfFetchFactory;
|
|
9
10
|
|
|
@@ -14,15 +15,24 @@ if (mainNodejsVersion >= 20) {
|
|
|
14
15
|
FetchFactory = urllib4.FetchFactory;
|
|
15
16
|
debug('urllib4 enable');
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
if (!fetchInitialized) {
|
|
18
|
+
fetch = function(url, init) {
|
|
19
|
+
if (!fetchInitializedMap.get(this)) {
|
|
20
20
|
const clientOptions = {};
|
|
21
21
|
if (this.config.httpclient?.lookup) {
|
|
22
22
|
clientOptions.lookup = this.config.httpclient.lookup;
|
|
23
23
|
}
|
|
24
24
|
FetchFactory.setClientOptions(clientOptions);
|
|
25
|
-
|
|
25
|
+
|
|
26
|
+
// Support custom interceptors via dispatcher.compose
|
|
27
|
+
// Must be set after setClientOptions because setClientOptions resets dispatcher
|
|
28
|
+
// interceptors is an array of interceptor functions that follow undici's dispatcher API(undici have not supported clientOptions.interceptors natively yet)
|
|
29
|
+
if (this.config.httpclient?.interceptors) {
|
|
30
|
+
const interceptors = this.config.httpclient.interceptors;
|
|
31
|
+
const originalDispatcher = FetchFactory.getDispatcher();
|
|
32
|
+
FetchFactory.setDispatcher(originalDispatcher.compose(interceptors));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
fetchInitializedMap.set(this, true);
|
|
26
36
|
}
|
|
27
37
|
return FetchFactory.fetch(url, init);
|
|
28
38
|
};
|
|
@@ -41,6 +51,12 @@ if (mainNodejsVersion >= 20) {
|
|
|
41
51
|
}
|
|
42
52
|
ssrfFetchFactory = new FetchFactory();
|
|
43
53
|
ssrfFetchFactory.setClientOptions(clientOptions);
|
|
54
|
+
|
|
55
|
+
if (this.config.httpclient?.interceptors) {
|
|
56
|
+
const interceptors = this.config.httpclient.interceptors;
|
|
57
|
+
const originalDispatcher = ssrfFetchFactory.getDispatcher();
|
|
58
|
+
ssrfFetchFactory.setDispatcher(originalDispatcher.compose(interceptors));
|
|
59
|
+
}
|
|
44
60
|
}
|
|
45
61
|
return ssrfFetchFactory.fetch(url, init);
|
|
46
62
|
};
|
package/lib/core/utils.js
CHANGED
|
@@ -7,6 +7,7 @@ const URL = require('url').URL;
|
|
|
7
7
|
module.exports = {
|
|
8
8
|
convertObject,
|
|
9
9
|
safeParseURL,
|
|
10
|
+
createTransparentProxy,
|
|
10
11
|
};
|
|
11
12
|
|
|
12
13
|
function convertObject(obj, ignore, ignoreKeyPaths) {
|
|
@@ -90,3 +91,118 @@ function safeParseURL(url) {
|
|
|
90
91
|
return null;
|
|
91
92
|
}
|
|
92
93
|
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Create a Proxy that behaves like the real object, but remains transparent to
|
|
97
|
+
* monkeypatch libraries (e.g. defineProperty-based overrides).
|
|
98
|
+
*
|
|
99
|
+
* - Lazily creates the real object on first access.
|
|
100
|
+
* - Allows overriding properties on the proxy target (overlay) to take effect.
|
|
101
|
+
* - Delegates everything else to the real object.
|
|
102
|
+
*
|
|
103
|
+
* @param {Object} options
|
|
104
|
+
* @param {Function} options.createReal Create the real object (lazy)
|
|
105
|
+
* @param {boolean} [options.bindFunctions=true] Bind real methods to the real object
|
|
106
|
+
* @return {Proxy}
|
|
107
|
+
*/
|
|
108
|
+
function createTransparentProxy({ createReal, bindFunctions = true }) {
|
|
109
|
+
if (typeof createReal !== 'function') {
|
|
110
|
+
throw new TypeError('createReal must be a function');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
let real = null;
|
|
114
|
+
let error = null;
|
|
115
|
+
let initialized = false;
|
|
116
|
+
|
|
117
|
+
const init = () => {
|
|
118
|
+
if (initialized) {
|
|
119
|
+
if (error) throw error;
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
initialized = true;
|
|
123
|
+
try {
|
|
124
|
+
real = createReal();
|
|
125
|
+
} catch (err) {
|
|
126
|
+
error = err;
|
|
127
|
+
throw err;
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
return new Proxy({}, {
|
|
132
|
+
get(target, prop, receiver) {
|
|
133
|
+
init();
|
|
134
|
+
// Check if property is defined on proxy target (monkeypatch overlay)
|
|
135
|
+
if (Object.getOwnPropertyDescriptor(target, prop)) {
|
|
136
|
+
return Reflect.get(target, prop, receiver);
|
|
137
|
+
}
|
|
138
|
+
const value = real[prop];
|
|
139
|
+
if (bindFunctions && typeof value === 'function') {
|
|
140
|
+
return value.bind(real);
|
|
141
|
+
}
|
|
142
|
+
return value;
|
|
143
|
+
},
|
|
144
|
+
|
|
145
|
+
set(target, prop, value, receiver) {
|
|
146
|
+
init();
|
|
147
|
+
if (Object.getOwnPropertyDescriptor(target, prop)) {
|
|
148
|
+
return Reflect.set(target, prop, value, receiver);
|
|
149
|
+
}
|
|
150
|
+
return Reflect.set(real, prop, value);
|
|
151
|
+
},
|
|
152
|
+
|
|
153
|
+
has(target, prop) {
|
|
154
|
+
init();
|
|
155
|
+
return prop in target || prop in real;
|
|
156
|
+
},
|
|
157
|
+
|
|
158
|
+
ownKeys(target) {
|
|
159
|
+
init();
|
|
160
|
+
const keys = new Set([ ...Reflect.ownKeys(real), ...Reflect.ownKeys(target) ]);
|
|
161
|
+
return Array.from(keys);
|
|
162
|
+
},
|
|
163
|
+
|
|
164
|
+
getOwnPropertyDescriptor(target, prop) {
|
|
165
|
+
init();
|
|
166
|
+
return Object.getOwnPropertyDescriptor(target, prop)
|
|
167
|
+
|| Object.getOwnPropertyDescriptor(real, prop);
|
|
168
|
+
},
|
|
169
|
+
|
|
170
|
+
deleteProperty(target, prop) {
|
|
171
|
+
init();
|
|
172
|
+
if (Object.getOwnPropertyDescriptor(target, prop)) {
|
|
173
|
+
return delete target[prop];
|
|
174
|
+
}
|
|
175
|
+
return delete real[prop];
|
|
176
|
+
},
|
|
177
|
+
|
|
178
|
+
getPrototypeOf() {
|
|
179
|
+
init();
|
|
180
|
+
return Object.getPrototypeOf(real);
|
|
181
|
+
},
|
|
182
|
+
|
|
183
|
+
setPrototypeOf(_target, proto) {
|
|
184
|
+
init();
|
|
185
|
+
return Reflect.setPrototypeOf(real, proto);
|
|
186
|
+
},
|
|
187
|
+
|
|
188
|
+
isExtensible() {
|
|
189
|
+
init();
|
|
190
|
+
return Reflect.isExtensible(real);
|
|
191
|
+
},
|
|
192
|
+
|
|
193
|
+
preventExtensions(target) {
|
|
194
|
+
init();
|
|
195
|
+
// Must also prevent extensions on target to satisfy Proxy invariants
|
|
196
|
+
const result = Reflect.preventExtensions(real);
|
|
197
|
+
if (result) {
|
|
198
|
+
Reflect.preventExtensions(target);
|
|
199
|
+
}
|
|
200
|
+
return result;
|
|
201
|
+
},
|
|
202
|
+
|
|
203
|
+
defineProperty(target, prop, descriptor) {
|
|
204
|
+
// Used by monkeypatch libs: keep overrides on proxy target (overlay layer).
|
|
205
|
+
return Reflect.defineProperty(target, prop, descriptor);
|
|
206
|
+
},
|
|
207
|
+
});
|
|
208
|
+
}
|
package/lib/egg.js
CHANGED
|
@@ -316,39 +316,10 @@ class EggApplication extends EggCore {
|
|
|
316
316
|
options.lookup = options.lookup ?? self.config.httpclient.lookup;
|
|
317
317
|
realClient = new self.HttpClientNext(self, options);
|
|
318
318
|
};
|
|
319
|
-
return
|
|
320
|
-
|
|
319
|
+
return utils.createTransparentProxy({
|
|
320
|
+
createReal() {
|
|
321
321
|
init();
|
|
322
|
-
|
|
323
|
-
if (typeof value === 'function') {
|
|
324
|
-
return value.bind(realClient);
|
|
325
|
-
}
|
|
326
|
-
return value;
|
|
327
|
-
},
|
|
328
|
-
set(_target, prop, value) {
|
|
329
|
-
init();
|
|
330
|
-
realClient[prop] = value;
|
|
331
|
-
return true;
|
|
332
|
-
},
|
|
333
|
-
has(_target, prop) {
|
|
334
|
-
init();
|
|
335
|
-
return prop in realClient;
|
|
336
|
-
},
|
|
337
|
-
ownKeys() {
|
|
338
|
-
init();
|
|
339
|
-
return Reflect.ownKeys(realClient);
|
|
340
|
-
},
|
|
341
|
-
getOwnPropertyDescriptor(_target, prop) {
|
|
342
|
-
init();
|
|
343
|
-
return Object.getOwnPropertyDescriptor(realClient, prop);
|
|
344
|
-
},
|
|
345
|
-
deleteProperty(_target, prop) {
|
|
346
|
-
init();
|
|
347
|
-
return delete realClient[prop];
|
|
348
|
-
},
|
|
349
|
-
getPrototypeOf() {
|
|
350
|
-
init();
|
|
351
|
-
return Object.getPrototypeOf(realClient);
|
|
322
|
+
return realClient;
|
|
352
323
|
},
|
|
353
324
|
});
|
|
354
325
|
}
|