superagent 3.6.0 → 3.7.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/.travis.yml CHANGED
@@ -1,9 +1,9 @@
1
1
  sudo: false
2
2
  language: node_js
3
3
  node_js:
4
- - "7.1"
5
- - "6.9"
6
- - "4.6"
4
+ - "8"
5
+ - "6"
6
+ - "4"
7
7
 
8
8
  env:
9
9
  global:
@@ -12,5 +12,5 @@ env:
12
12
 
13
13
  matrix:
14
14
  include:
15
- - node_js: "4.6"
15
+ - node_js: "8"
16
16
  env: BROWSER=1
package/History.md CHANGED
@@ -1,3 +1,15 @@
1
+ # 3.7.0 (2017-10-17)
2
+
3
+ * Limit maximum response size. Prevents zip bombs (Kornel)
4
+ * Catch and pass along errors in `.ok()` callback (Jeremy Ruppel)
5
+ * Fixed parsing of XHR headers without a newline (nsf)
6
+
7
+ # 3.6.2 (2017-10-02)
8
+
9
+ * Upgrade MIME type dependency to a newer, secure version
10
+ * Recognize PDF MIME as binary
11
+ * Fix for error in subsequent require() calls (Steven de Salas)
12
+
1
13
  # 3.6.0 (2017-08-20)
2
14
 
3
15
  * Support disabling TCP_NODELAY option (#1240) (xiamengyu)
package/Makefile CHANGED
@@ -20,7 +20,7 @@ test-cov: lib-cov
20
20
  SUPERAGENT_COV=1 $(MAKE) test REPORTER=html-cov > coverage.html
21
21
 
22
22
  test-browser:
23
- SAUCE_APPIUM_VERSION=1.6 ./node_modules/.bin/zuul -- $(BROWSERTESTS)
23
+ SAUCE_APPIUM_VERSION=1.7 ./node_modules/.bin/zuul -- $(BROWSERTESTS)
24
24
 
25
25
  test-browser-local:
26
26
  ./node_modules/.bin/zuul --no-coverage --local 4000 -- $(BROWSERTESTS)
package/Readme.md CHANGED
@@ -14,15 +14,15 @@ node:
14
14
  $ npm install superagent
15
15
  ```
16
16
 
17
- Works with [browserify](https://github.com/substack/node-browserify) and should work with [webpack](https://github.com/visionmedia/superagent/wiki/SuperAgent-for-Webpack)
17
+ Works with [browserify](https://github.com/substack/node-browserify) and [webpack](https://github.com/visionmedia/superagent/wiki/SuperAgent-for-Webpack).
18
18
 
19
19
  ```js
20
20
  request
21
21
  .post('/api/pet')
22
22
  .send({ name: 'Manny', species: 'cat' }) // sends a JSON post body
23
23
  .set('X-API-Key', 'foobar')
24
- .set('Accept', 'application/json')
25
- .end(function(err, res){
24
+ .set('accept', 'json')
25
+ .end((err, res) => {
26
26
  // Calling the end function will send the request
27
27
  });
28
28
  ```
@@ -33,27 +33,25 @@ Tested browsers:
33
33
 
34
34
  - Latest Firefox, Chrome, Safari
35
35
  - Latest Android, iPhone
36
- - IE10 through latest. IE9 with polyfills.
37
-
38
- Even though IE9 is supported, a polyfill for `window.FormData` is required for `.field()`.
36
+ - IE10 through latest. IE9 with polyfills. Even though IE9 is supported, a polyfill for `window.FormData` is required for `.field()`.
39
37
 
40
38
  Node 4 or later is required.
41
39
 
42
- # Plugins
40
+ ## Plugins
43
41
 
44
42
  SuperAgent is easily extended via plugins.
45
43
 
46
44
  ```js
47
- var nocache = require('superagent-no-cache');
48
- var request = require('superagent');
49
- var prefix = require('superagent-prefix')('/static');
45
+ const nocache = require('superagent-no-cache');
46
+ const request = require('superagent');
47
+ const prefix = require('superagent-prefix')('/static');
50
48
 
51
49
  request
52
50
  .get('/some-url')
53
51
  .query({ action: 'edit', city: 'London' }) // query string
54
52
  .use(prefix) // Prefixes *only* this request
55
53
  .use(nocache) // Prevents caching of *only* this request
56
- .end(function(err, res){
54
+ .end((err, res) => {
57
55
  // Do something
58
56
  });
59
57
  ```
@@ -77,6 +75,20 @@ Please prefix your plugin with `superagent-*` so that it can easily be found by
77
75
 
78
76
  For SuperAgent extensions such as couchdb and oauth visit the [wiki](https://github.com/visionmedia/superagent/wiki).
79
77
 
78
+ ## Upgrading from previous versions:
79
+
80
+ Our breaking changes are mostly in rarely used functionality and from stricter error handling.
81
+
82
+ * [2.x to 3.x](https://github.com/visionmedia/superagent/releases/tag/v3.0.0):
83
+ - Ensure you're running Node 4 or later. We dropped support for Node 0.x.
84
+ - Test code that calls `.send()` multiple times. Invalid calls to `.send()` will now throw instead of sending garbage.
85
+ * [1.x to 2.x](https://github.com/visionmedia/superagent/releases/tag/v2.0.0):
86
+ - If you use `.parse()` in the *browser* version, rename it to `.serialize()`.
87
+ - If you rely on `undefined` in query-string values being sent literally as the text "undefined", switch to checking for missing value instead. `?key=undefined` is now `?key` (without a value).
88
+ - If you use `.then()` in Internet Explorer, ensure that you have a polyfill that adds a global `Promise` object.
89
+ * 0.x to 1.x:
90
+ - Use `.end(function(err, res){})`. 1-argument version is no longer supported.
91
+
80
92
  ## Running node tests
81
93
 
82
94
  Install dependencies:
package/docs/index.md CHANGED
@@ -178,7 +178,7 @@ SuperAgent formats are extensible, however by default "json" and "form" are supp
178
178
  .send({ name: 'tj' })
179
179
  .send({ pet: 'tobi' })
180
180
  .end(callback)
181
-
181
+
182
182
  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
183
 
184
184
  request.post('/user')
@@ -467,6 +467,8 @@ The Node client allows you to pipe data to and from the request. For example pip
467
467
  const req = request.post('/somewhere');
468
468
  req.type('json');
469
469
  stream.pipe(req);
470
+
471
+ Note that when you pipe to a request, superagent sends the piped data with [chunked transfer encoding](https://en.wikipedia.org/wiki/Chunked_transfer_encoding), which isn't supported by all servers (for instance, Python WSGI servers).
470
472
 
471
473
  Or piping the response to a file:
472
474
 
@@ -478,20 +480,28 @@ Or piping the response to a file:
478
480
 
479
481
  SuperAgent is also great for _building_ multipart requests for which it provides methods `.attach()` and `.field()`.
480
482
 
483
+ When you use `.field()` or `.attach()` you can't use `.send()` and you *must not* set `Content-Type` (the correct type will be set for you).
484
+
481
485
  ### Attaching files
482
486
 
483
- As mentioned a higher-level API is also provided, in the form of `.attach(name, [path], [filename])` and `.field(name, value)`/`.field(object)`. Attaching several files is simple, you can also provide a custom filename for the attachment, otherwise the basename of the attached file is used.
487
+ To send a file use `.attach(name, [file], [options])`. You can attach multiple files by calling `.attach` multiple times. The arguments are:
488
+
489
+ * `name` — filed name in the form.
490
+ * `file` — either string with file path or `Blob`/`Buffer` object.
491
+ * `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.
492
+
493
+ <br>
484
494
 
485
495
  request
486
496
  .post('/upload')
487
- .attach('avatar', 'path/to/tobi.png', 'user.png')
488
- .attach('image', 'path/to/loki.png')
489
- .attach('file', 'path/to/jane.png')
497
+ .attach('image1', 'path/to/felix.jpeg')
498
+ .attach('image2', imageBuffer, 'luna.jpeg')
499
+ .field('caption', 'My cats')
490
500
  .end(callback);
491
501
 
492
502
  ### Field values
493
503
 
494
- Much like form fields in HTML, you can set field values with the `.field(name, value)` method. Suppose you want to upload a few images with your name and email, your request might look something like this:
504
+ Much like form fields in HTML, you can set field values with `.field(name, value)` and `.field({name: value})`. Suppose you want to upload a few images with your name and email, your request might look something like this:
495
505
 
496
506
  request
497
507
  .post('/upload')
package/lib/client.js CHANGED
@@ -220,11 +220,12 @@ function parseHeader(str) {
220
220
  var field;
221
221
  var val;
222
222
 
223
- lines.pop(); // trailing CRLF
224
-
225
223
  for (var i = 0, len = lines.length; i < len; ++i) {
226
224
  line = lines[i];
227
225
  index = line.indexOf(':');
226
+ if (index === -1) { // could be empty line, just skip it
227
+ continue;
228
+ }
228
229
  field = line.slice(0, index).toLowerCase();
229
230
  val = trim(line.slice(index + 1));
230
231
  fields[field] = val;
@@ -420,16 +421,16 @@ function Request(method, url) {
420
421
  try {
421
422
  if (!self._isResponseOK(res)) {
422
423
  new_err = new Error(res.statusText || 'Unsuccessful HTTP response');
423
- new_err.original = err;
424
- new_err.response = res;
425
- new_err.status = res.status;
426
424
  }
427
- } catch(e) {
428
- new_err = e; // #985 touching res may cause INVALID_STATE_ERR on old Android
425
+ } catch(custom_err) {
426
+ new_err = custom_err; // ok() callback can throw
429
427
  }
430
428
 
431
429
  // #1000 don't catch errors from the callback to avoid double calling it
432
430
  if (new_err) {
431
+ new_err.original = err;
432
+ new_err.response = res;
433
+ new_err.status = res.status;
433
434
  self.callback(new_err, res);
434
435
  } else {
435
436
  self.callback(null, res);
package/lib/is-object.js CHANGED
@@ -1,3 +1,5 @@
1
+ 'use strict';
2
+
1
3
  /**
2
4
  * Check if `obj` is an object.
3
5
  *
package/lib/node/agent.js CHANGED
@@ -1,3 +1,4 @@
1
+ 'use strict';
1
2
 
2
3
  /**
3
4
  * Module dependencies.
package/lib/node/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ 'use strict';
1
2
 
2
3
  /**
3
4
  * Module dependencies.
@@ -70,7 +71,7 @@ exports.Response = Response;
70
71
 
71
72
  mime.define({
72
73
  'application/x-www-form-urlencoded': ['form', 'urlencoded', 'form-data']
73
- });
74
+ }, true);
74
75
 
75
76
  /**
76
77
  * Protocol map.
@@ -675,16 +676,20 @@ Request.prototype.callback = function(err, res){
675
676
  this.called = true;
676
677
 
677
678
  if (!err) {
678
- if (this._isResponseOK(res)) {
679
- return fn(err, res);
680
- }
679
+ try {
680
+ if (this._isResponseOK(res)) {
681
+ return fn(err, res);
682
+ }
681
683
 
682
- var msg = 'Unsuccessful HTTP response';
683
- if (res) {
684
- msg = http.STATUS_CODES[res.status] || msg;
684
+ var msg = 'Unsuccessful HTTP response';
685
+ if (res) {
686
+ msg = http.STATUS_CODES[res.status] || msg;
687
+ }
688
+ err = new Error(msg);
689
+ err.status = res ? res.status : undefined;
690
+ } catch (new_err) {
691
+ err = new_err;
685
692
  }
686
- err = new Error(msg);
687
- err.status = res ? res.status : undefined;
688
693
  }
689
694
 
690
695
  err.response = res;
@@ -845,6 +850,24 @@ Request.prototype._end = function() {
845
850
  }
846
851
 
847
852
  var parserHandlesEnd = false;
853
+ if (buffer) {
854
+ // Protectiona against zip bombs and other nuisance
855
+ let responseBytesLeft = self._maxResponseSize || 200000000;
856
+ res.on('data', function(buf) {
857
+ responseBytesLeft -= buf.byteLength || buf.length;
858
+ if (responseBytesLeft < 0) {
859
+ // This will propagate through error event
860
+ const err = Error("Maximum response size reached");
861
+ err.code = "ETOOLARGE";
862
+ // Parsers aren't required to observe error event,
863
+ // so would incorrectly report success
864
+ parserHandlesEnd = false;
865
+ // Will emit error event
866
+ res.destroy(err);
867
+ }
868
+ });
869
+ }
870
+
848
871
  if (parser) {
849
872
  try {
850
873
  // Unbuffered parsers are supposed to emit response early,
@@ -890,6 +913,7 @@ Request.prototype._end = function() {
890
913
 
891
914
  // terminating events
892
915
  res.once('error', function(err){
916
+ parserHandlesEnd = false;
893
917
  self.callback(err, null);
894
918
  });
895
919
  if (!parserHandlesEnd) res.once('end', function(){
@@ -1,3 +1,5 @@
1
+ 'use strict';
2
+
1
3
  module.exports = function(res, fn){
2
4
  var data = []; // Binary data needs binary storage
3
5
 
@@ -7,4 +9,4 @@ module.exports = function(res, fn){
7
9
  res.on('end', function () {
8
10
  fn(null, Buffer.concat(data));
9
11
  });
10
- };
12
+ };
@@ -1,3 +1,4 @@
1
+ 'use strict';
1
2
 
2
3
  exports['application/x-www-form-urlencoded'] = require('./urlencoded');
3
4
  exports['application/json'] = require('./json');
@@ -5,4 +6,5 @@ exports.text = require('./text');
5
6
 
6
7
  var binary = require('./image');
7
8
  exports['application/octet-stream'] = binary;
9
+ exports['application/pdf'] = binary;
8
10
  exports.image = binary;
@@ -1,3 +1,4 @@
1
+ 'use strict';
1
2
 
2
3
  module.exports = function parseJSON(res, fn){
3
4
  res.text = '';
@@ -1,7 +1,8 @@
1
+ 'use strict';
1
2
 
2
3
  module.exports = function(res, fn){
3
4
  res.text = '';
4
5
  res.setEncoding('utf8');
5
6
  res.on('data', function(chunk){ res.text += chunk; });
6
7
  res.on('end', fn);
7
- };
8
+ };
@@ -1,3 +1,4 @@
1
+ 'use strict';
1
2
 
2
3
  /**
3
4
  * Module dependencies.
@@ -16,4 +17,4 @@ module.exports = function(res, fn){
16
17
  fn(err);
17
18
  }
18
19
  });
19
- };
20
+ };
@@ -1,3 +1,4 @@
1
+ 'use strict';
1
2
 
2
3
  /**
3
4
  * Module dependencies.
package/lib/node/unzip.js CHANGED
@@ -1,3 +1,4 @@
1
+ 'use strict';
1
2
 
2
3
  /**
3
4
  * Module dependencies.
@@ -66,4 +67,4 @@ exports.unzip = function(req, res){
66
67
  }
67
68
  return this;
68
69
  };
69
- };
70
+ };
@@ -1,3 +1,5 @@
1
+ 'use strict';
2
+
1
3
  /**
2
4
  * Module of mixed-in functions shared between node and client code
3
5
  */
@@ -413,6 +415,21 @@ RequestBase.prototype.redirects = function(n){
413
415
  return this;
414
416
  };
415
417
 
418
+ /**
419
+ * Maximum size of buffered response body, in bytes. Counts uncompressed size.
420
+ * Default 200MB.
421
+ *
422
+ * @param {Number} n
423
+ * @return {Request} for chaining
424
+ */
425
+ RequestBase.prototype.maxResponseSize = function(n){
426
+ if ('number' !== typeof n) {
427
+ throw TypeError("Invalid argument");
428
+ }
429
+ this._maxResponseSize = n;
430
+ return this;
431
+ };
432
+
416
433
  /**
417
434
  * Convert to a plain javascript object (not JSON string) of scalar properties.
418
435
  * Note as this method is designed to return a useful non-this value,
@@ -1,3 +1,4 @@
1
+ 'use strict';
1
2
 
2
3
  /**
3
4
  * Module dependencies.
@@ -1,3 +1,5 @@
1
+ 'use strict';
2
+
1
3
  var ERROR_CODES = [
2
4
  'ECONNRESET',
3
5
  'ETIMEDOUT',
package/lib/utils.js CHANGED
@@ -1,3 +1,4 @@
1
+ 'use strict';
1
2
 
2
3
  /**
3
4
  * Return the mime type for the given `str`.
@@ -65,4 +66,4 @@ exports.cleanHeader = function(header, shouldStripCookie){
65
66
  delete header['cookie'];
66
67
  }
67
68
  return header;
68
- };
69
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "superagent",
3
- "version": "3.6.0",
3
+ "version": "3.7.0",
4
4
  "description": "elegant & feature rich browser / node HTTP with a fluent API",
5
5
  "scripts": {
6
6
  "prepublish": "make all",
@@ -26,25 +26,25 @@
26
26
  "dependencies": {
27
27
  "component-emitter": "^1.2.0",
28
28
  "cookiejar": "^2.1.0",
29
- "debug": "^2.6.0",
29
+ "debug": "^3.1.0",
30
30
  "extend": "^3.0.0",
31
- "form-data": "^2.1.1",
31
+ "form-data": "^2.3.1",
32
32
  "formidable": "^1.1.1",
33
33
  "methods": "^1.1.1",
34
- "mime": "^1.3.6",
35
- "qs": "^6.4.0",
34
+ "mime": "^1.4.1",
35
+ "qs": "^6.5.1",
36
36
  "readable-stream": "^2.0.5"
37
37
  },
38
38
  "devDependencies": {
39
39
  "Base64": "^1.0.1",
40
40
  "basic-auth-connect": "^1.0.0",
41
- "body-parser": "^1.17.0",
41
+ "body-parser": "^1.18.2",
42
42
  "browserify": "^14.1.0",
43
43
  "cookie-parser": "^1.4.3",
44
- "express": "^4.15.2",
45
- "express-session": "^1.15.0",
44
+ "express": "^4.16.0",
45
+ "express-session": "^1.15.6",
46
46
  "marked": "^0.3.6",
47
- "mocha": "^3.1.2",
47
+ "mocha": "^3.5.3",
48
48
  "multer": "^1.3.0",
49
49
  "should": "^11.2.0",
50
50
  "should-http": "^0.1.1",
package/superagent.js CHANGED
@@ -1,4 +1,6 @@
1
1
  (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.superagent = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
2
+ 'use strict';
3
+
2
4
  /**
3
5
  * Check if `obj` is an object.
4
6
  *
@@ -14,6 +16,8 @@ function isObject(obj) {
14
16
  module.exports = isObject;
15
17
 
16
18
  },{}],2:[function(require,module,exports){
19
+ 'use strict';
20
+
17
21
  /**
18
22
  * Module of mixed-in functions shared between node and client code
19
23
  */
@@ -429,6 +433,21 @@ RequestBase.prototype.redirects = function(n){
429
433
  return this;
430
434
  };
431
435
 
436
+ /**
437
+ * Maximum size of buffered response body, in bytes. Counts uncompressed size.
438
+ * Default 200MB.
439
+ *
440
+ * @param {Number} n
441
+ * @return {Request} for chaining
442
+ */
443
+ RequestBase.prototype.maxResponseSize = function(n){
444
+ if ('number' !== typeof n) {
445
+ throw TypeError("Invalid argument");
446
+ }
447
+ this._maxResponseSize = n;
448
+ return this;
449
+ };
450
+
432
451
  /**
433
452
  * Convert to a plain javascript object (not JSON string) of scalar properties.
434
453
  * Note as this method is designed to return a useful non-this value,
@@ -636,6 +655,7 @@ RequestBase.prototype._setTimeouts = function() {
636
655
  }
637
656
 
638
657
  },{"./is-object":1}],3:[function(require,module,exports){
658
+ 'use strict';
639
659
 
640
660
  /**
641
661
  * Module dependencies.
@@ -771,6 +791,8 @@ ResponseBase.prototype._setStatusProperties = function(status){
771
791
  };
772
792
 
773
793
  },{"./utils":5}],4:[function(require,module,exports){
794
+ 'use strict';
795
+
774
796
  var ERROR_CODES = [
775
797
  'ECONNRESET',
776
798
  'ETIMEDOUT',
@@ -796,6 +818,7 @@ module.exports = function shouldRetry(err, res) {
796
818
  };
797
819
 
798
820
  },{}],5:[function(require,module,exports){
821
+ 'use strict';
799
822
 
800
823
  /**
801
824
  * Return the mime type for the given `str`.
@@ -864,6 +887,7 @@ exports.cleanHeader = function(header, shouldStripCookie){
864
887
  }
865
888
  return header;
866
889
  };
890
+
867
891
  },{}],6:[function(require,module,exports){
868
892
 
869
893
  /**
@@ -1252,11 +1276,12 @@ function parseHeader(str) {
1252
1276
  var field;
1253
1277
  var val;
1254
1278
 
1255
- lines.pop(); // trailing CRLF
1256
-
1257
1279
  for (var i = 0, len = lines.length; i < len; ++i) {
1258
1280
  line = lines[i];
1259
1281
  index = line.indexOf(':');
1282
+ if (index === -1) { // could be empty line, just skip it
1283
+ continue;
1284
+ }
1260
1285
  field = line.slice(0, index).toLowerCase();
1261
1286
  val = trim(line.slice(index + 1));
1262
1287
  fields[field] = val;
@@ -1452,16 +1477,16 @@ function Request(method, url) {
1452
1477
  try {
1453
1478
  if (!self._isResponseOK(res)) {
1454
1479
  new_err = new Error(res.statusText || 'Unsuccessful HTTP response');
1455
- new_err.original = err;
1456
- new_err.response = res;
1457
- new_err.status = res.status;
1458
1480
  }
1459
- } catch(e) {
1460
- new_err = e; // #985 touching res may cause INVALID_STATE_ERR on old Android
1481
+ } catch(custom_err) {
1482
+ new_err = custom_err; // ok() callback can throw
1461
1483
  }
1462
1484
 
1463
1485
  // #1000 don't catch errors from the callback to avoid double calling it
1464
1486
  if (new_err) {
1487
+ new_err.original = err;
1488
+ new_err.response = res;
1489
+ new_err.status = res.status;
1465
1490
  self.callback(new_err, res);
1466
1491
  } else {
1467
1492
  self.callback(null, res);