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/lib/node/index.js CHANGED
@@ -25,7 +25,7 @@ const zlib = require('zlib');
25
25
  const util = require('util');
26
26
  const pkg = require('../../package.json');
27
27
  const RequestBase = require('../request-base');
28
- const shouldRetry = require('../should-retry');
28
+ const CookieJar = require('cookiejar');
29
29
 
30
30
  function request(method, url) {
31
31
  // callback
@@ -165,7 +165,7 @@ RequestBase(Request.prototype);
165
165
  *
166
166
  * ``` js
167
167
  * request.post('http://localhost/upload')
168
- * .attach(new Buffer('<b>Hello world</b>'), 'hello.html')
168
+ * .attach('field', Buffer.from('<b>Hello world</b>'), 'hello.html')
169
169
  * .end(callback);
170
170
  * ```
171
171
  *
@@ -418,13 +418,13 @@ Request.prototype._redirect = function(res){
418
418
 
419
419
  let headers = this.req._headers;
420
420
 
421
- const shouldStripCookie = parse(url).host !== parse(this.url).host;
421
+ const changesOrigin = parse(url).host !== parse(this.url).host;
422
422
 
423
423
  // implementation of 302 following defacto standard
424
424
  if (res.statusCode == 301 || res.statusCode == 302){
425
425
  // strip Content-* related fields
426
426
  // in case of POST etc
427
- headers = utils.cleanHeader(this.req._headers, shouldStripCookie);
427
+ headers = utils.cleanHeader(this.req._headers, changesOrigin);
428
428
 
429
429
  // force GET
430
430
  this.method = 'HEAD' == this.method
@@ -438,7 +438,7 @@ Request.prototype._redirect = function(res){
438
438
  if (res.statusCode == 303) {
439
439
  // strip Content-* related fields
440
440
  // in case of POST etc
441
- headers = utils.cleanHeader(this.req._headers, shouldStripCookie);
441
+ headers = utils.cleanHeader(this.req._headers, changesOrigin);
442
442
 
443
443
  // force method
444
444
  this.method = 'GET';
@@ -655,15 +655,24 @@ Request.prototype.request = function(){
655
655
  if (this.username && this.password) {
656
656
  this.auth(this.username, this.password);
657
657
  }
658
-
659
- // add cookies
660
- if (this.cookies) req.setHeader('Cookie', this.cookies);
661
-
662
658
  for (const key in this.header) {
663
659
  if (this.header.hasOwnProperty(key))
664
660
  req.setHeader(key, this.header[key]);
665
661
  }
666
662
 
663
+ // add cookies
664
+ if (this.cookies) {
665
+ if(this.header.hasOwnProperty('cookie')) {
666
+ // merge
667
+ const tmpJar = new CookieJar.CookieJar();
668
+ tmpJar.setCookies(this.header.cookie.split(';'));
669
+ tmpJar.setCookies(this.cookies.split(';'));
670
+ req.setHeader('Cookie',tmpJar.getCookies(CookieJar.CookieAccessInfo.All).toValueString());
671
+ } else {
672
+ req.setHeader('Cookie', this.cookies);
673
+ }
674
+ }
675
+
667
676
  return req;
668
677
  };
669
678
 
@@ -677,8 +686,7 @@ Request.prototype.request = function(){
677
686
  */
678
687
 
679
688
  Request.prototype.callback = function(err, res){
680
- // console.log(this._retries, this._maxRetries)
681
- if (this._maxRetries && this._retries++ < this._maxRetries && shouldRetry(err, res)) {
689
+ if (this._shouldRetry(err, res)) {
682
690
  return this._retry();
683
691
  }
684
692
 
@@ -690,20 +698,23 @@ Request.prototype.callback = function(err, res){
690
698
 
691
699
  if (!err) {
692
700
  try {
693
- if (this._isResponseOK(res)) {
694
- return fn(err, res);
695
- }
696
-
697
- let msg = 'Unsuccessful HTTP response';
698
- if (res) {
699
- msg = http.STATUS_CODES[res.status] || msg;
701
+ if (!this._isResponseOK(res)) {
702
+ let msg = 'Unsuccessful HTTP response';
703
+ if (res) {
704
+ msg = http.STATUS_CODES[res.status] || msg;
705
+ }
706
+ err = new Error(msg);
707
+ err.status = res ? res.status : undefined;
700
708
  }
701
- err = new Error(msg);
702
- err.status = res ? res.status : undefined;
703
709
  } catch (new_err) {
704
710
  err = new_err;
705
711
  }
706
712
  }
713
+ // It's important that the callback is called outside try/catch
714
+ // to avoid double callback
715
+ if (!err) {
716
+ return fn(null, res);
717
+ }
707
718
 
708
719
  err.response = res;
709
720
  if (this._maxRetries) err.retries = this._retries - 1;
@@ -941,6 +952,47 @@ Request.prototype._end = function() {
941
952
 
942
953
  this.emit('request', this);
943
954
 
955
+ const getProgressMonitor = () => {
956
+ const lengthComputable = true;
957
+ const total = req.getHeader('Content-Length');
958
+ let loaded = 0;
959
+
960
+ const progress = new Stream.Transform();
961
+ progress._transform = (chunk, encoding, cb) => {
962
+ loaded += chunk.length;
963
+ this.emit('progress', {
964
+ direction: 'upload',
965
+ lengthComputable,
966
+ loaded,
967
+ total,
968
+ });
969
+ cb(null, chunk);
970
+ };
971
+ return progress;
972
+ };
973
+
974
+ const bufferToChunks = (buffer) => {
975
+ const chunkSize = 16 * 1024; // default highWaterMark value
976
+ const chunking = new Stream.Readable();
977
+ const totalLength = buffer.length;
978
+ const remainder = totalLength % chunkSize;
979
+ const cutoff = totalLength - remainder;
980
+
981
+ for (let i = 0; i < cutoff; i += chunkSize) {
982
+ const chunk = buffer.slice(i, i + chunkSize);
983
+ chunking.push(chunk);
984
+ }
985
+
986
+ if (remainder > 0) {
987
+ const remainderBuffer = buffer.slice(-remainder);
988
+ chunking.push(remainderBuffer);
989
+ }
990
+
991
+ chunking.push(null); // no more data
992
+
993
+ return chunking;
994
+ }
995
+
944
996
  // if a FormData instance got created, then we send that as the request body
945
997
  const formData = this._formData;
946
998
  if (formData) {
@@ -960,27 +1012,11 @@ Request.prototype._end = function() {
960
1012
  if ('number' == typeof length) {
961
1013
  req.setHeader('Content-Length', length);
962
1014
  }
963
-
964
- const getProgressMonitor = () => {
965
- const lengthComputable = true;
966
- const total = req.getHeader('Content-Length');
967
- let loaded = 0;
968
-
969
- const progress = new Stream.Transform();
970
- progress._transform = (chunk, encoding, cb) => {
971
- loaded += chunk.length;
972
- this.emit('progress', {
973
- direction: 'upload',
974
- lengthComputable,
975
- loaded,
976
- total,
977
- });
978
- cb(null, chunk);
979
- };
980
- return progress;
981
- };
1015
+
982
1016
  formData.pipe(getProgressMonitor()).pipe(req);
983
1017
  });
1018
+ } else if (Buffer.isBuffer(data)) {
1019
+ bufferToChunks(data).pipe(getProgressMonitor()).pipe(req);
984
1020
  } else {
985
1021
  req.end(data);
986
1022
  }
@@ -1066,7 +1102,9 @@ function isImageOrVideo(mime) {
1066
1102
  */
1067
1103
 
1068
1104
  function isJSON(mime) {
1069
- return /[\/+]json\b/.test(mime);
1105
+ // should match /json or +json
1106
+ // but not /json-seq
1107
+ return /[\/+]json($|[^-\w])/.test(mime);
1070
1108
  }
1071
1109
 
1072
1110
  /**
@@ -143,19 +143,60 @@ RequestBase.prototype.timeout = function timeout(options){
143
143
  * Failed requests will be retried 'count' times if timeout or err.code >= 500.
144
144
  *
145
145
  * @param {Number} count
146
+ * @param {Function} [fn]
146
147
  * @return {Request} for chaining
147
148
  * @api public
148
149
  */
149
150
 
150
- RequestBase.prototype.retry = function retry(count){
151
+ RequestBase.prototype.retry = function retry(count, fn){
151
152
  // Default to 1 if no count passed or true
152
153
  if (arguments.length === 0 || count === true) count = 1;
153
154
  if (count <= 0) count = 0;
154
155
  this._maxRetries = count;
155
156
  this._retries = 0;
157
+ this._retryCallback = fn;
156
158
  return this;
157
159
  };
158
160
 
161
+ var ERROR_CODES = [
162
+ 'ECONNRESET',
163
+ 'ETIMEDOUT',
164
+ 'EADDRINFO',
165
+ 'ESOCKETTIMEDOUT'
166
+ ];
167
+
168
+ /**
169
+ * Determine if a request should be retried.
170
+ * (Borrowed from segmentio/superagent-retry)
171
+ *
172
+ * @param {Error} err
173
+ * @param {Response} [res]
174
+ * @returns {Boolean}
175
+ */
176
+ RequestBase.prototype._shouldRetry = function(err, res) {
177
+ if (!this._maxRetries || this._retries++ >= this._maxRetries) {
178
+ return false;
179
+ }
180
+ if (this._retryCallback) {
181
+ try {
182
+ var override = this._retryCallback(err, res);
183
+ if (override === true) return true;
184
+ if (override === false) return false;
185
+ // undefined falls back to defaults
186
+ } catch(e) {
187
+ console.error(e);
188
+ }
189
+ }
190
+ if (res && res.status && res.status >= 500 && res.status != 501) return true;
191
+ if (err) {
192
+ if (err.code && ~ERROR_CODES.indexOf(err.code)) return true;
193
+ // Superagent timeout
194
+ if (err.timeout && err.code == 'ECONNABORTED') return true;
195
+ if (err.crossDomain) return true;
196
+ }
197
+ return false;
198
+ };
199
+
159
200
  /**
160
201
  * Retry request
161
202
  *
@@ -164,6 +205,7 @@ RequestBase.prototype.retry = function retry(count){
164
205
  */
165
206
 
166
207
  RequestBase.prototype._retry = function() {
208
+
167
209
  this.clearTimeout();
168
210
 
169
211
  // node
@@ -202,7 +244,7 @@ RequestBase.prototype.then = function then(resolve, reject) {
202
244
  return this._fullfilledPromise.then(resolve, reject);
203
245
  };
204
246
 
205
- RequestBase.prototype.catch = function(cb) {
247
+ RequestBase.prototype['catch'] = function(cb) {
206
248
  return this.then(undefined, cb);
207
249
  };
208
250
 
@@ -124,6 +124,7 @@ ResponseBase.prototype._setStatusProperties = function(status){
124
124
  : false;
125
125
 
126
126
  // sugar
127
+ this.created = 201 == status;
127
128
  this.accepted = 202 == status;
128
129
  this.noContent = 204 == status;
129
130
  this.badRequest = 400 == status;
@@ -131,4 +132,5 @@ ResponseBase.prototype._setStatusProperties = function(status){
131
132
  this.notAcceptable = 406 == status;
132
133
  this.forbidden = 403 == status;
133
134
  this.notFound = 404 == status;
135
+ this.unprocessableEntity = 422 == status;
134
136
  };
package/lib/utils.js CHANGED
@@ -57,12 +57,14 @@ exports.parseLinks = function(str){
57
57
  * @api private
58
58
  */
59
59
 
60
- exports.cleanHeader = function(header, shouldStripCookie){
60
+ exports.cleanHeader = function(header, changesOrigin){
61
61
  delete header['content-type'];
62
62
  delete header['content-length'];
63
63
  delete header['transfer-encoding'];
64
64
  delete header['host'];
65
- if (shouldStripCookie) {
65
+ // secuirty
66
+ if (changesOrigin) {
67
+ delete header['authorization'];
66
68
  delete header['cookie'];
67
69
  }
68
70
  return header;
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "superagent",
3
- "version": "3.8.0-alpha.1",
3
+ "version": "3.8.3",
4
4
  "description": "elegant & feature rich browser / node HTTP with a fluent API",
5
5
  "scripts": {
6
- "prepublish": "make all",
6
+ "prepare": "make all",
7
7
  "test": "make test"
8
8
  },
9
9
  "keywords": [
@@ -29,11 +29,11 @@
29
29
  "debug": "^3.1.0",
30
30
  "extend": "^3.0.0",
31
31
  "form-data": "^2.3.1",
32
- "formidable": "^1.1.1",
32
+ "formidable": "^1.2.0",
33
33
  "methods": "^1.1.1",
34
34
  "mime": "^1.4.1",
35
35
  "qs": "^6.5.1",
36
- "readable-stream": "^2.0.5"
36
+ "readable-stream": "^2.3.5"
37
37
  },
38
38
  "devDependencies": {
39
39
  "Base64": "^1.0.1",
@@ -41,9 +41,9 @@
41
41
  "body-parser": "^1.18.2",
42
42
  "browserify": "^14.1.0",
43
43
  "cookie-parser": "^1.4.3",
44
- "express": "^4.16.0",
44
+ "express": "^4.16.3",
45
45
  "express-session": "^1.15.6",
46
- "marked": "^0.3.6",
46
+ "marked": "0.3.12",
47
47
  "mocha": "^3.5.3",
48
48
  "multer": "^1.3.0",
49
49
  "should": "^11.2.0",
package/superagent.js CHANGED
@@ -1,4 +1,4 @@
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){
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(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
2
2
  function Agent() {
3
3
  this._defaults = [];
4
4
  }
@@ -183,19 +183,60 @@ RequestBase.prototype.timeout = function timeout(options){
183
183
  * Failed requests will be retried 'count' times if timeout or err.code >= 500.
184
184
  *
185
185
  * @param {Number} count
186
+ * @param {Function} [fn]
186
187
  * @return {Request} for chaining
187
188
  * @api public
188
189
  */
189
190
 
190
- RequestBase.prototype.retry = function retry(count){
191
+ RequestBase.prototype.retry = function retry(count, fn){
191
192
  // Default to 1 if no count passed or true
192
193
  if (arguments.length === 0 || count === true) count = 1;
193
194
  if (count <= 0) count = 0;
194
195
  this._maxRetries = count;
195
196
  this._retries = 0;
197
+ this._retryCallback = fn;
196
198
  return this;
197
199
  };
198
200
 
201
+ var ERROR_CODES = [
202
+ 'ECONNRESET',
203
+ 'ETIMEDOUT',
204
+ 'EADDRINFO',
205
+ 'ESOCKETTIMEDOUT'
206
+ ];
207
+
208
+ /**
209
+ * Determine if a request should be retried.
210
+ * (Borrowed from segmentio/superagent-retry)
211
+ *
212
+ * @param {Error} err
213
+ * @param {Response} [res]
214
+ * @returns {Boolean}
215
+ */
216
+ RequestBase.prototype._shouldRetry = function(err, res) {
217
+ if (!this._maxRetries || this._retries++ >= this._maxRetries) {
218
+ return false;
219
+ }
220
+ if (this._retryCallback) {
221
+ try {
222
+ var override = this._retryCallback(err, res);
223
+ if (override === true) return true;
224
+ if (override === false) return false;
225
+ // undefined falls back to defaults
226
+ } catch(e) {
227
+ console.error(e);
228
+ }
229
+ }
230
+ if (res && res.status && res.status >= 500 && res.status != 501) return true;
231
+ if (err) {
232
+ if (err.code && ~ERROR_CODES.indexOf(err.code)) return true;
233
+ // Superagent timeout
234
+ if (err.timeout && err.code == 'ECONNABORTED') return true;
235
+ if (err.crossDomain) return true;
236
+ }
237
+ return false;
238
+ };
239
+
199
240
  /**
200
241
  * Retry request
201
242
  *
@@ -204,6 +245,7 @@ RequestBase.prototype.retry = function retry(count){
204
245
  */
205
246
 
206
247
  RequestBase.prototype._retry = function() {
248
+
207
249
  this.clearTimeout();
208
250
 
209
251
  // node
@@ -242,7 +284,7 @@ RequestBase.prototype.then = function then(resolve, reject) {
242
284
  return this._fullfilledPromise.then(resolve, reject);
243
285
  };
244
286
 
245
- RequestBase.prototype.catch = function(cb) {
287
+ RequestBase.prototype['catch'] = function(cb) {
246
288
  return this.then(undefined, cb);
247
289
  };
248
290
 
@@ -818,6 +860,7 @@ ResponseBase.prototype._setStatusProperties = function(status){
818
860
  : false;
819
861
 
820
862
  // sugar
863
+ this.created = 201 == status;
821
864
  this.accepted = 202 == status;
822
865
  this.noContent = 204 == status;
823
866
  this.badRequest = 400 == status;
@@ -825,36 +868,10 @@ ResponseBase.prototype._setStatusProperties = function(status){
825
868
  this.notAcceptable = 406 == status;
826
869
  this.forbidden = 403 == status;
827
870
  this.notFound = 404 == status;
871
+ this.unprocessableEntity = 422 == status;
828
872
  };
829
873
 
830
- },{"./utils":6}],5:[function(require,module,exports){
831
- 'use strict';
832
-
833
- var ERROR_CODES = [
834
- 'ECONNRESET',
835
- 'ETIMEDOUT',
836
- 'EADDRINFO',
837
- 'ESOCKETTIMEDOUT'
838
- ];
839
-
840
- /**
841
- * Determine if a request should be retried.
842
- * (Borrowed from segmentio/superagent-retry)
843
- *
844
- * @param {Error} err
845
- * @param {Response} [res]
846
- * @returns {Boolean}
847
- */
848
- module.exports = function shouldRetry(err, res) {
849
- if (err && err.code && ~ERROR_CODES.indexOf(err.code)) return true;
850
- if (res && res.status && res.status >= 500) return true;
851
- // Superagent timeout
852
- if (err && 'timeout' in err && err.code == 'ECONNABORTED') return true;
853
- if (err && 'crossDomain' in err) return true;
854
- return false;
855
- };
856
-
857
- },{}],6:[function(require,module,exports){
874
+ },{"./utils":5}],5:[function(require,module,exports){
858
875
  'use strict';
859
876
 
860
877
  /**
@@ -914,18 +931,20 @@ exports.parseLinks = function(str){
914
931
  * @api private
915
932
  */
916
933
 
917
- exports.cleanHeader = function(header, shouldStripCookie){
934
+ exports.cleanHeader = function(header, changesOrigin){
918
935
  delete header['content-type'];
919
936
  delete header['content-length'];
920
937
  delete header['transfer-encoding'];
921
938
  delete header['host'];
922
- if (shouldStripCookie) {
939
+ // secuirty
940
+ if (changesOrigin) {
941
+ delete header['authorization'];
923
942
  delete header['cookie'];
924
943
  }
925
944
  return header;
926
945
  };
927
946
 
928
- },{}],7:[function(require,module,exports){
947
+ },{}],6:[function(require,module,exports){
929
948
 
930
949
  /**
931
950
  * Expose `Emitter`.
@@ -1090,7 +1109,7 @@ Emitter.prototype.hasListeners = function(event){
1090
1109
  return !! this.listeners(event).length;
1091
1110
  };
1092
1111
 
1093
- },{}],8:[function(require,module,exports){
1112
+ },{}],7:[function(require,module,exports){
1094
1113
  /**
1095
1114
  * Root reference for iframes.
1096
1115
  */
@@ -1110,7 +1129,6 @@ var RequestBase = require('./request-base');
1110
1129
  var isObject = require('./is-object');
1111
1130
  var ResponseBase = require('./response-base');
1112
1131
  var Agent = require('./agent-base');
1113
- var shouldRetry = require('./should-retry');
1114
1132
 
1115
1133
  /**
1116
1134
  * Noop.
@@ -1280,7 +1298,7 @@ request.types = {
1280
1298
 
1281
1299
  request.serialize = {
1282
1300
  'application/x-www-form-urlencoded': serialize,
1283
- 'application/json': JSON.stringify,
1301
+ 'application/json': JSON.stringify
1284
1302
  };
1285
1303
 
1286
1304
  /**
@@ -1294,7 +1312,7 @@ request.serialize = {
1294
1312
 
1295
1313
  request.parse = {
1296
1314
  'application/x-www-form-urlencoded': parseString,
1297
- 'application/json': JSON.parse,
1315
+ 'application/json': JSON.parse
1298
1316
  };
1299
1317
 
1300
1318
  /**
@@ -1337,7 +1355,9 @@ function parseHeader(str) {
1337
1355
  */
1338
1356
 
1339
1357
  function isJSON(mime) {
1340
- return /[\/+]json\b/.test(mime);
1358
+ // should match /json or +json
1359
+ // but not /json-seq
1360
+ return /[\/+]json($|[^-\w])/.test(mime);
1341
1361
  }
1342
1362
 
1343
1363
  /**
@@ -1688,8 +1708,7 @@ Request.prototype._getFormData = function(){
1688
1708
  */
1689
1709
 
1690
1710
  Request.prototype.callback = function(err, res){
1691
- // console.log(this._retries, this._maxRetries)
1692
- if (this._maxRetries && this._retries++ < this._maxRetries && shouldRetry(err, res)) {
1711
+ if (this._shouldRetry(err, res)) {
1693
1712
  return this._retry();
1694
1713
  }
1695
1714
 
@@ -2012,5 +2031,5 @@ request.put = function(url, data, fn) {
2012
2031
  return req;
2013
2032
  };
2014
2033
 
2015
- },{"./agent-base":1,"./is-object":2,"./request-base":3,"./response-base":4,"./should-retry":5,"component-emitter":7}]},{},[8])(8)
2016
- });
2034
+ },{"./agent-base":1,"./is-object":2,"./request-base":3,"./response-base":4,"component-emitter":6}]},{},[7])(7)
2035
+ });
package/test.js ADDED
@@ -0,0 +1,7 @@
1
+ const request = require('./lib/node');
2
+
3
+ request.post('nevermind')
4
+ .field({a:1,b:2})
5
+ .attach('c', 'does-not-exist.txt')
6
+ .then(() => assert.fail("It should not allow this"))
7
+ .catch(() => true);