superagent 3.8.0-alpha.1 → 3.8.3

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/History.md CHANGED
@@ -1,3 +1,28 @@
1
+
2
+ # 3.8.3 (2018-04-29)
3
+
4
+ * Add flags for 201 & 422 responses (Nikhil Fadnis)
5
+ * Emit progress event while uploading Node `Buffer` via send method (Sergey Akhalkov)
6
+ * Fixed setting correct cookies for redirects (Damien Clark)
7
+ * Replace .catch with ['catch'] for IE9 Support (Miguel Stevens)
8
+
9
+ # 3.8.2 (2017-12-09)
10
+
11
+ * Fixed handling of exceptions thrown from callbacks
12
+ * Stricter matching of `+json` MIME types.
13
+
14
+ # 3.8.1 (2017-11-08)
15
+
16
+ * Clear authorization header on cross-domain redirect
17
+
18
+ # 3.8.0
19
+
20
+ * Added support for "globally" defined headers and event handlers via `superagent.agent()`. It now remembers default settings for all its requests.
21
+ * Added optional callback to `.retry()` (Alexander Murphy)
22
+ * Unified auth args handling in node/browser (Edmundo Alvarez)
23
+ * Fixed error handling in zlib pipes (Kornel)
24
+ * Documented that 3xx status codes are errors (Mickey Reiss)
25
+
1
26
  # 3.7.0 (2017-10-17)
2
27
 
3
28
  * Limit maximum response size. Prevents zip bombs (Kornel)
package/Readme.md CHANGED
@@ -70,6 +70,7 @@ Existing plugins:
70
70
  * [superagent-httpbackend](https://www.npmjs.com/package/superagent-httpbackend) - stub out requests using AngularJS' $httpBackend syntax
71
71
  * [superagent-throttle](https://github.com/leviwheatcroft/superagent-throttle) - queues and intelligently throttles requests
72
72
  * [superagent-charset](https://github.com/magicdawn/superagent-charset) - add charset support for node's SuperAgent
73
+ * [superagent-verbose-errors](https://github.com/jcoreio/superagent-verbose-errors) - include response body in error messages for failed requests
73
74
 
74
75
  Please prefix your plugin with `superagent-*` so that it can easily be found by others.
75
76
 
package/docs/index.md CHANGED
@@ -8,12 +8,8 @@ SuperAgent is light-weight progressive ajax API crafted for flexibility, readabi
8
8
  .send({ name: 'Manny', species: 'cat' })
9
9
  .set('X-API-Key', 'foobar')
10
10
  .set('Accept', 'application/json')
11
- .end(function(err, res){
12
- if (err || !res.ok) {
13
- alert('Oh no! error');
14
- } else {
15
- alert('yay got ' + JSON.stringify(res.body));
16
- }
11
+ .then(function(res) {
12
+ alert('yay got ' + JSON.stringify(res.body));
17
13
  });
18
14
 
19
15
  ## Test documentation
@@ -22,27 +18,32 @@ The following [test documentation](docs/test.html) was generated with [Mocha's](
22
18
 
23
19
  ## Request basics
24
20
 
25
- A request can be initiated by invoking the appropriate method on the `request` object, then calling `.end()` to send the request. For example a simple __GET__ request:
21
+ A request can be initiated by invoking the appropriate method on the `request` object, then calling `.then()` (or `.end()` [or `await`](#promise-and-generator-support)) to send the request. For example a simple __GET__ request:
26
22
 
27
23
  request
28
24
  .get('/search')
29
- .end(function(err, res){
30
-
25
+ .then(function(res) {
26
+ // res.body, res.headers, res.status
27
+ })
28
+ .catch(function(err) {
29
+ // err.message, err.response
31
30
  });
32
31
 
33
- A method string may also be passed:
32
+ HTTP method may also be passed as a string:
34
33
 
35
- request('GET', '/search').end(callback);
34
+ request('GET', '/search').then(success, failure);
36
35
 
37
- ES6 promises are supported. *Instead* of `.end()` you can call `.then()`:
36
+ Old-style callbacks are also supported. *Instead of* `.then()` you can call `.end()`:
38
37
 
39
- request('GET', '/search').then(success, failure);
38
+ request('GET', '/search').end(function(err, res){
39
+ if (res.ok) {}
40
+ });
40
41
 
41
- The __Node__ client may also provide absolute URLs. In browsers absolute URLs won't work unless the server implements [CORS](#cors).
42
+ Absolute URLs can be used. In web browsers absolute URLs work only if the server implements [CORS](#cors).
42
43
 
43
44
  request
44
45
  .get('http://example.com/search')
45
- .end(function(err, res){
46
+ .then(function(res) {
46
47
 
47
48
  });
48
49
 
@@ -52,7 +53,7 @@ The __Node__ client supports making requests to [Unix Domain Sockets](http://en.
52
53
  // Use `%2F` as `/` in SOCKET_PATH
53
54
  request
54
55
  .get('http+unix://%2Fabsolute%2Fpath%2Fto%2Funix.sock/search')
55
- .end(function(err, res){
56
+ .then(res => {
56
57
 
57
58
  });
58
59
 
@@ -60,13 +61,13 @@ __DELETE__, __HEAD__, __PATCH__, __POST__, and __PUT__ requests can also be used
60
61
 
61
62
  request
62
63
  .head('/favicon.ico')
63
- .end(function(err, res){
64
+ .then(function(res) {
64
65
 
65
66
  });
66
67
 
67
68
  __DELETE__ can be also called as `.del()` for compatibility with old IE where `delete` is a reserved word.
68
69
 
69
- The HTTP method defaults to __GET__, so if you wish, the following is valid:
70
+ The HTTP method defaults to __GET__, so if you wish, the following is valid:
70
71
 
71
72
  request('/search', function(err, res){
72
73
 
@@ -80,14 +81,14 @@ Setting header fields is simple, invoke `.set()` with a field name and value:
80
81
  .get('/search')
81
82
  .set('API-Key', 'foobar')
82
83
  .set('Accept', 'application/json')
83
- .end(callback);
84
+ .then(callback);
84
85
 
85
86
  You may also pass an object to set several fields in a single call:
86
87
 
87
88
  request
88
89
  .get('/search')
89
90
  .set({ 'API-Key': 'foobar', Accept: 'application/json' })
90
- .end(callback);
91
+ .then(callback);
91
92
 
92
93
  ## `GET` requests
93
94
 
@@ -98,7 +99,7 @@ The `.query()` method accepts objects, which when used with the __GET__ method w
98
99
  .query({ query: 'Manny' })
99
100
  .query({ range: '1..5' })
100
101
  .query({ order: 'desc' })
101
- .end(function(err, res){
102
+ .then(function(res) {
102
103
 
103
104
  });
104
105
 
@@ -107,7 +108,7 @@ Or as a single object:
107
108
  request
108
109
  .get('/search')
109
110
  .query({ query: 'Manny', range: '1..5', order: 'desc' })
110
- .end(function(err, res){
111
+ .then(function(res) {
111
112
 
112
113
  });
113
114
 
@@ -116,7 +117,7 @@ The `.query()` method accepts strings as well:
116
117
  request
117
118
  .get('/querystring')
118
119
  .query('search=Manny&range=1..5')
119
- .end(function(err, res){
120
+ .then(function(res) {
120
121
 
121
122
  });
122
123
 
@@ -126,7 +127,7 @@ Or joined:
126
127
  .get('/querystring')
127
128
  .query('search=Manny')
128
129
  .query('range=1..5')
129
- .end(function(err, res){
130
+ .then(function(res) {
130
131
 
131
132
  });
132
133
 
@@ -137,7 +138,7 @@ You can also use the `.query()` method for HEAD requests. The following will pro
137
138
  request
138
139
  .head('/users')
139
140
  .query({ email: 'joe@smith.com' })
140
- .end(function(err, res){
141
+ .then(function(res) {
141
142
 
142
143
  });
143
144
 
@@ -148,20 +149,20 @@ A typical JSON __POST__ request might look a little like the following, where we
148
149
  request.post('/user')
149
150
  .set('Content-Type', 'application/json')
150
151
  .send('{"name":"tj","pet":"tobi"}')
151
- .end(callback)
152
+ .then(callback)
152
153
 
153
154
  Since JSON is undoubtedly the most common, it's the _default_! The following example is equivalent to the previous.
154
155
 
155
156
  request.post('/user')
156
157
  .send({ name: 'tj', pet: 'tobi' })
157
- .end(callback)
158
+ .then(callback)
158
159
 
159
160
  Or using multiple `.send()` calls:
160
161
 
161
162
  request.post('/user')
162
163
  .send({ name: 'tj' })
163
164
  .send({ pet: 'tobi' })
164
- .end(callback)
165
+ .then(callback)
165
166
 
166
167
  By default sending strings will set the `Content-Type` to `application/x-www-form-urlencoded`,
167
168
  multiple calls will be concatenated with `&`, here resulting in `name=tj&pet=tobi`:
@@ -169,7 +170,7 @@ By default sending strings will set the `Content-Type` to `application/x-www-for
169
170
  request.post('/user')
170
171
  .send('name=tj')
171
172
  .send('pet=tobi')
172
- .end(callback);
173
+ .then(callback);
173
174
 
174
175
  SuperAgent formats are extensible, however by default "json" and "form" are supported. To send the data as `application/x-www-form-urlencoded` simply invoke `.type()` with "form", where the default is "json". This request will __POST__ the body "name=tj&pet=tobi".
175
176
 
@@ -177,13 +178,13 @@ SuperAgent formats are extensible, however by default "json" and "form" are supp
177
178
  .type('form')
178
179
  .send({ name: 'tj' })
179
180
  .send({ pet: 'tobi' })
180
- .end(callback)
181
+ .then(callback)
181
182
 
182
183
  Sending a [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData/FormData) object is also supported. The following example will __POST__ the content of the HTML form identified by id="myForm":
183
184
 
184
185
  request.post('/user')
185
186
  .send(new FormData(document.getElementById('myForm')))
186
- .end(callback)
187
+ .then(callback)
187
188
 
188
189
  ## Setting the `Content-Type`
189
190
 
@@ -207,16 +208,41 @@ simply the extension name such as "xml", "json", "png", etc:
207
208
 
208
209
  ## Serializing request body
209
210
 
210
- SuperAgent will automatically serialize JSON and forms. If you want to send the payload in a custom format, you can replace the built-in serialization with `.serialize()` method.
211
+ SuperAgent will automatically serialize JSON and forms.
212
+ You can setup automatic serialization for other types as well:
213
+
214
+ ```js
215
+ request.serialize['application/xml'] = function (obj) {
216
+ return 'string generated from obj';
217
+ };
218
+
219
+ //going forward, all requests with a Content-type of
220
+ //'application/xml' will be automatically serialized
221
+ ```
222
+ If you want to send the payload in a custom format, you can replace
223
+ the built-in serialization with the `.serialize()` method on a per-request basis:
211
224
 
225
+ ```js
226
+ request
227
+ .post('/user')
228
+ .send({foo: 'bar'})
229
+ .serialize(function serializer(obj) {
230
+ return 'string generated from obj';
231
+ });
232
+ ```
212
233
  ## Retrying requests
213
234
 
214
- When given the `.retry()` method, SuperAgent will automatically retry requests, if they fail in a way that is transient or could be due to a flaky Internet connection. `.retry()` takes an optional argument which is the maximum number of times to retry failed requests; the default is 3 times.
235
+ When given the `.retry()` method, SuperAgent will automatically retry requests, if they fail in a way that is transient or could be due to a flaky Internet connection.
236
+
237
+ This method has two optional arguments: number of retries (default 3) and a callback. It calls `callback(err, res)` before each retry. The callback may return `true`/`false` to control whether the request sould be retried (but the maximum number of retries is always applied).
215
238
 
216
239
  request
217
240
  .get('http://example.com/search')
218
- .retry(2)
219
- .end(callback);
241
+ .retry(2) // or:
242
+ .retry(2, callback)
243
+ .then(finished);
244
+
245
+ Use `.retry()` only with requests that are *idempotent* (i.e. multiple requests reaching the server won't cause undesirable side effects like duplicate purchases).
220
246
 
221
247
  ## Setting Accept
222
248
 
@@ -244,7 +270,7 @@ If you are calling Facebook's API, be sure to send an `Accept: application/json`
244
270
  .query({ format: 'json' })
245
271
  .query({ dest: '/login' })
246
272
  .send({ post: 'data', here: 'wahoo' })
247
- .end(callback);
273
+ .then(callback);
248
274
 
249
275
  By default the query string is not assembled in any particular order. An asciibetically-sorted query string can be enabled with `req.sortQuery()`. You may also provide a custom sorting comparison function with `req.sortQuery(myComparisonFn)`. The comparison function should take 2 arguments and return a negative/zero/positive integer.
250
276
 
@@ -254,7 +280,7 @@ By default the query string is not assembled in any particular order. An asciibe
254
280
  .query('name=Nick')
255
281
  .query('search=Manny')
256
282
  .sortQuery()
257
- .end(callback)
283
+ .then(callback)
258
284
 
259
285
  // customized sort function
260
286
  request.get('/user')
@@ -263,7 +289,7 @@ By default the query string is not assembled in any particular order. An asciibe
263
289
  .sortQuery(function(a, b){
264
290
  return a.length - b.length;
265
291
  })
266
- .end(callback)
292
+ .then(callback)
267
293
  ```
268
294
 
269
295
  ## TLS options
@@ -285,7 +311,7 @@ request
285
311
  .post('/client-auth')
286
312
  .key(key)
287
313
  .cert(cert)
288
- .end(callback);
314
+ .then(callback);
289
315
  ```
290
316
 
291
317
  ```js
@@ -294,12 +320,32 @@ var ca = fs.readFileSync('ca.cert.pem');
294
320
  request
295
321
  .post('https://localhost/private-ca-server')
296
322
  .ca(ca)
297
- .end(callback);
323
+ .then(res => {});
298
324
  ```
299
325
 
300
326
  ## Parsing response bodies
301
327
 
302
- SuperAgent will parse known response-body data for you, currently supporting `application/x-www-form-urlencoded`, `application/json`, and `multipart/form-data`.
328
+ SuperAgent will parse known response-body data for you,
329
+ currently supporting `application/x-www-form-urlencoded`,
330
+ `application/json`, and `multipart/form-data`. You can setup
331
+ automatic parsing for other response-body data as well:
332
+
333
+ ```js
334
+ //browser
335
+ request.parse['application/xml'] = function (str) {
336
+ return {'object': 'parsed from str'};
337
+ };
338
+
339
+ //node
340
+ request.parse['application/xml'] = function (res, cb) {
341
+ //parse response text and set res.body here
342
+
343
+ cb(null, res);
344
+ };
345
+
346
+ //going forward, responses of type 'application/xml'
347
+ //will be parsed automatically
348
+ ```
303
349
 
304
350
  You can set a custom parser (that takes precedence over built-in parsers) with the `.buffer(true).parse(fn)` method. If response buffering is not enabled (`.buffer(false)`) then the `response` event will be emitted without waiting for the body parser to finish, so `response.body` won't be available.
305
351
 
@@ -410,8 +456,10 @@ You should use both `deadline` and `response` timeouts. This way you can use a s
410
456
  response: 5000, // Wait 5 seconds for the server to start sending,
411
457
  deadline: 60000, // but allow 1 minute for the file to finish loading.
412
458
  })
413
- .end(function(err, res){
414
- if (err.timeout) { /* timed out! */ }
459
+ .then(res => {
460
+ /* responded in time */
461
+ }, err => {
462
+ if (err.timeout) { /* timed out! */ } else { /* other error */ }
415
463
  });
416
464
 
417
465
  Timeout errors have a `.timeout` property.
@@ -423,12 +471,12 @@ In both Node and browsers auth available via the `.auth()` method:
423
471
  request
424
472
  .get('http://local')
425
473
  .auth('tobi', 'learnboost')
426
- .end(callback);
474
+ .then(callback);
427
475
 
428
476
 
429
477
  In the _Node_ client Basic auth can be in the URL as "user:pass":
430
478
 
431
- request.get('http://tobi:learnboost@local').end(callback);
479
+ request.get('http://tobi:learnboost@local').then(callback);
432
480
 
433
481
  By default only `Basic` auth is used. In browser you can add `{type:'auto'}` to enable all methods built-in in the browser (Digest, NTLM, etc.):
434
482
 
@@ -441,7 +489,7 @@ By default up to 5 redirects will be followed, however you may specify this with
441
489
  request
442
490
  .get('/some.png')
443
491
  .redirects(2)
444
- .end(callback);
492
+ .then(callback);
445
493
 
446
494
  ## Agents for global state
447
495
 
@@ -460,7 +508,7 @@ In browsers cookies are managed automatically by the browser, so the `.agent()`
460
508
 
461
509
  ### Default options for multiple requests
462
510
 
463
- Regular request methods (`.use()`, `.set()`, `.auth()`) called on the agent will be used as defaults for all requests made by that agent.
511
+ Regular request methods called on the agent will be used as defaults for all requests made by that agent.
464
512
 
465
513
  const agent = request.agent()
466
514
  .use(plugin)
@@ -469,9 +517,13 @@ Regular request methods (`.use()`, `.set()`, `.auth()`) called on the agent will
469
517
  await agent.get('/with-plugin-and-auth');
470
518
  await agent.get('/also-with-plugin-and-auth');
471
519
 
520
+ The complete list of methods that the agent can use to set defaults is: `use`, `on`, `once`, `set`, `query`, `type`, `accept`, `auth`, `withCredentials`, `sortQuery`, `retry`, `ok`, `redirects`, `timeout`, `buffer`, `serialize`, `parse`, `ca`, `key`, `pfx`, `cert`.
521
+
472
522
  ## Piping data
473
523
 
474
- The Node client allows you to pipe data to and from the request. For example piping a file's contents as the request:
524
+ The Node client allows you to pipe data to and from the request. Please note that `.pipe()` is used **instead of** `.end()`/`.then()` methods.
525
+
526
+ For example piping a file's contents as the request:
475
527
 
476
528
  const request = require('superagent');
477
529
  const fs = require('fs');
@@ -489,6 +541,22 @@ Or piping the response to a file:
489
541
  const req = request.get('/some.json');
490
542
  req.pipe(stream);
491
543
 
544
+ It's not possible to mix pipes and callbacks or promises. Note that you should **NOT** attempt to pipe the result of `.end()` or the `Response` object:
545
+
546
+ // Don't do either of these:
547
+ const stream = getAWritableStream();
548
+ const req = request
549
+ .get('/some.json')
550
+ // BAD: this pipes garbage to the stream and fails in unexpected ways
551
+ .end((err, this_does_not_work) => this_does_not_work.pipe(stream))
552
+ const req = request
553
+ .get('/some.json')
554
+ .end()
555
+ // BAD: this is also unsupported, .pipe calls .end for you.
556
+ .pipe(nope_its_too_late);
557
+
558
+ In a [future version](https://github.com/visionmedia/superagent/issues/1188) of superagent, improper calls to `pipe()` will fail.
559
+
492
560
  ## Multipart requests
493
561
 
494
562
  SuperAgent is also great for _building_ multipart requests for which it provides methods `.attach()` and `.field()`.
@@ -499,7 +567,7 @@ When you use `.field()` or `.attach()` you can't use `.send()` and you *must not
499
567
 
500
568
  To send a file use `.attach(name, [file], [options])`. You can attach multiple files by calling `.attach` multiple times. The arguments are:
501
569
 
502
- * `name` — filed name in the form.
570
+ * `name` — field name in the form.
503
571
  * `file` — either string with file path or `Blob`/`Buffer` object.
504
572
  * `options` — (optional) either string with custom file name or `{filename: string}` object. In Node also `{contentType: 'mime/type'}` is supported. In browser create a `Blob` with an appropriate type instead.
505
573
 
@@ -510,7 +578,7 @@ To send a file use `.attach(name, [file], [options])`. You can attach multiple f
510
578
  .attach('image1', 'path/to/felix.jpeg')
511
579
  .attach('image2', imageBuffer, 'luna.jpeg')
512
580
  .field('caption', 'My cats')
513
- .end(callback);
581
+ .then(callback);
514
582
 
515
583
  ### Field values
516
584
 
@@ -522,7 +590,7 @@ Much like form fields in HTML, you can set field values with `.field(name, value
522
590
  .field('user[email]', 'tobi@learnboost.com')
523
591
  .field('friends[]', ['loki', 'jane'])
524
592
  .attach('image', 'path/to/tobi.png')
525
- .end(callback);
593
+ .then(callback);
526
594
 
527
595
  ## Compression
528
596
 
@@ -543,7 +611,7 @@ The `.withCredentials()` method enables the ability to send cookies from the ori
543
611
  request
544
612
  .get('http://api.example.com:4001/')
545
613
  .withCredentials()
546
- .then(function(res){
614
+ .then(function(res) {
547
615
  assert.equal(200, res.status);
548
616
  assert.equal('tobi', res.text);
549
617
  })
@@ -555,7 +623,7 @@ Your callback function will always be passed two arguments: error and response.
555
623
  request
556
624
  .post('/upload')
557
625
  .attach('image', 'path/to/tobi.png')
558
- .end(function(err, res){
626
+ .then(function(res) {
559
627
 
560
628
  });
561
629
 
@@ -565,7 +633,7 @@ An "error" event is also emitted, with you can listen for:
565
633
  .post('/upload')
566
634
  .attach('image', 'path/to/tobi.png')
567
635
  .on('error', handle)
568
- .end(function(err, res){
636
+ .then(function(res) {
569
637
 
570
638
  });
571
639
 
@@ -609,7 +677,9 @@ SuperAgent fires `progress` events on upload and download of large files.
609
677
 
610
678
  ## Promise and Generator support
611
679
 
612
- SuperAgent's request is a "thenable" object that's compatible with JavaScript promises and `async`/`await` syntax. Do not call `.end()` if you're using promises.
680
+ SuperAgent's request is a "thenable" object that's compatible with JavaScript promises and `async`/`await` syntax.
681
+
682
+ If you're using promises, **do not** call `.end()` or `.pipe()`. Any use of `.then()` or `await` disables all other ways of using the request.
613
683
 
614
684
  Libraries like [co](https://github.com/tj/co) or a web framework like [koa](https://github.com/koajs/koa) can `yield` on any SuperAgent method:
615
685
 
package/lib/client.js CHANGED
@@ -17,7 +17,6 @@ var RequestBase = require('./request-base');
17
17
  var isObject = require('./is-object');
18
18
  var ResponseBase = require('./response-base');
19
19
  var Agent = require('./agent-base');
20
- var shouldRetry = require('./should-retry');
21
20
 
22
21
  /**
23
22
  * Noop.
@@ -187,7 +186,7 @@ request.types = {
187
186
 
188
187
  request.serialize = {
189
188
  'application/x-www-form-urlencoded': serialize,
190
- 'application/json': JSON.stringify,
189
+ 'application/json': JSON.stringify
191
190
  };
192
191
 
193
192
  /**
@@ -201,7 +200,7 @@ request.serialize = {
201
200
 
202
201
  request.parse = {
203
202
  'application/x-www-form-urlencoded': parseString,
204
- 'application/json': JSON.parse,
203
+ 'application/json': JSON.parse
205
204
  };
206
205
 
207
206
  /**
@@ -244,7 +243,9 @@ function parseHeader(str) {
244
243
  */
245
244
 
246
245
  function isJSON(mime) {
247
- return /[\/+]json\b/.test(mime);
246
+ // should match /json or +json
247
+ // but not /json-seq
248
+ return /[\/+]json($|[^-\w])/.test(mime);
248
249
  }
249
250
 
250
251
  /**
@@ -595,8 +596,7 @@ Request.prototype._getFormData = function(){
595
596
  */
596
597
 
597
598
  Request.prototype.callback = function(err, res){
598
- // console.log(this._retries, this._maxRetries)
599
- if (this._maxRetries && this._retries++ < this._maxRetries && shouldRetry(err, res)) {
599
+ if (this._shouldRetry(err, res)) {
600
600
  return this._retry();
601
601
  }
602
602