urllib 2.38.0 → 3.0.0-alpha.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 CHANGED
@@ -21,63 +21,29 @@ and digest authentication, redirections, cookies, timeout and more.
21
21
  ## Install
22
22
 
23
23
  ```bash
24
- $ npm install urllib --save
24
+ npm install urllib --save
25
25
  ```
26
26
 
27
27
  ## Usage
28
28
 
29
- ### callback
29
+ ### TypeScript and ESM
30
30
 
31
31
  ```js
32
- var urllib = require('urllib');
32
+ import { request } from 'urllib';
33
33
 
34
- urllib.request('http://cnodejs.org/', function (err, data, res) {
35
- if (err) {
36
- throw err; // you need to handle error
37
- }
38
- console.log(res.statusCode);
39
- console.log(res.headers);
40
- // data is Buffer instance
41
- console.log(data.toString());
42
- });
34
+ const { data, res } = await request('http://cnodejs.org/');
35
+ // result: { data: Buffer, res: Response }
36
+ console.log('status: %s, body size: %d, headers: %j', res.statusCode, data.length, res.headers);
43
37
  ```
44
38
 
45
- ### Promise
46
-
47
- If you've installed [bluebird][bluebird],
48
- [bluebird][bluebird] will be used.
49
- `urllib` does not install [bluebird][bluebird] for you.
50
-
51
- Otherwise, if you're using a node that has native v8 Promises (v0.11.13+),
52
- then that will be used.
53
-
54
- Otherwise, this library will crash the process and exit,
55
- so you might as well install [bluebird][bluebird] as a dependency!
39
+ ### CommonJS
56
40
 
57
41
  ```js
58
- var urllib = require('urllib');
42
+ const { request } = require('urllib');
59
43
 
60
- urllib.request('http://nodejs.org').then(function (result) {
61
- // result: {data: buffer, res: response object}
62
- console.log('status: %s, body size: %d, headers: %j', result.res.statusCode, result.data.length, result.res.headers);
63
- }).catch(function (err) {
64
- console.error(err);
65
- });
66
- ```
67
-
68
- ### co & generator
69
-
70
- If you are using [co](https://github.com/visionmedia/co) or [koa](https://github.com/koajs/koa):
71
-
72
- ```js
73
- var co = require('co');
74
- var urllib = require('urllib');
75
-
76
- co(function* () {
77
- var result = yield urllib.requestThunk('http://nodejs.org');
78
- console.log('status: %s, body size: %d, headers: %j',
79
- result.status, result.data.length, result.headers);
80
- })();
44
+ const { data, res } = await request('http://cnodejs.org/');
45
+ // result: { data: Buffer, res: Response }
46
+ console.log('status: %s, body size: %d, headers: %j', res.statusCode, data.length, res.headers);
81
47
  ```
82
48
 
83
49
  ## Global `response` event
@@ -85,9 +51,10 @@ co(function* () {
85
51
  You should create a urllib instance first.
86
52
 
87
53
  ```js
88
- var httpclient = require('urllib').create();
54
+ import { HttpClient } from 'urllib';
89
55
 
90
- httpclient.on('response', function (info) {
56
+ const httpclient = new HttpClient();
57
+ httpclient.on('response', (info) => {
91
58
  error: err,
92
59
  ctx: args.ctx,
93
60
  req: {
@@ -98,89 +65,83 @@ httpclient.on('response', function (info) {
98
65
  res: res
99
66
  });
100
67
 
101
- httpclient.request('http://nodejs.org', function (err, body) {
102
- console.log('body size: %d', body.length);
103
- });
68
+ const { data, res } = await httpclient.request('https://nodejs.org');
69
+ console.log('status: %s, body size: %d, headers: %j', res.statusCode, data.length, res.headers);
104
70
  ```
105
71
 
106
72
  ## API Doc
107
73
 
108
- ### Method: `http.request(url[, options][, callback])`
74
+ ### Method: `async request(url[, options])`
109
75
 
110
76
  #### Arguments
111
77
 
112
78
  - **url** String | Object - The URL to request, either a String or a Object that return by [url.parse](http://nodejs.org/api/url.html#url_url_parse_urlstr_parsequerystring_slashesdenotehost).
113
79
  - ***options*** Object - Optional
114
- - ***method*** String - Request method, defaults to `GET`. Could be `GET`, `POST`, `DELETE` or `PUT`. Alias 'type'.
115
- - ***data*** Object - Data to be sent. Will be stringify automatically.
116
- - ***dataAsQueryString*** Boolean - Force convert `data` to query string.
117
- - ***content*** String | [Buffer](http://nodejs.org/api/buffer.html) - Manually set the content of payload. If set, `data` will be ignored.
118
- - ***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.
119
- - ***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.
120
- - ***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.
121
- - ***consumeWriteStream*** [true] - consume the writeStream, invoke the callback after writeStream close.
122
- - ***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.
123
- - ***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.
124
- - ***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`.
125
- - **fixJSONCtlChars** Boolean - Fix the control characters (U+0000 through U+001F) before JSON parse response. Default is `false`.
126
- - ***headers*** Object - Request headers.
127
- - ***keepHeaderCase*** Boolean - by default will convert header keys to lowercase
128
- - ***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.
129
- - ***auth*** String - `username:password` used in HTTP Basic Authorization.
130
- - ***digestAuth*** String - `username:password` used in HTTP [Digest Authorization](http://en.wikipedia.org/wiki/Digest_access_authentication).
131
- - ***agent*** [http.Agent](http://nodejs.org/api/http.html#http_class_http_agent) - HTTP Agent object.
80
+ - ***method*** String - Request method, defaults to `GET`. Could be `GET`, `POST`, `DELETE` or `PUT`. Alias 'type'.
81
+ - ***data*** Object - Data to be sent. Will be stringify automatically.
82
+ - ***dataAsQueryString*** Boolean - Force convert `data` to query string.
83
+ - ***content*** String | [Buffer](http://nodejs.org/api/buffer.html) - Manually set the content of payload. If set, `data` will be ignored.
84
+ - ***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
+ - ***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
+ - ***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
+ - ***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
+ - ***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
+ - **fixJSONCtlChars** Boolean - Fix the control characters (U+0000 through U+001F) before JSON parse response. Default is `false`.
92
+ - ***headers*** Object - Request headers.
93
+ - ***keepHeaderCase*** Boolean - by default will convert header keys to lowercase
94
+ - ***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
+ - ***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
+ - ***agent*** [http.Agent](http://nodejs.org/api/http.html#http_class_http_agent) - HTTP Agent object.
132
98
  Set `false` if you does not use agent.
133
- - ***httpsAgent*** [https.Agent](http://nodejs.org/api/https.html#https_class_https_agent) - HTTPS Agent object.
99
+ - ***httpsAgent*** [https.Agent](http://nodejs.org/api/https.html#https_class_https_agent) - HTTPS Agent object.
134
100
  Set `false` if you does not use agent.
135
- - ***ca*** String | Buffer | Array - An array of strings or Buffers of trusted certificates.
101
+ - ***ca*** String | Buffer | Array - An array of strings or Buffers of trusted certificates.
136
102
  If this is omitted several well known "root" CAs will be used, like VeriSign.
137
103
  These are used to authorize connections.
138
104
  **Notes**: This is necessary only if the server uses the self-signed certificate
139
- - ***rejectUnauthorized*** Boolean - If true, the server certificate is verified against the list of supplied CAs.
105
+ - ***rejectUnauthorized*** Boolean - If true, the server certificate is verified against the list of supplied CAs.
140
106
  An 'error' event is emitted if verification fails. Default: true.
141
- - ***pfx*** String | Buffer - A string or Buffer containing the private key,
107
+ - ***pfx*** String | Buffer - A string or Buffer containing the private key,
142
108
  certificate and CA certs of the server in PFX or PKCS12 format.
143
- - ***key*** String | Buffer - A string or Buffer containing the private key of the client in PEM format.
109
+ - ***key*** String | Buffer - A string or Buffer containing the private key of the client in PEM format.
144
110
  **Notes**: This is necessary only if using the client certificate authentication
145
- - ***cert*** String | Buffer - A string or Buffer containing the certificate key of the client in PEM format.
111
+ - ***cert*** String | Buffer - A string or Buffer containing the certificate key of the client in PEM format.
146
112
  **Notes**: This is necessary only if using the client certificate authentication
147
- - ***passphrase*** String - A string of passphrase for the private key or pfx.
148
- - ***ciphers*** String - A string describing the ciphers to use or exclude.
149
- - ***secureProtocol*** String - The SSL method to use, e.g. SSLv3_method to force SSL version 3.
150
- - ***followRedirect*** Boolean - follow HTTP 3xx responses as redirects. defaults to false.
151
- - ***maxRedirects*** Number - The maximum number of redirects to follow, defaults to 10.
152
- - ***formatRedirectUrl*** Function - Format the redirect url by your self. Default is `url.resolve(from, to)`.
153
- - ***beforeRequest*** Function - Before request hook, you can change every thing here.
154
- - ***streaming*** Boolean - let you get the `res` object when request connected, default `false`. alias `customResponse`
155
- - ***gzip*** Boolean - Accept gzip response content and auto decode it, default is `false`.
156
- - ***timing*** Boolean - Enable timing or not, default is `false`.
157
- - ***enableProxy*** Boolean - Enable proxy request, default is `false`.
158
- - ***proxy*** String | Object - proxy agent uri or options, default is `null`.
159
- - ***lookup*** Function - Custom DNS lookup function, default is `dns.lookup`. Require node >= 4.0.0(for http protocol) and node >=8(for https protocol)
160
- - ***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.
161
- - ***trace*** Boolean - Enable capture stack include call site of library entrance, default is `false`.
162
- - ***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))
163
- - ***callback(err, data, res)*** Function - Optional callback.
164
- - **err** Error - Would be `null` if no error accured.
165
- - **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`.
166
- - **res** [http.IncomingMessage](http://nodejs.org/api/http.html#http_http_incomingmessage) - The response.
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
+ - ***followRedirect*** Boolean - follow HTTP 3xx responses as redirects. defaults to false.
117
+ - ***maxRedirects*** Number - The maximum number of redirects to follow, defaults to 10.
118
+ - ***formatRedirectUrl*** Function - Format the redirect url by your self. Default is `url.resolve(from, to)`.
119
+ - ***beforeRequest*** Function - Before request hook, you can change every thing here.
120
+ - ***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`.
122
+ - ***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))
167
129
 
168
130
  #### Returns
169
131
 
170
- [http.ClientRequest](http://nodejs.org/api/http.html#http_class_http_clientrequest) - The request.
171
-
172
- Calling `.abort()` method of the request stream can cancel the request.
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.
173
134
 
174
135
  #### Options: `options.data`
175
136
 
176
137
  When making a request:
177
138
 
178
139
  ```js
179
- urllib.request('http://example.com', {
140
+ await request('https://example.com', {
180
141
  method: 'GET',
181
142
  data: {
182
143
  'a': 'hello',
183
- 'b': 'world'
144
+ 'b': 'world',
184
145
  }
185
146
  });
186
147
  ```
@@ -201,14 +162,14 @@ for example making a `Content-Type: application/json` request.
201
162
  Notes that if you want to send a JSON body, you should stringify it yourself:
202
163
 
203
164
  ```js
204
- urllib.request('http://example.com', {
165
+ await request('https://example.com', {
205
166
  method: 'POST',
206
167
  headers: {
207
168
  'Content-Type': 'application/json'
208
169
  },
209
170
  content: JSON.stringify({
210
171
  a: 'hello',
211
- b: 'world'
172
+ b: 'world',
212
173
  })
213
174
  });
214
175
  ```
@@ -229,14 +190,14 @@ Content-Type: application/json
229
190
  This exmaple can use `options.data` with `application/json` content type:
230
191
 
231
192
  ```js
232
- urllib.request('http://example.com', {
193
+ await request('https://example.com', {
233
194
  method: 'POST',
234
195
  headers: {
235
196
  'Content-Type': 'application/json'
236
197
  },
237
198
  data: {
238
199
  a: 'hello',
239
- b: 'world'
200
+ b: 'world',
240
201
  }
241
202
  });
242
203
  ```
@@ -246,24 +207,20 @@ urllib.request('http://example.com', {
246
207
  Upload a file with a `hello` field.
247
208
 
248
209
  ```js
249
- var urllib = request('urllib');
250
-
251
- var req = urllib.request('http://my.server.com/upload', {
210
+ await request('https://example.com/upload', {
211
+ method: 'POST',
252
212
  files: __filename,
253
213
  data: {
254
214
  hello: 'hello urllib',
255
215
  },
256
- }, function (err, data, res) {
257
- // upload finished
258
216
  });
259
217
  ```
260
218
 
261
219
  Upload multi files with a `hello` field.
262
220
 
263
221
  ```js
264
- var urllib = request('urllib');
265
-
266
- var req = urllib.request('http://my.server.com/upload', {
222
+ await request('https://example.com/upload', {
223
+ method: 'POST',
267
224
  files: [
268
225
  __filename,
269
226
  fs.createReadStream(__filename),
@@ -272,22 +229,17 @@ var req = urllib.request('http://my.server.com/upload', {
272
229
  data: {
273
230
  hello: 'hello urllib with multi files',
274
231
  },
275
- }, function (err, data, res) {
276
- // upload finished
277
232
  });
278
233
  ```
279
234
 
280
235
  Custom file field name with `uploadfile`.
281
236
 
282
237
  ```js
283
- var urllib = request('urllib');
284
-
285
- var req = urllib.request('http://my.server.com/upload', {
238
+ await request('https://example.com/upload', {
239
+ method: 'POST',
286
240
  files: {
287
241
  uploadfile: __filename,
288
242
  },
289
- }, function (err, data, res) {
290
- // upload finished
291
243
  });
292
244
  ```
293
245
 
@@ -296,19 +248,16 @@ var req = urllib.request('http://my.server.com/upload', {
296
248
  Uploads a file with [formstream](https://github.com/node-modules/formstream):
297
249
 
298
250
  ```js
299
- var urllib = require('urllib');
300
- var formstream = require('formstream');
251
+ import formstream from 'formstream';
301
252
 
302
- var form = formstream();
253
+ const form = formstream();
303
254
  form.file('file', __filename);
304
255
  form.field('hello', '你好urllib');
305
256
 
306
- var req = urllib.request('http://my.server.com/upload', {
257
+ await request('https://example.com/upload', {
307
258
  method: 'POST',
308
259
  headers: form.headers(),
309
- stream: form
310
- }, function (err, data, res) {
311
- // upload finished
260
+ stream: form,
312
261
  });
313
262
  ```
314
263
 
@@ -316,19 +265,19 @@ var req = urllib.request('http://my.server.com/upload', {
316
265
 
317
266
  Response is normal object, it contains:
318
267
 
319
- * `status` or `statusCode`: response status code.
320
- * `-1` meaning some network error like `ENOTFOUND`
321
- * `-2` meaning ConnectionTimeoutError
322
- * `statusMessage`: response status message.
323
- * `headers`: response http headers, default is `{}`
324
- * `size`: response size
325
- * `aborted`: response was aborted or not
326
- * `rt`: total request and response time in ms.
327
- * `timing`: timing object if timing enable.
328
- * `remoteAddress`: http server ip address
329
- * `remotePort`: http server ip port
330
- * `socketHandledRequests`: socket already handled request count
331
- * `socketHandledResponses`: socket already handled response count
268
+ - `status` or `statusCode`: response status code.
269
+ - `-1` meaning some network error like `ENOTFOUND`
270
+ - `-2` meaning ConnectionTimeoutError
271
+ - `statusMessage`: response status message.
272
+ - `headers`: response http headers, default is `{}`
273
+ - `size`: response size
274
+ - `aborted`: response was aborted or not
275
+ - `rt`: total request and response time in ms.
276
+ - `timing`: timing object if timing enable.
277
+ - `remoteAddress`: http server ip address
278
+ - `remotePort`: http server ip port
279
+ - `socketHandledRequests`: socket already handled request count
280
+ - `socketHandledResponses`: socket already handled response count
332
281
 
333
282
  #### Response: `res.aborted`
334
283
 
@@ -336,30 +285,30 @@ If the underlaying connection was terminated before `response.end()` was called,
336
285
  `res.aborted` should be `true`.
337
286
 
338
287
  ```js
339
- require('http').createServer(function (req, res) {
288
+ import { createServer } from 'http';
289
+
290
+ createServer((req, res) => {
340
291
  req.resume();
341
- req.on('end', function () {
292
+ req.on('end', () => {
342
293
  res.write('foo haha\n');
343
- setTimeout(function () {
294
+ setTimeout(() => {
344
295
  res.write('foo haha 2');
345
- setTimeout(function () {
296
+ setTimeout(() => {
346
297
  res.socket.end();
347
298
  }, 300);
348
299
  }, 200);
349
300
  return;
350
301
  });
351
- }).listen(1984);
302
+ }).listen(2022);
352
303
 
353
- urllib.request('http://127.0.0.1:1984/socket.end', function (err, data, res) {
354
- data.toString().should.equal('foo haha\nfoo haha 2');
355
- should.ok(res.aborted);
356
- done();
357
- });
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);
358
307
  ```
359
308
 
360
309
  ### HttpClient2
361
310
 
362
- HttpClient2 is a new instance for future. request method only return a promise, compatible with `async/await` and generator in co.
311
+ HttpClient2 base on HttpClient.
363
312
 
364
313
  #### Options
365
314
 
@@ -373,47 +322,10 @@ options extends from urllib, besides below
373
322
 
374
323
  It's not supported by using retry and writeStream, because the retry request can't stop the stream which is consuming.
375
324
 
376
- ## Proxy
377
-
378
- Support both `http` and `https` protocol.
379
-
380
- **Notice: Only support on Node.js >= 4.0.0**
381
-
382
- ### Programming
383
-
384
- ```js
385
- urllib.request('https://twitter.com/', {
386
- enableProxy: true,
387
- proxy: 'http://localhost:8008',
388
- }, (err, data, res) => {
389
- console.log(res.status, res.headers);
390
- });
391
- ```
392
-
393
- ### System environment variable
394
-
395
- - http
396
-
397
- ```bash
398
- HTTP_PROXY=http://localhost:8008
399
- http_proxy=http://localhost:8008
400
- ```
401
-
402
- - https
403
-
404
- ```bash
405
- HTTP_PROXY=http://localhost:8008
406
- http_proxy=http://localhost:8008
407
- HTTPS_PROXY=https://localhost:8008
408
- https_proxy=https://localhost:8008
409
- ```
410
-
411
- ```bash
412
- $ http_proxy=http://localhost:8008 node index.js
413
- ```
414
-
415
325
  ### Trace
326
+
416
327
  If set trace true, error stack will contains full call stack, like
328
+
417
329
  ```
418
330
  Error: connect ECONNREFUSED 127.0.0.1:11
419
331
  at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1113:14)
@@ -429,28 +341,28 @@ When open the trace, urllib may have poor perfomance, please consider carefully.
429
341
 
430
342
  ## TODO
431
343
 
432
- * [ ] Support component
433
- * [ ] Browser env use Ajax
434
- * [√] Support Proxy
435
- * [√] Upload file like form upload
436
- * [√] Auto redirect handle
437
- * [√] https & self-signed certificate
438
- * [√] Connection timeout & Response timeout
439
- * [√] Support `Accept-Encoding=gzip` by `options.gzip = true`
440
- * [√] Support [Digest access authentication](http://en.wikipedia.org/wiki/Digest_access_authentication)
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)
441
351
 
442
352
  <!-- GITCONTRIBUTOR_START -->
443
353
 
444
354
  ## Contributors
445
355
 
446
- |[<img src="https://avatars0.githubusercontent.com/u/156269?v=4" width="100px;"/><br/><sub><b>fengmk2</b></sub>](https://github.com/fengmk2)<br/>|[<img src="https://avatars3.githubusercontent.com/u/985607?v=4" width="100px;"/><br/><sub><b>dead-horse</b></sub>](https://github.com/dead-horse)<br/>|[<img src="https://avatars2.githubusercontent.com/u/288288?v=4" width="100px;"/><br/><sub><b>xingrz</b></sub>](https://github.com/xingrz)<br/>|[<img src="https://avatars1.githubusercontent.com/u/360661?v=4" width="100px;"/><br/><sub><b>popomore</b></sub>](https://github.com/popomore)<br/>|[<img src="https://avatars3.githubusercontent.com/u/327019?v=4" width="100px;"/><br/><sub><b>JacksonTian</b></sub>](https://github.com/JacksonTian)<br/>|[<img src="https://avatars3.githubusercontent.com/u/543405?v=4" width="100px;"/><br/><sub><b>ibigbug</b></sub>](https://github.com/ibigbug)<br/>|
356
+ |[<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/>|
447
357
  | :---: | :---: | :---: | :---: | :---: | :---: |
448
- |[<img src="https://avatars1.githubusercontent.com/u/14790466?v=4" width="100px;"/><br/><sub><b>greenkeeperio-bot</b></sub>](https://github.com/greenkeeperio-bot)<br/>|[<img src="https://avatars2.githubusercontent.com/u/2569835?v=4" width="100px;"/><br/><sub><b>haoxins</b></sub>](https://github.com/haoxins)<br/>|[<img src="https://avatars2.githubusercontent.com/u/227713?v=4" width="100px;"/><br/><sub><b>atian25</b></sub>](https://github.com/atian25)<br/>|[<img src="https://avatars3.githubusercontent.com/u/5381764?v=4" width="100px;"/><br/><sub><b>paambaati</b></sub>](https://github.com/paambaati)<br/>|[<img src="https://avatars1.githubusercontent.com/u/1147375?v=4" width="100px;"/><br/><sub><b>alsotang</b></sub>](https://github.com/alsotang)<br/>|[<img src="https://avatars1.githubusercontent.com/u/546535?v=4" width="100px;"/><br/><sub><b>leoner</b></sub>](https://github.com/leoner)<br/>|
449
- |[<img src="https://avatars3.githubusercontent.com/u/1747852?v=4" width="100px;"/><br/><sub><b>isayme</b></sub>](https://github.com/isayme)<br/>|[<img src="https://avatars0.githubusercontent.com/u/252317?v=4" width="100px;"/><br/><sub><b>cyjake</b></sub>](https://github.com/cyjake)<br/>|[<img src="https://avatars0.githubusercontent.com/u/5856440?v=4" width="100px;"/><br/><sub><b>whxaxes</b></sub>](https://github.com/whxaxes)<br/>|[<img src="https://avatars1.githubusercontent.com/u/5139554?v=4" width="100px;"/><br/><sub><b>danielwpz</b></sub>](https://github.com/danielwpz)<br/>|[<img src="https://avatars3.githubusercontent.com/u/5127897?v=4" width="100px;"/><br/><sub><b>danielsss</b></sub>](https://github.com/danielsss)<br/>|[<img src="https://avatars2.githubusercontent.com/u/3367820?v=4" width="100px;"/><br/><sub><b>Jeff-Tian</b></sub>](https://github.com/Jeff-Tian)<br/>|
450
- |[<img src="https://avatars1.githubusercontent.com/u/32407?v=4" width="100px;"/><br/><sub><b>jedahan</b></sub>](https://github.com/jedahan)<br/>|[<img src="https://avatars3.githubusercontent.com/u/2842176?v=4" width="100px;"/><br/><sub><b>XadillaX</b></sub>](https://github.com/XadillaX)<br/>|[<img src="https://avatars1.githubusercontent.com/u/17075261?v=4" width="100px;"/><br/><sub><b>nick-ng</b></sub>](https://github.com/nick-ng)<br/>|[<img src="https://avatars2.githubusercontent.com/u/1706595?v=4" width="100px;"/><br/><sub><b>rishavsharan</b></sub>](https://github.com/rishavsharan)<br/>|[<img src="https://avatars1.githubusercontent.com/u/1886161?v=4" width="100px;"/><br/><sub><b>willizm</b></sub>](https://github.com/willizm)<br/>|[<img src="https://avatars1.githubusercontent.com/u/7227589?v=4" width="100px;"/><br/><sub><b>davidkhala</b></sub>](https://github.com/davidkhala)<br/>|
451
- [<img src="https://avatars0.githubusercontent.com/u/535479?v=4" width="100px;"/><br/><sub><b>aleafs</b></sub>](https://github.com/aleafs)<br/>|[<img src="https://avatars1.githubusercontent.com/u/3689968?v=4" width="100px;"/><br/><sub><b>Amunu</b></sub>](https://github.com/Amunu)<br/>|[<img src="https://avatars3.githubusercontent.com/in/9426?v=4" width="100px;"/><br/><sub><b>azure-pipelines[bot]</b></sub>](https://github.com/apps/azure-pipelines)<br/>|[<img src="https://avatars3.githubusercontent.com/u/929503?v=4" width="100px;"/><br/><sub><b>yuzhigang33</b></sub>](https://github.com/yuzhigang33)<br/>|[<img src="https://avatars1.githubusercontent.com/u/981128?v=4" width="100px;"/><br/><sub><b>fishbar</b></sub>](https://github.com/fishbar)<br/>|[<img src="https://avatars2.githubusercontent.com/u/1207064?v=4" width="100px;"/><br/><sub><b>gxcsoccer</b></sub>](https://github.com/gxcsoccer)<br/>
358
+ |[<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/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/>|[<img src="https://avatars.githubusercontent.com/u/309219?v=4" width="100px;"/><br/><sub><b>chadxz</b></sub>](https://github.com/chadxz)<br/>|
360
+ |[<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/>|[<img src="https://avatars.githubusercontent.com/u/1706595?v=4" width="100px;"/><br/><sub><b>rishavsharan</b></sub>](https://github.com/rishavsharan)<br/>|
361
+ |[<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/>|[<img src="https://avatars.githubusercontent.com/u/1281323?v=4" width="100px;"/><br/><sub><b>changzhiwin</b></sub>](https://github.com/changzhiwin)<br/>|
362
+ |[<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/6897780?v=4" width="100px;"/><br/><sub><b>killagu</b></sub>](https://github.com/killagu)<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
+ [<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/>
452
364
 
453
- This project follows the git-contributor [spec](https://github.com/xudafeng/git-contributor), auto updated at `Sat Jan 16 2021 23:16:33 GMT+0800`.
365
+ This project follows the git-contributor [spec](https://github.com/xudafeng/git-contributor), auto updated at `Fri Mar 25 2022 00:05:23 GMT+0800`.
454
366
 
455
367
  <!-- GITCONTRIBUTOR_END -->
456
368
 
@@ -458,5 +370,4 @@ This project follows the git-contributor [spec](https://github.com/xudafeng/git-
458
370
 
459
371
  [MIT](LICENSE)
460
372
 
461
-
462
373
  [bluebird]: https://github.com/petkaantonov/bluebird