urllib 3.0.0-alpha.1 → 3.0.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/README.md +15 -113
- package/package.json +13 -9
- package/src/HttpAgent.ts +72 -0
- package/src/HttpClient.ts +303 -84
- package/src/Request.ts +51 -54
- package/src/Response.ts +11 -10
- package/src/cjs/HttpAgent.d.ts +16 -0
- package/src/cjs/HttpAgent.js +62 -0
- package/src/cjs/HttpAgent.js.map +1 -0
- package/src/cjs/HttpClient.d.ts +28 -1
- package/src/cjs/HttpClient.js +371 -198
- package/src/cjs/HttpClient.js.map +1 -1
- package/src/cjs/Request.d.ts +47 -54
- package/src/cjs/Response.d.ts +9 -8
- package/src/cjs/utils.d.ts +1 -0
- package/src/cjs/utils.js +7 -1
- package/src/cjs/utils.js.map +1 -1
- package/src/esm/HttpAgent.d.ts +16 -0
- package/src/esm/HttpAgent.js +58 -0
- package/src/esm/HttpAgent.js.map +1 -0
- package/src/esm/HttpClient.d.ts +28 -1
- package/src/esm/HttpClient.js +372 -199
- package/src/esm/HttpClient.js.map +1 -1
- package/src/esm/Request.d.ts +47 -54
- package/src/esm/Response.d.ts +9 -8
- package/src/esm/utils.d.ts +1 -0
- package/src/esm/utils.js +5 -0
- package/src/esm/utils.js.map +1 -1
- package/src/index.ts +0 -1
- package/src/utils.ts +6 -0
package/README.md
CHANGED
@@ -79,58 +79,27 @@ console.log('status: %s, body size: %d, headers: %j', res.statusCode, data.lengt
|
|
79
79
|
- ***options*** Object - Optional
|
80
80
|
- ***method*** String - Request method, defaults to `GET`. Could be `GET`, `POST`, `DELETE` or `PUT`. Alias 'type'.
|
81
81
|
- ***data*** Object - Data to be sent. Will be stringify automatically.
|
82
|
-
- ***dataAsQueryString*** Boolean - Force convert `data` to query string.
|
83
82
|
- ***content*** String | [Buffer](http://nodejs.org/api/buffer.html) - Manually set the content of payload. If set, `data` will be ignored.
|
84
83
|
- ***stream*** [stream.Readable](http://nodejs.org/api/stream.html#stream_class_stream_readable) - Stream to be pipe to the remote. If set, `data` and `content` will be ignored.
|
85
84
|
- ***writeStream*** [stream.Writable](http://nodejs.org/api/stream.html#stream_class_stream_writable) - A writable stream to be piped by the response stream. Responding data will be write to this stream and `callback` will be called with `data` set `null` after finished writing.
|
86
85
|
- ***files*** {Array<ReadStream|Buffer|String> | Object | ReadStream | Buffer | String - The files will send with `multipart/form-data` format, base on `formstream`. If `method` not set, will use `POST` method by default.
|
87
|
-
- ***consumeWriteStream*** [true] - consume the writeStream, invoke the callback after writeStream close.
|
88
86
|
- ***contentType*** String - Type of request data. Could be `json` (**Notes**: not use `application/json` here). If it's `json`, will auto set `Content-Type: application/json` header.
|
89
|
-
- ***nestedQuerystring*** Boolean - urllib default use querystring to stringify form data which don't support nested object, will use [qs](https://github.com/ljharb/qs) instead of querystring to support nested object by set this option to true.
|
90
87
|
- ***dataType*** String - Type of response data. Could be `text` or `json`. If it's `text`, the `callback`ed `data` would be a String. If it's `json`, the `data` of callback would be a parsed JSON Object and will auto set `Accept: application/json` header. Default `callback`ed `data` would be a `Buffer`.
|
91
88
|
- **fixJSONCtlChars** Boolean - Fix the control characters (U+0000 through U+001F) before JSON parse response. Default is `false`.
|
92
89
|
- ***headers*** Object - Request headers.
|
93
|
-
- ***keepHeaderCase*** Boolean - by default will convert header keys to lowercase
|
94
90
|
- ***timeout*** Number | Array - Request timeout in milliseconds for connecting phase and response receiving phase. Defaults to `exports.TIMEOUT`, both are 5s. You can use `timeout: 5000` to tell urllib use same timeout on two phase or set them seperately such as `timeout: [3000, 5000]`, which will set connecting timeout to 3s and response 5s.
|
95
91
|
- ***auth*** String - `username:password` used in HTTP Basic Authorization.
|
96
|
-
- ***digestAuth*** String - `username:password` used in HTTP [Digest Authorization](http://en.wikipedia.org/wiki/Digest_access_authentication).
|
97
92
|
- ***agent*** [http.Agent](http://nodejs.org/api/http.html#http_class_http_agent) - HTTP Agent object.
|
98
93
|
Set `false` if you does not use agent.
|
99
94
|
- ***httpsAgent*** [https.Agent](http://nodejs.org/api/https.html#https_class_https_agent) - HTTPS Agent object.
|
100
95
|
Set `false` if you does not use agent.
|
101
|
-
- ***ca*** String | Buffer | Array - An array of strings or Buffers of trusted certificates.
|
102
|
-
If this is omitted several well known "root" CAs will be used, like VeriSign.
|
103
|
-
These are used to authorize connections.
|
104
|
-
**Notes**: This is necessary only if the server uses the self-signed certificate
|
105
|
-
- ***rejectUnauthorized*** Boolean - If true, the server certificate is verified against the list of supplied CAs.
|
106
|
-
An 'error' event is emitted if verification fails. Default: true.
|
107
|
-
- ***pfx*** String | Buffer - A string or Buffer containing the private key,
|
108
|
-
certificate and CA certs of the server in PFX or PKCS12 format.
|
109
|
-
- ***key*** String | Buffer - A string or Buffer containing the private key of the client in PEM format.
|
110
|
-
**Notes**: This is necessary only if using the client certificate authentication
|
111
|
-
- ***cert*** String | Buffer - A string or Buffer containing the certificate key of the client in PEM format.
|
112
|
-
**Notes**: This is necessary only if using the client certificate authentication
|
113
|
-
- ***passphrase*** String - A string of passphrase for the private key or pfx.
|
114
|
-
- ***ciphers*** String - A string describing the ciphers to use or exclude.
|
115
|
-
- ***secureProtocol*** String - The SSL method to use, e.g. SSLv3_method to force SSL version 3.
|
116
96
|
- ***followRedirect*** Boolean - follow HTTP 3xx responses as redirects. defaults to false.
|
117
97
|
- ***maxRedirects*** Number - The maximum number of redirects to follow, defaults to 10.
|
118
98
|
- ***formatRedirectUrl*** Function - Format the redirect url by your self. Default is `url.resolve(from, to)`.
|
119
99
|
- ***beforeRequest*** Function - Before request hook, you can change every thing here.
|
120
100
|
- ***streaming*** Boolean - let you get the `res` object when request connected, default `false`. alias `customResponse`
|
121
|
-
- ***gzip*** Boolean - Accept gzip response content and auto decode it, default is `false`.
|
101
|
+
- ***gzip*** Boolean - Accept `gzip, br` response content and auto decode it, default is `false`.
|
122
102
|
- ***timing*** Boolean - Enable timing or not, default is `false`.
|
123
|
-
- ***enableProxy*** Boolean - Enable proxy request, default is `false`.
|
124
|
-
- ***proxy*** String | Object - proxy agent uri or options, default is `null`.
|
125
|
-
- ***lookup*** Function - Custom DNS lookup function, default is `dns.lookup`. Require node >= 4.0.0(for http protocol) and node >=8(for https protocol)
|
126
|
-
- ***checkAddress*** Function: optional, check request address to protect from SSRF and similar attacks. It receive tow arguments(`ip` and `family`) and should return true or false to identified the address is legal or not. It rely on `lookup` and have the same version requirement.
|
127
|
-
- ***trace*** Boolean - Enable capture stack include call site of library entrance, default is `false`.
|
128
|
-
- ***socketPath*** String - optional Unix Domain Socket. (Refer to [Node.js Document](https://nodejs.org/dist/latest-v14.x/docs/api/http.html#http_http_request_options_callback))
|
129
|
-
|
130
|
-
#### Returns
|
131
|
-
|
132
|
-
- **data** Buffer | Object - The data responsed. Would be a Buffer if `dataType` is set to `text` or an JSON parsed into Object if it's set to `json`.
|
133
|
-
- **res** [http.IncomingMessage](http://nodejs.org/api/http.html#http_http_incomingmessage) - The response.
|
134
103
|
|
135
104
|
#### Options: `options.data`
|
136
105
|
|
@@ -150,14 +119,14 @@ For `GET` request, `data` will be stringify to query string, e.g. `http://exampl
|
|
150
119
|
|
151
120
|
For others like `POST`, `PATCH` or `PUT` request,
|
152
121
|
in defaults, the `data` will be stringify into `application/x-www-form-urlencoded` format
|
153
|
-
if `
|
122
|
+
if `content-type` header is not set.
|
154
123
|
|
155
|
-
If `
|
124
|
+
If `content-type` is `application/json`, the `data` will be `JSON.stringify` to JSON data format.
|
156
125
|
|
157
126
|
#### Options: `options.content`
|
158
127
|
|
159
128
|
`options.content` is useful when you wish to construct the request body by yourself,
|
160
|
-
for example making a `
|
129
|
+
for example making a `content-type: application/json` request.
|
161
130
|
|
162
131
|
Notes that if you want to send a JSON body, you should stringify it yourself:
|
163
132
|
|
@@ -178,8 +147,8 @@ It would make a HTTP request like:
|
|
178
147
|
|
179
148
|
```http
|
180
149
|
POST / HTTP/1.1
|
181
|
-
|
182
|
-
|
150
|
+
host: example.com
|
151
|
+
content-type: application/json
|
183
152
|
|
184
153
|
{
|
185
154
|
"a": "hello",
|
@@ -193,7 +162,7 @@ This exmaple can use `options.data` with `application/json` content type:
|
|
193
162
|
await request('https://example.com', {
|
194
163
|
method: 'POST',
|
195
164
|
headers: {
|
196
|
-
'
|
165
|
+
'content-type': 'application/json'
|
197
166
|
},
|
198
167
|
data: {
|
199
168
|
a: 'hello',
|
@@ -268,7 +237,6 @@ Response is normal object, it contains:
|
|
268
237
|
- `status` or `statusCode`: response status code.
|
269
238
|
- `-1` meaning some network error like `ENOTFOUND`
|
270
239
|
- `-2` meaning ConnectionTimeoutError
|
271
|
-
- `statusMessage`: response status message.
|
272
240
|
- `headers`: response http headers, default is `{}`
|
273
241
|
- `size`: response size
|
274
242
|
- `aborted`: response was aborted or not
|
@@ -279,76 +247,12 @@ Response is normal object, it contains:
|
|
279
247
|
- `socketHandledRequests`: socket already handled request count
|
280
248
|
- `socketHandledResponses`: socket already handled response count
|
281
249
|
|
282
|
-
|
283
|
-
|
284
|
-
If the underlaying connection was terminated before `response.end()` was called,
|
285
|
-
`res.aborted` should be `true`.
|
286
|
-
|
287
|
-
```js
|
288
|
-
import { createServer } from 'http';
|
289
|
-
|
290
|
-
createServer((req, res) => {
|
291
|
-
req.resume();
|
292
|
-
req.on('end', () => {
|
293
|
-
res.write('foo haha\n');
|
294
|
-
setTimeout(() => {
|
295
|
-
res.write('foo haha 2');
|
296
|
-
setTimeout(() => {
|
297
|
-
res.socket.end();
|
298
|
-
}, 300);
|
299
|
-
}, 200);
|
300
|
-
return;
|
301
|
-
});
|
302
|
-
}).listen(2022);
|
303
|
-
|
304
|
-
const { data, res } = await request('http://127.0.0.1:2022/socket.end');
|
305
|
-
assert.equal(data.toString(), 'foo haha\nfoo haha 2');
|
306
|
-
assert(res.aborted);
|
307
|
-
```
|
308
|
-
|
309
|
-
### HttpClient2
|
310
|
-
|
311
|
-
HttpClient2 base on HttpClient.
|
312
|
-
|
313
|
-
#### Options
|
314
|
-
|
315
|
-
options extends from urllib, besides below
|
250
|
+
## Run test with debug log
|
316
251
|
|
317
|
-
|
318
|
-
|
319
|
-
- ***isRetry*** Function - determine whether retry, a response object as the first argument. it will retry when status >= 500 by default. Request error is not included.
|
320
|
-
|
321
|
-
#### Warning
|
322
|
-
|
323
|
-
It's not supported by using retry and writeStream, because the retry request can't stop the stream which is consuming.
|
324
|
-
|
325
|
-
### Trace
|
326
|
-
|
327
|
-
If set trace true, error stack will contains full call stack, like
|
328
|
-
|
329
|
-
```
|
330
|
-
Error: connect ECONNREFUSED 127.0.0.1:11
|
331
|
-
at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1113:14)
|
332
|
-
--------------------
|
333
|
-
at ~/workspace/urllib/lib/urllib.js:150:13
|
334
|
-
at new Promise (<anonymous>)
|
335
|
-
at Object.request (~/workspace/urllib/lib/urllib.js:149:10)
|
336
|
-
at Context.<anonymous> (~/workspace/urllib/test/urllib_promise.test.js:49:19)
|
337
|
-
....
|
252
|
+
```bash
|
253
|
+
NODE_DEBUG=urllib npm test
|
338
254
|
```
|
339
255
|
|
340
|
-
When open the trace, urllib may have poor perfomance, please consider carefully.
|
341
|
-
|
342
|
-
## TODO
|
343
|
-
|
344
|
-
- ❎ Support Proxy
|
345
|
-
- ✅ Upload file like form upload
|
346
|
-
- ✅ Auto redirect handle
|
347
|
-
- ✅ https & self-signed certificate
|
348
|
-
- ✅ Connection timeout & Response timeout
|
349
|
-
- ✅ Support `Accept-Encoding=gzip` by default
|
350
|
-
- ✅ Support [Digest access authentication](http://en.wikipedia.org/wiki/Digest_access_authentication)
|
351
|
-
|
352
256
|
<!-- GITCONTRIBUTOR_START -->
|
353
257
|
|
354
258
|
## Contributors
|
@@ -356,18 +260,16 @@ When open the trace, urllib may have poor perfomance, please consider carefully.
|
|
356
260
|
|[<img src="https://avatars.githubusercontent.com/u/156269?v=4" width="100px;"/><br/><sub><b>fengmk2</b></sub>](https://github.com/fengmk2)<br/>|[<img src="https://avatars.githubusercontent.com/u/985607?v=4" width="100px;"/><br/><sub><b>dead-horse</b></sub>](https://github.com/dead-horse)<br/>|[<img src="https://avatars.githubusercontent.com/u/288288?v=4" width="100px;"/><br/><sub><b>xingrz</b></sub>](https://github.com/xingrz)<br/>|[<img src="https://avatars.githubusercontent.com/u/360661?v=4" width="100px;"/><br/><sub><b>popomore</b></sub>](https://github.com/popomore)<br/>|[<img src="https://avatars.githubusercontent.com/u/327019?v=4" width="100px;"/><br/><sub><b>JacksonTian</b></sub>](https://github.com/JacksonTian)<br/>|[<img src="https://avatars.githubusercontent.com/u/543405?v=4" width="100px;"/><br/><sub><b>ibigbug</b></sub>](https://github.com/ibigbug)<br/>|
|
357
261
|
| :---: | :---: | :---: | :---: | :---: | :---: |
|
358
262
|
|[<img src="https://avatars.githubusercontent.com/u/14790466?v=4" width="100px;"/><br/><sub><b>greenkeeperio-bot</b></sub>](https://github.com/greenkeeperio-bot)<br/>|[<img src="https://avatars.githubusercontent.com/u/227713?v=4" width="100px;"/><br/><sub><b>atian25</b></sub>](https://github.com/atian25)<br/>|[<img src="https://avatars.githubusercontent.com/u/5381764?v=4" width="100px;"/><br/><sub><b>paambaati</b></sub>](https://github.com/paambaati)<br/>|[<img src="https://avatars.githubusercontent.com/u/1433247?v=4" width="100px;"/><br/><sub><b>denghongcai</b></sub>](https://github.com/denghongcai)<br/>|[<img src="https://avatars.githubusercontent.com/u/2842176?v=4" width="100px;"/><br/><sub><b>XadillaX</b></sub>](https://github.com/XadillaX)<br/>|[<img src="https://avatars.githubusercontent.com/u/1147375?v=4" width="100px;"/><br/><sub><b>alsotang</b></sub>](https://github.com/alsotang)<br/>|
|
359
|
-
|[<img src="https://avatars.githubusercontent.com/u/546535?v=4" width="100px;"/><br/><sub><b>leoner</b></sub>](https://github.com/leoner)<br/>|[<img src="https://avatars.githubusercontent.com/u/19908330?v=4" width="100px;"/><br/><sub><b>hyj1991</b></sub>](https://github.com/hyj1991)<br/>|[<img src="https://avatars.githubusercontent.com/u/1747852?v=4" width="100px;"/><br/><sub><b>isayme</b></sub>](https://github.com/isayme)<br/>|[<img src="https://avatars.githubusercontent.com/u/
|
360
|
-
|[<img src="https://avatars.githubusercontent.com/u/
|
361
|
-
|[<img src="https://avatars.githubusercontent.com/u/
|
362
|
-
|[<img src="https://avatars.githubusercontent.com/u/
|
263
|
+
|[<img src="https://avatars.githubusercontent.com/u/546535?v=4" width="100px;"/><br/><sub><b>leoner</b></sub>](https://github.com/leoner)<br/>|[<img src="https://avatars.githubusercontent.com/u/19908330?v=4" width="100px;"/><br/><sub><b>hyj1991</b></sub>](https://github.com/hyj1991)<br/>|[<img src="https://avatars.githubusercontent.com/u/1747852?v=4" width="100px;"/><br/><sub><b>isayme</b></sub>](https://github.com/isayme)<br/>|[<img src="https://avatars.githubusercontent.com/u/6897780?v=4" width="100px;"/><br/><sub><b>killagu</b></sub>](https://github.com/killagu)<br/>|[<img src="https://avatars.githubusercontent.com/u/252317?v=4" width="100px;"/><br/><sub><b>cyjake</b></sub>](https://github.com/cyjake)<br/>|[<img src="https://avatars.githubusercontent.com/u/5856440?v=4" width="100px;"/><br/><sub><b>whxaxes</b></sub>](https://github.com/whxaxes)<br/>|
|
264
|
+
|[<img src="https://avatars.githubusercontent.com/u/309219?v=4" width="100px;"/><br/><sub><b>chadxz</b></sub>](https://github.com/chadxz)<br/>|[<img src="https://avatars.githubusercontent.com/u/5139554?v=4" width="100px;"/><br/><sub><b>danielwpz</b></sub>](https://github.com/danielwpz)<br/>|[<img src="https://avatars.githubusercontent.com/u/5127897?v=4" width="100px;"/><br/><sub><b>danielsss</b></sub>](https://github.com/danielsss)<br/>|[<img src="https://avatars.githubusercontent.com/u/3367820?v=4" width="100px;"/><br/><sub><b>Jeff-Tian</b></sub>](https://github.com/Jeff-Tian)<br/>|[<img src="https://avatars.githubusercontent.com/u/32407?v=4" width="100px;"/><br/><sub><b>jedahan</b></sub>](https://github.com/jedahan)<br/>|[<img src="https://avatars.githubusercontent.com/u/17075261?v=4" width="100px;"/><br/><sub><b>nick-ng</b></sub>](https://github.com/nick-ng)<br/>|
|
265
|
+
|[<img src="https://avatars.githubusercontent.com/u/1706595?v=4" width="100px;"/><br/><sub><b>rishavsharan</b></sub>](https://github.com/rishavsharan)<br/>|[<img src="https://avatars.githubusercontent.com/u/1886161?v=4" width="100px;"/><br/><sub><b>willizm</b></sub>](https://github.com/willizm)<br/>|[<img src="https://avatars.githubusercontent.com/u/7227589?v=4" width="100px;"/><br/><sub><b>davidkhala</b></sub>](https://github.com/davidkhala)<br/>|[<img src="https://avatars.githubusercontent.com/u/535479?v=4" width="100px;"/><br/><sub><b>aleafs</b></sub>](https://github.com/aleafs)<br/>|[<img src="https://avatars.githubusercontent.com/u/3689968?v=4" width="100px;"/><br/><sub><b>Amunu</b></sub>](https://github.com/Amunu)<br/>|[<img src="https://avatars.githubusercontent.com/in/9426?v=4" width="100px;"/><br/><sub><b>azure-pipelines[bot]</b></sub>](https://github.com/apps/azure-pipelines)<br/>|
|
266
|
+
|[<img src="https://avatars.githubusercontent.com/u/1281323?v=4" width="100px;"/><br/><sub><b>changzhiwin</b></sub>](https://github.com/changzhiwin)<br/>|[<img src="https://avatars.githubusercontent.com/u/929503?v=4" width="100px;"/><br/><sub><b>yuzhigang33</b></sub>](https://github.com/yuzhigang33)<br/>|[<img src="https://avatars.githubusercontent.com/u/981128?v=4" width="100px;"/><br/><sub><b>fishbar</b></sub>](https://github.com/fishbar)<br/>|[<img src="https://avatars.githubusercontent.com/u/1207064?v=4" width="100px;"/><br/><sub><b>gxcsoccer</b></sub>](https://github.com/gxcsoccer)<br/>|[<img src="https://avatars.githubusercontent.com/u/17476119?v=4" width="100px;"/><br/><sub><b>mars-coder</b></sub>](https://github.com/mars-coder)<br/>|[<img src="https://avatars.githubusercontent.com/u/929179?v=4" width="100px;"/><br/><sub><b>rockdai</b></sub>](https://github.com/rockdai)<br/>|
|
363
267
|
[<img src="https://avatars.githubusercontent.com/u/2196373?v=4" width="100px;"/><br/><sub><b>dickeylth</b></sub>](https://github.com/dickeylth)<br/>|[<img src="https://avatars.githubusercontent.com/u/13050025?v=4" width="100px;"/><br/><sub><b>aladdin-add</b></sub>](https://github.com/aladdin-add)<br/>
|
364
268
|
|
365
|
-
This project follows the git-contributor [spec](https://github.com/xudafeng/git-contributor), auto updated at `
|
269
|
+
This project follows the git-contributor [spec](https://github.com/xudafeng/git-contributor), auto updated at `Tue Jul 05 2022 16:17:31 GMT+0800`.
|
366
270
|
|
367
271
|
<!-- GITCONTRIBUTOR_END -->
|
368
272
|
|
369
273
|
## License
|
370
274
|
|
371
275
|
[MIT](LICENSE)
|
372
|
-
|
373
|
-
[bluebird]: https://github.com/petkaantonov/bluebird
|
package/package.json
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
{
|
2
2
|
"name": "urllib",
|
3
|
-
"version": "3.0.0
|
3
|
+
"version": "3.0.0",
|
4
|
+
"publishConfig": {
|
5
|
+
"tag": "latest"
|
6
|
+
},
|
4
7
|
"description": "Help in opening URLs (mostly HTTP) in a complex world — basic and digest authentication, redirections, cookies and more. Base undici fetch API.",
|
5
8
|
"keywords": [
|
6
9
|
"urllib",
|
@@ -54,15 +57,19 @@
|
|
54
57
|
"dependencies": {
|
55
58
|
"default-user-agent": "^1.0.0",
|
56
59
|
"digest-header": "^0.0.1",
|
57
|
-
"
|
60
|
+
"form-data-encoder": "^1.7.2",
|
61
|
+
"formdata-node": "^4.3.3",
|
58
62
|
"mime-types": "^2.1.35",
|
59
|
-
"
|
63
|
+
"pump": "^3.0.0",
|
64
|
+
"undici": "^5.6.0"
|
60
65
|
},
|
61
66
|
"devDependencies": {
|
62
67
|
"@types/busboy": "^1.5.0",
|
63
68
|
"@types/default-user-agent": "^1.0.0",
|
64
69
|
"@types/jest": "28",
|
65
70
|
"@types/mime-types": "^2.1.1",
|
71
|
+
"@types/pump": "^1.1.1",
|
72
|
+
"@types/selfsigned": "^2.0.1",
|
66
73
|
"busboy": "^1.6.0",
|
67
74
|
"coffee": "5",
|
68
75
|
"egg-ci": "2",
|
@@ -72,19 +79,16 @@
|
|
72
79
|
"iconv-lite": "^0.6.3",
|
73
80
|
"jest": "28",
|
74
81
|
"jest-summary-reporter": "^0.0.2",
|
75
|
-
"
|
82
|
+
"selfsigned": "^2.0.1",
|
76
83
|
"ts-jest": "28",
|
77
84
|
"tslib": "^2.4.0",
|
78
85
|
"typescript": "4"
|
79
86
|
},
|
80
87
|
"engines": {
|
81
|
-
"node": ">=
|
88
|
+
"node": ">= 14.0.0"
|
82
89
|
},
|
83
90
|
"ci": {
|
84
|
-
"version": "16, 18"
|
85
|
-
},
|
86
|
-
"publishConfig": {
|
87
|
-
"tag": "next"
|
91
|
+
"version": "14, 16, 18"
|
88
92
|
},
|
89
93
|
"license": "MIT"
|
90
94
|
}
|
package/src/HttpAgent.ts
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
import dns from 'dns';
|
2
|
+
import { LookupFunction, isIP } from 'net';
|
3
|
+
import {
|
4
|
+
Agent,
|
5
|
+
} from 'undici';
|
6
|
+
import { DispatchHandlers } from 'undici/types/dispatcher';
|
7
|
+
import { BuildOptions } from 'undici/types/connector';
|
8
|
+
|
9
|
+
export type CheckAddressFunction = (ip: string, family: number | string) => boolean;
|
10
|
+
|
11
|
+
export type HttpAgentOptions = {
|
12
|
+
lookup?: LookupFunction;
|
13
|
+
checkAddress?: CheckAddressFunction;
|
14
|
+
connect?: BuildOptions,
|
15
|
+
};
|
16
|
+
|
17
|
+
class IllegalAddressError extends Error {
|
18
|
+
hostname: string;
|
19
|
+
ip: string;
|
20
|
+
family: number;
|
21
|
+
|
22
|
+
constructor(hostname: string, ip: string, family: number) {
|
23
|
+
const message = 'illegal address';
|
24
|
+
super(message);
|
25
|
+
this.name = this.constructor.name;
|
26
|
+
this.hostname = hostname;
|
27
|
+
this.ip = ip;
|
28
|
+
this.family = family;
|
29
|
+
Error.captureStackTrace(this, this.constructor);
|
30
|
+
}
|
31
|
+
}
|
32
|
+
|
33
|
+
export class HttpAgent extends Agent {
|
34
|
+
#checkAddress?: CheckAddressFunction;
|
35
|
+
|
36
|
+
constructor(options: HttpAgentOptions) {
|
37
|
+
/* eslint node/prefer-promises/dns: off*/
|
38
|
+
const _lookup = options.lookup ?? dns.lookup;
|
39
|
+
const lookup: LookupFunction = (hostname, dnsOptions, callback) => {
|
40
|
+
_lookup(hostname, dnsOptions, (err, address, family) => {
|
41
|
+
if (err) return callback(err, address, family);
|
42
|
+
if (options.checkAddress && !options.checkAddress(address, family)) {
|
43
|
+
err = new IllegalAddressError(hostname, address, family);
|
44
|
+
}
|
45
|
+
callback(err, address, family);
|
46
|
+
});
|
47
|
+
};
|
48
|
+
super({
|
49
|
+
connect: { ...options.connect, lookup },
|
50
|
+
});
|
51
|
+
this.#checkAddress = options.checkAddress;
|
52
|
+
}
|
53
|
+
|
54
|
+
dispatch(options: Agent.DispatchOptions, handler: DispatchHandlers): boolean {
|
55
|
+
if (this.#checkAddress && options.origin) {
|
56
|
+
const originUrl = typeof options.origin === 'string' ? new URL(options.origin) : options.origin;
|
57
|
+
let hostname = originUrl.hostname;
|
58
|
+
// [2001:db8:2de::e13] => 2001:db8:2de::e13
|
59
|
+
if (hostname.startsWith('[') && hostname.endsWith(']')) {
|
60
|
+
hostname = hostname.substring(1, hostname.length - 1);
|
61
|
+
}
|
62
|
+
const family = isIP(hostname);
|
63
|
+
if (family === 4 || family === 6) {
|
64
|
+
// if request hostname is ip, custom lookup won't excute
|
65
|
+
if (!this.#checkAddress(hostname, family)) {
|
66
|
+
throw new IllegalAddressError(hostname, hostname, family);
|
67
|
+
}
|
68
|
+
}
|
69
|
+
}
|
70
|
+
return super.dispatch(options, handler);
|
71
|
+
}
|
72
|
+
}
|