superagent 3.8.1 → 4.0.0-beta.5
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 +5 -2
- package/.zuul.yml +5 -2
- package/History.md +35 -0
- package/Makefile +10 -1
- package/Readme.md +10 -6
- package/docs/index.md +133 -69
- package/docs/test.html +6 -6
- package/dump.js +1 -0
- package/lib/agent-base.js +5 -5
- package/lib/client.js +80 -79
- package/lib/node/http2wrapper.js +188 -0
- package/lib/node/index.js +189 -52
- package/lib/node/parsers/text.js +1 -1
- package/lib/node/parsers/urlencoded.js +1 -1
- package/lib/node/response.js +1 -1
- package/lib/node/unzip.js +2 -2
- package/lib/request-base.js +29 -28
- package/lib/response-base.js +8 -6
- package/lib/utils.js +16 -22
- package/package.json +18 -15
- package/superagent.js +231 -209
- package/test.js +7 -0
- package/yarn.lock +1774 -1080
package/docs/test.html
CHANGED
|
@@ -265,7 +265,7 @@
|
|
|
265
265
|
.post(uri + '/echo')
|
|
266
266
|
.send({ name: 'tobi' })
|
|
267
267
|
.end(function(err, res){
|
|
268
|
-
assert.
|
|
268
|
+
assert.ifError(err)
|
|
269
269
|
res.text.should.equal('{"name":"tobi"}');
|
|
270
270
|
done();
|
|
271
271
|
});</code></pre></dd>
|
|
@@ -662,7 +662,7 @@ request
|
|
|
662
662
|
.get('http://localhost:5000/custom')
|
|
663
663
|
.buffer()
|
|
664
664
|
.end(function(err, res){
|
|
665
|
-
assert.
|
|
665
|
+
assert.ifError(err)
|
|
666
666
|
assert.equal('custom stuff', res.text);
|
|
667
667
|
assert(res.buffered);
|
|
668
668
|
done();
|
|
@@ -679,7 +679,7 @@ request
|
|
|
679
679
|
.send('hello this is dog')
|
|
680
680
|
.buffer(false)
|
|
681
681
|
.end(function(err, res){
|
|
682
|
-
assert.
|
|
682
|
+
assert.ifError(err)
|
|
683
683
|
assert.equal(null, res.text);
|
|
684
684
|
res.body.should.eql({});
|
|
685
685
|
var buf = '';
|
|
@@ -734,7 +734,7 @@ done();</code></pre></dd>
|
|
|
734
734
|
.type('application/x-dog')
|
|
735
735
|
.send('hello this is dog')
|
|
736
736
|
.end(function(err, res){
|
|
737
|
-
assert.
|
|
737
|
+
assert.ifError(err)
|
|
738
738
|
assert.equal(null, res.text);
|
|
739
739
|
res.body.should.eql({});
|
|
740
740
|
var buf = '';
|
|
@@ -761,7 +761,7 @@ request
|
|
|
761
761
|
.send(img)
|
|
762
762
|
.buffer(false)
|
|
763
763
|
.end(function(err, res){
|
|
764
|
-
assert.
|
|
764
|
+
assert.ifError(err)
|
|
765
765
|
assert(!res.buffered);
|
|
766
766
|
assert.equal(res.header['content-length'], Buffer.byteLength(img));
|
|
767
767
|
done();
|
|
@@ -774,7 +774,7 @@ request
|
|
|
774
774
|
.send(img)
|
|
775
775
|
.buffer(true)
|
|
776
776
|
.end(function(err, res){
|
|
777
|
-
assert.
|
|
777
|
+
assert.ifError(err)
|
|
778
778
|
assert(res.buffered);
|
|
779
779
|
assert.equal(res.header['content-length'], img.length);
|
|
780
780
|
done();
|
package/dump.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
package/lib/agent-base.js
CHANGED
|
@@ -3,17 +3,17 @@ function Agent() {
|
|
|
3
3
|
}
|
|
4
4
|
|
|
5
5
|
["use", "on", "once", "set", "query", "type", "accept", "auth", "withCredentials", "sortQuery", "retry", "ok", "redirects",
|
|
6
|
-
"timeout", "buffer", "serialize", "parse", "ca", "key", "pfx", "cert"].forEach(
|
|
6
|
+
"timeout", "buffer", "serialize", "parse", "ca", "key", "pfx", "cert"].forEach(fn => {
|
|
7
7
|
/** Default setting for all requests from this agent */
|
|
8
|
-
Agent.prototype[fn] = function(
|
|
9
|
-
this._defaults.push({fn
|
|
8
|
+
Agent.prototype[fn] = function(...args) {
|
|
9
|
+
this._defaults.push({fn, args});
|
|
10
10
|
return this;
|
|
11
11
|
}
|
|
12
12
|
});
|
|
13
13
|
|
|
14
14
|
Agent.prototype._setDefaults = function(req) {
|
|
15
|
-
this._defaults.forEach(
|
|
16
|
-
req[def.fn].apply(req, def.
|
|
15
|
+
this._defaults.forEach(def => {
|
|
16
|
+
req[def.fn].apply(req, def.args);
|
|
17
17
|
});
|
|
18
18
|
};
|
|
19
19
|
|
package/lib/client.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Root reference for iframes.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
let root;
|
|
6
6
|
if (typeof window !== 'undefined') { // Browser window
|
|
7
7
|
root = window;
|
|
8
8
|
} else if (typeof self !== 'undefined') { // Web Worker
|
|
@@ -12,11 +12,11 @@ if (typeof window !== 'undefined') { // Browser window
|
|
|
12
12
|
root = this;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
const Emitter = require('component-emitter');
|
|
16
|
+
const RequestBase = require('./request-base');
|
|
17
|
+
const isObject = require('./is-object');
|
|
18
|
+
const ResponseBase = require('./response-base');
|
|
19
|
+
const Agent = require('./agent-base');
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
22
|
* Noop.
|
|
@@ -28,7 +28,7 @@ function noop(){};
|
|
|
28
28
|
* Expose `request`.
|
|
29
29
|
*/
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
const request = exports = module.exports = function(method, url) {
|
|
32
32
|
// callback
|
|
33
33
|
if ('function' == typeof url) {
|
|
34
34
|
return new exports.Request('GET', method).end(url);
|
|
@@ -40,7 +40,7 @@ var request = exports = module.exports = function(method, url) {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
return new exports.Request(method, url);
|
|
43
|
-
}
|
|
43
|
+
};
|
|
44
44
|
|
|
45
45
|
exports.Request = Request;
|
|
46
46
|
|
|
@@ -48,7 +48,7 @@ exports.Request = Request;
|
|
|
48
48
|
* Determine XHR.
|
|
49
49
|
*/
|
|
50
50
|
|
|
51
|
-
request.getXHR =
|
|
51
|
+
request.getXHR = () => {
|
|
52
52
|
if (root.XMLHttpRequest
|
|
53
53
|
&& (!root.location || 'file:' != root.location.protocol
|
|
54
54
|
|| !root.ActiveXObject)) {
|
|
@@ -70,9 +70,9 @@ request.getXHR = function () {
|
|
|
70
70
|
* @api private
|
|
71
71
|
*/
|
|
72
72
|
|
|
73
|
-
|
|
74
|
-
?
|
|
75
|
-
:
|
|
73
|
+
const trim = ''.trim
|
|
74
|
+
? s => s.trim()
|
|
75
|
+
: s => s.replace(/(^\s*|\s*$)/g, '');
|
|
76
76
|
|
|
77
77
|
/**
|
|
78
78
|
* Serialize the given `obj`.
|
|
@@ -84,8 +84,8 @@ var trim = ''.trim
|
|
|
84
84
|
|
|
85
85
|
function serialize(obj) {
|
|
86
86
|
if (!isObject(obj)) return obj;
|
|
87
|
-
|
|
88
|
-
for (
|
|
87
|
+
const pairs = [];
|
|
88
|
+
for (const key in obj) {
|
|
89
89
|
pushEncodedKeyValuePair(pairs, key, obj[key]);
|
|
90
90
|
}
|
|
91
91
|
return pairs.join('&');
|
|
@@ -103,12 +103,12 @@ function serialize(obj) {
|
|
|
103
103
|
function pushEncodedKeyValuePair(pairs, key, val) {
|
|
104
104
|
if (val != null) {
|
|
105
105
|
if (Array.isArray(val)) {
|
|
106
|
-
val.forEach(
|
|
106
|
+
val.forEach(v => {
|
|
107
107
|
pushEncodedKeyValuePair(pairs, key, v);
|
|
108
108
|
});
|
|
109
109
|
} else if (isObject(val)) {
|
|
110
|
-
for(
|
|
111
|
-
pushEncodedKeyValuePair(pairs, key
|
|
110
|
+
for(const subkey in val) {
|
|
111
|
+
pushEncodedKeyValuePair(pairs, `${key}[${subkey}]`, val[subkey]);
|
|
112
112
|
}
|
|
113
113
|
} else {
|
|
114
114
|
pairs.push(encodeURIComponent(key)
|
|
@@ -134,12 +134,12 @@ request.serializeObject = serialize;
|
|
|
134
134
|
*/
|
|
135
135
|
|
|
136
136
|
function parseString(str) {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
137
|
+
const obj = {};
|
|
138
|
+
const pairs = str.split('&');
|
|
139
|
+
let pair;
|
|
140
|
+
let pos;
|
|
141
141
|
|
|
142
|
-
for (
|
|
142
|
+
for (let i = 0, len = pairs.length; i < len; ++i) {
|
|
143
143
|
pair = pairs[i];
|
|
144
144
|
pos = pair.indexOf('=');
|
|
145
145
|
if (pos == -1) {
|
|
@@ -186,7 +186,7 @@ request.types = {
|
|
|
186
186
|
|
|
187
187
|
request.serialize = {
|
|
188
188
|
'application/x-www-form-urlencoded': serialize,
|
|
189
|
-
'application/json': JSON.stringify
|
|
189
|
+
'application/json': JSON.stringify
|
|
190
190
|
};
|
|
191
191
|
|
|
192
192
|
/**
|
|
@@ -200,7 +200,7 @@ request.serialize = {
|
|
|
200
200
|
|
|
201
201
|
request.parse = {
|
|
202
202
|
'application/x-www-form-urlencoded': parseString,
|
|
203
|
-
'application/json': JSON.parse
|
|
203
|
+
'application/json': JSON.parse
|
|
204
204
|
};
|
|
205
205
|
|
|
206
206
|
/**
|
|
@@ -213,14 +213,14 @@ request.parse = {
|
|
|
213
213
|
*/
|
|
214
214
|
|
|
215
215
|
function parseHeader(str) {
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
for (
|
|
216
|
+
const lines = str.split(/\r?\n/);
|
|
217
|
+
const fields = {};
|
|
218
|
+
let index;
|
|
219
|
+
let line;
|
|
220
|
+
let field;
|
|
221
|
+
let val;
|
|
222
|
+
|
|
223
|
+
for (let i = 0, len = lines.length; i < len; ++i) {
|
|
224
224
|
line = lines[i];
|
|
225
225
|
index = line.indexOf(':');
|
|
226
226
|
if (index === -1) { // could be empty line, just skip it
|
|
@@ -243,7 +243,9 @@ function parseHeader(str) {
|
|
|
243
243
|
*/
|
|
244
244
|
|
|
245
245
|
function isJSON(mime) {
|
|
246
|
-
|
|
246
|
+
// should match /json or +json
|
|
247
|
+
// but not /json-seq
|
|
248
|
+
return /[\/+]json($|[^-\w])/.test(mime);
|
|
247
249
|
}
|
|
248
250
|
|
|
249
251
|
/**
|
|
@@ -300,7 +302,7 @@ function Response(req) {
|
|
|
300
302
|
? this.xhr.responseText
|
|
301
303
|
: null;
|
|
302
304
|
this.statusText = this.req.xhr.statusText;
|
|
303
|
-
|
|
305
|
+
let status = this.xhr.status;
|
|
304
306
|
// handle IE9 bug: http://stackoverflow.com/questions/10046972/msie-returns-status-code-of-1223-for-ajax-request
|
|
305
307
|
if (status === 1223) {
|
|
306
308
|
status = 204;
|
|
@@ -336,7 +338,7 @@ ResponseBase(Response.prototype);
|
|
|
336
338
|
*/
|
|
337
339
|
|
|
338
340
|
Response.prototype._parseBody = function(str) {
|
|
339
|
-
|
|
341
|
+
let parse = request.parse[this.type];
|
|
340
342
|
if (this.req._parser) {
|
|
341
343
|
return this.req._parser(this, str);
|
|
342
344
|
}
|
|
@@ -356,12 +358,12 @@ Response.prototype._parseBody = function(str) {
|
|
|
356
358
|
*/
|
|
357
359
|
|
|
358
360
|
Response.prototype.toError = function(){
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
361
|
+
const req = this.req;
|
|
362
|
+
const method = req.method;
|
|
363
|
+
const url = req.url;
|
|
362
364
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
+
const msg = `cannot ${method} ${url} (${this.status})`;
|
|
366
|
+
const err = new Error(msg);
|
|
365
367
|
err.status = this.status;
|
|
366
368
|
err.method = method;
|
|
367
369
|
err.url = url;
|
|
@@ -384,15 +386,15 @@ request.Response = Response;
|
|
|
384
386
|
*/
|
|
385
387
|
|
|
386
388
|
function Request(method, url) {
|
|
387
|
-
|
|
389
|
+
const self = this;
|
|
388
390
|
this._query = this._query || [];
|
|
389
391
|
this.method = method;
|
|
390
392
|
this.url = url;
|
|
391
393
|
this.header = {}; // preserves header name case
|
|
392
394
|
this._header = {}; // coerces header names to lowercase
|
|
393
|
-
this.on('end',
|
|
394
|
-
|
|
395
|
-
|
|
395
|
+
this.on('end', () => {
|
|
396
|
+
let err = null;
|
|
397
|
+
let res = null;
|
|
396
398
|
|
|
397
399
|
try {
|
|
398
400
|
res = new Response(self);
|
|
@@ -417,7 +419,7 @@ function Request(method, url) {
|
|
|
417
419
|
|
|
418
420
|
self.emit('response', res);
|
|
419
421
|
|
|
420
|
-
|
|
422
|
+
let new_err;
|
|
421
423
|
try {
|
|
422
424
|
if (!self._isResponseOK(res)) {
|
|
423
425
|
new_err = new Error(res.statusText || 'Unsuccessful HTTP response');
|
|
@@ -519,7 +521,7 @@ Request.prototype.auth = function(user, pass, options){
|
|
|
519
521
|
};
|
|
520
522
|
}
|
|
521
523
|
|
|
522
|
-
|
|
524
|
+
const encoder = string => {
|
|
523
525
|
if ('function' === typeof btoa) {
|
|
524
526
|
return btoa(string);
|
|
525
527
|
}
|
|
@@ -598,7 +600,7 @@ Request.prototype.callback = function(err, res){
|
|
|
598
600
|
return this._retry();
|
|
599
601
|
}
|
|
600
602
|
|
|
601
|
-
|
|
603
|
+
const fn = this._callback;
|
|
602
604
|
this.clearTimeout();
|
|
603
605
|
|
|
604
606
|
if (err) {
|
|
@@ -616,7 +618,7 @@ Request.prototype.callback = function(err, res){
|
|
|
616
618
|
*/
|
|
617
619
|
|
|
618
620
|
Request.prototype.crossDomainError = function(){
|
|
619
|
-
|
|
621
|
+
const err = new Error('Request has been terminated\nPossible causes: the network is offline, Origin is not allowed by Access-Control-Allow-Origin, the page is being unloaded, etc.');
|
|
620
622
|
err.crossDomain = true;
|
|
621
623
|
|
|
622
624
|
err.status = this.status;
|
|
@@ -633,7 +635,7 @@ Request.prototype.buffer = Request.prototype.ca = Request.prototype.agent = func
|
|
|
633
635
|
};
|
|
634
636
|
|
|
635
637
|
// This throws, because it can't send/receive data as expected
|
|
636
|
-
Request.prototype.pipe = Request.prototype.write =
|
|
638
|
+
Request.prototype.pipe = Request.prototype.write = () => {
|
|
637
639
|
throw Error("Streaming is not supported in browser version of superagent");
|
|
638
640
|
};
|
|
639
641
|
|
|
@@ -671,19 +673,21 @@ Request.prototype.end = function(fn){
|
|
|
671
673
|
// querystring
|
|
672
674
|
this._finalizeQueryString();
|
|
673
675
|
|
|
674
|
-
|
|
676
|
+
this._end();
|
|
675
677
|
};
|
|
676
678
|
|
|
677
679
|
Request.prototype._end = function() {
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
680
|
+
if (this._aborted) return this.callback(Error("The request has been aborted even before .end() was called"));
|
|
681
|
+
|
|
682
|
+
const self = this;
|
|
683
|
+
const xhr = (this.xhr = request.getXHR());
|
|
684
|
+
let data = this._formData || this._data;
|
|
681
685
|
|
|
682
686
|
this._setTimeouts();
|
|
683
687
|
|
|
684
688
|
// state change
|
|
685
|
-
xhr.onreadystatechange =
|
|
686
|
-
|
|
689
|
+
xhr.onreadystatechange = () => {
|
|
690
|
+
const readyState = xhr.readyState;
|
|
687
691
|
if (readyState >= 2 && self._responseTimeoutTimer) {
|
|
688
692
|
clearTimeout(self._responseTimeoutTimer);
|
|
689
693
|
}
|
|
@@ -693,7 +697,7 @@ Request.prototype._end = function() {
|
|
|
693
697
|
|
|
694
698
|
// In IE9, reads to any property (e.g. status) off of an aborted XHR will
|
|
695
699
|
// result in the error "Could not complete the operation due to error c00c023f"
|
|
696
|
-
|
|
700
|
+
let status;
|
|
697
701
|
try { status = xhr.status } catch(e) { status = 0; }
|
|
698
702
|
|
|
699
703
|
if (!status) {
|
|
@@ -704,7 +708,7 @@ Request.prototype._end = function() {
|
|
|
704
708
|
};
|
|
705
709
|
|
|
706
710
|
// progress
|
|
707
|
-
|
|
711
|
+
const handleProgress = (direction, e) => {
|
|
708
712
|
if (e.total > 0) {
|
|
709
713
|
e.percent = e.loaded / e.total * 100;
|
|
710
714
|
}
|
|
@@ -742,8 +746,8 @@ Request.prototype._end = function() {
|
|
|
742
746
|
// body
|
|
743
747
|
if (!this._formData && 'GET' != this.method && 'HEAD' != this.method && 'string' != typeof data && !this._isHost(data)) {
|
|
744
748
|
// serialize stuff
|
|
745
|
-
|
|
746
|
-
|
|
749
|
+
const contentType = this._header['content-type'];
|
|
750
|
+
let serialize = this._serializer || request.serialize[contentType ? contentType.split(';')[0] : ''];
|
|
747
751
|
if (!serialize && isJSON(contentType)) {
|
|
748
752
|
serialize = request.serialize['application/json'];
|
|
749
753
|
}
|
|
@@ -751,7 +755,7 @@ Request.prototype._end = function() {
|
|
|
751
755
|
}
|
|
752
756
|
|
|
753
757
|
// set header fields
|
|
754
|
-
for (
|
|
758
|
+
for (const field in this.header) {
|
|
755
759
|
if (null == this.header[field]) continue;
|
|
756
760
|
|
|
757
761
|
if (this.header.hasOwnProperty(field))
|
|
@@ -768,16 +772,13 @@ Request.prototype._end = function() {
|
|
|
768
772
|
// IE11 xhr.send(undefined) sends 'undefined' string as POST payload (instead of nothing)
|
|
769
773
|
// We need null here if data is undefined
|
|
770
774
|
xhr.send(typeof data !== 'undefined' ? data : null);
|
|
771
|
-
return this;
|
|
772
775
|
};
|
|
773
776
|
|
|
774
|
-
request.agent =
|
|
775
|
-
return new Agent();
|
|
776
|
-
};
|
|
777
|
+
request.agent = () => new Agent();
|
|
777
778
|
|
|
778
|
-
["GET", "POST", "OPTIONS", "PATCH", "PUT", "DELETE"].forEach(
|
|
779
|
+
["GET", "POST", "OPTIONS", "PATCH", "PUT", "DELETE"].forEach(method => {
|
|
779
780
|
Agent.prototype[method.toLowerCase()] = function(url, fn) {
|
|
780
|
-
|
|
781
|
+
const req = new request.Request(method, url);
|
|
781
782
|
this._setDefaults(req);
|
|
782
783
|
if (fn) {
|
|
783
784
|
req.end(fn);
|
|
@@ -798,8 +799,8 @@ Agent.prototype.del = Agent.prototype['delete'];
|
|
|
798
799
|
* @api public
|
|
799
800
|
*/
|
|
800
801
|
|
|
801
|
-
request.get =
|
|
802
|
-
|
|
802
|
+
request.get = (url, data, fn) => {
|
|
803
|
+
const req = request('GET', url);
|
|
803
804
|
if ('function' == typeof data) (fn = data), (data = null);
|
|
804
805
|
if (data) req.query(data);
|
|
805
806
|
if (fn) req.end(fn);
|
|
@@ -816,8 +817,8 @@ request.get = function(url, data, fn) {
|
|
|
816
817
|
* @api public
|
|
817
818
|
*/
|
|
818
819
|
|
|
819
|
-
request.head =
|
|
820
|
-
|
|
820
|
+
request.head = (url, data, fn) => {
|
|
821
|
+
const req = request('HEAD', url);
|
|
821
822
|
if ('function' == typeof data) (fn = data), (data = null);
|
|
822
823
|
if (data) req.query(data);
|
|
823
824
|
if (fn) req.end(fn);
|
|
@@ -834,8 +835,8 @@ request.head = function(url, data, fn) {
|
|
|
834
835
|
* @api public
|
|
835
836
|
*/
|
|
836
837
|
|
|
837
|
-
request.options =
|
|
838
|
-
|
|
838
|
+
request.options = (url, data, fn) => {
|
|
839
|
+
const req = request('OPTIONS', url);
|
|
839
840
|
if ('function' == typeof data) (fn = data), (data = null);
|
|
840
841
|
if (data) req.send(data);
|
|
841
842
|
if (fn) req.end(fn);
|
|
@@ -853,7 +854,7 @@ request.options = function(url, data, fn) {
|
|
|
853
854
|
*/
|
|
854
855
|
|
|
855
856
|
function del(url, data, fn) {
|
|
856
|
-
|
|
857
|
+
const req = request('DELETE', url);
|
|
857
858
|
if ('function' == typeof data) (fn = data), (data = null);
|
|
858
859
|
if (data) req.send(data);
|
|
859
860
|
if (fn) req.end(fn);
|
|
@@ -873,8 +874,8 @@ request['delete'] = del;
|
|
|
873
874
|
* @api public
|
|
874
875
|
*/
|
|
875
876
|
|
|
876
|
-
request.patch =
|
|
877
|
-
|
|
877
|
+
request.patch = (url, data, fn) => {
|
|
878
|
+
const req = request('PATCH', url);
|
|
878
879
|
if ('function' == typeof data) (fn = data), (data = null);
|
|
879
880
|
if (data) req.send(data);
|
|
880
881
|
if (fn) req.end(fn);
|
|
@@ -891,8 +892,8 @@ request.patch = function(url, data, fn) {
|
|
|
891
892
|
* @api public
|
|
892
893
|
*/
|
|
893
894
|
|
|
894
|
-
request.post =
|
|
895
|
-
|
|
895
|
+
request.post = (url, data, fn) => {
|
|
896
|
+
const req = request('POST', url);
|
|
896
897
|
if ('function' == typeof data) (fn = data), (data = null);
|
|
897
898
|
if (data) req.send(data);
|
|
898
899
|
if (fn) req.end(fn);
|
|
@@ -909,8 +910,8 @@ request.post = function(url, data, fn) {
|
|
|
909
910
|
* @api public
|
|
910
911
|
*/
|
|
911
912
|
|
|
912
|
-
request.put =
|
|
913
|
-
|
|
913
|
+
request.put = (url, data, fn) => {
|
|
914
|
+
const req = request('PUT', url);
|
|
914
915
|
if ('function' == typeof data) (fn = data), (data = null);
|
|
915
916
|
if (data) req.send(data);
|
|
916
917
|
if (fn) req.end(fn);
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const http2 = require('http2');
|
|
4
|
+
const Stream = require('stream');
|
|
5
|
+
const util = require('util');
|
|
6
|
+
const net = require('net');
|
|
7
|
+
const tls = require('tls');
|
|
8
|
+
const parse = require('url').parse;
|
|
9
|
+
|
|
10
|
+
const {
|
|
11
|
+
HTTP2_HEADER_PATH,
|
|
12
|
+
HTTP2_HEADER_STATUS,
|
|
13
|
+
HTTP2_HEADER_METHOD,
|
|
14
|
+
HTTP2_HEADER_AUTHORITY,
|
|
15
|
+
HTTP2_HEADER_HOST,
|
|
16
|
+
HTTP2_HEADER_SET_COOKIE,
|
|
17
|
+
NGHTTP2_CANCEL,
|
|
18
|
+
} = http2.constants;
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
function setProtocol(protocol) {
|
|
22
|
+
return {
|
|
23
|
+
request: function (options) {
|
|
24
|
+
return new Request(protocol, options);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function Request(protocol, options) {
|
|
30
|
+
Stream.call(this);
|
|
31
|
+
const defaultPort = protocol === 'https:' ? 443 : 80;
|
|
32
|
+
const defaultHost = 'localhost'
|
|
33
|
+
const port = options.port || defaultPort;
|
|
34
|
+
const host = options.host || defaultHost;
|
|
35
|
+
|
|
36
|
+
delete options.port
|
|
37
|
+
delete options.host
|
|
38
|
+
|
|
39
|
+
this.method = options.method;
|
|
40
|
+
this.path = options.path;
|
|
41
|
+
this.protocol = protocol;
|
|
42
|
+
this.host = host;
|
|
43
|
+
|
|
44
|
+
delete options.method
|
|
45
|
+
delete options.path
|
|
46
|
+
|
|
47
|
+
const sessionOptions = Object.assign({}, options);
|
|
48
|
+
if (options.socketPath) {
|
|
49
|
+
sessionOptions.socketPath = options.socketPath;
|
|
50
|
+
sessionOptions.createConnection = this.createUnixConnection.bind(this);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
this._headers = {};
|
|
54
|
+
|
|
55
|
+
const session = http2.connect(`${protocol}//${host}:${port}`, sessionOptions);
|
|
56
|
+
this.setHeader('host', `${host}:${port}`)
|
|
57
|
+
|
|
58
|
+
session.on('error', (err) => this.emit('error', err));
|
|
59
|
+
|
|
60
|
+
this.session = session;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Inherit from `Stream` (which inherits from `EventEmitter`).
|
|
65
|
+
*/
|
|
66
|
+
util.inherits(Request, Stream);
|
|
67
|
+
|
|
68
|
+
Request.prototype.createUnixConnection = function (authority, options) {
|
|
69
|
+
switch (this.protocol) {
|
|
70
|
+
case 'http:':
|
|
71
|
+
return net.connect(options.socketPath);
|
|
72
|
+
case 'https:':
|
|
73
|
+
options.ALPNProtocols = ['h2'];
|
|
74
|
+
options.servername = this.host;
|
|
75
|
+
options.allowHalfOpen = true;
|
|
76
|
+
return tls.connect(options.socketPath, options);
|
|
77
|
+
default:
|
|
78
|
+
throw new Error('Unsupported protocol', this.protocol);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
Request.prototype.setNoDelay = function (bool) {
|
|
83
|
+
// We can not use setNoDelay with HTTP/2.
|
|
84
|
+
// Node 10 limits http2session.socket methods to ones safe to use with HTTP/2.
|
|
85
|
+
// See also https://nodejs.org/api/http2.html#http2_http2session_socket
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
Request.prototype.getFrame = function () {
|
|
89
|
+
if (this.frame) {
|
|
90
|
+
return this.frame;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const method = {
|
|
94
|
+
[HTTP2_HEADER_PATH]: this.path,
|
|
95
|
+
[HTTP2_HEADER_METHOD]: this.method,
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
let headers = this.mapToHttp2Header(this._headers);
|
|
99
|
+
|
|
100
|
+
headers = Object.assign(headers, method);
|
|
101
|
+
|
|
102
|
+
const frame = this.session.request(headers);
|
|
103
|
+
frame.once('response', (headers, flags) => {
|
|
104
|
+
headers = this.mapToHttpHeader(headers);
|
|
105
|
+
frame.headers = headers;
|
|
106
|
+
frame.status = frame.statusCode = headers[HTTP2_HEADER_STATUS];
|
|
107
|
+
this.emit('response', frame);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
this._headerSent = true;
|
|
111
|
+
|
|
112
|
+
frame.once('drain', () => this.emit('drain'));
|
|
113
|
+
frame.on('error', (err) => this.emit('error', err));
|
|
114
|
+
frame.on('close', () => this.session.close());
|
|
115
|
+
|
|
116
|
+
this.frame = frame;
|
|
117
|
+
return frame;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
Request.prototype.mapToHttpHeader = function (headers) {
|
|
121
|
+
const keys = Object.keys(headers);
|
|
122
|
+
const http2Headers = {};
|
|
123
|
+
for (var i = 0; i < keys.length; i++) {
|
|
124
|
+
let key = keys[i];
|
|
125
|
+
let value = headers[key];
|
|
126
|
+
key = key.toLowerCase();
|
|
127
|
+
switch (key) {
|
|
128
|
+
case HTTP2_HEADER_SET_COOKIE:
|
|
129
|
+
value = Array.isArray(value) ? value : [value];
|
|
130
|
+
break;
|
|
131
|
+
default:
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
http2Headers[key] = value;
|
|
135
|
+
}
|
|
136
|
+
return http2Headers;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
Request.prototype.mapToHttp2Header = function (headers) {
|
|
140
|
+
const keys = Object.keys(headers);
|
|
141
|
+
const http2Headers = {};
|
|
142
|
+
for (var i = 0; i < keys.length; i++) {
|
|
143
|
+
let key = keys[i];
|
|
144
|
+
let value = headers[key];
|
|
145
|
+
key = key.toLowerCase();
|
|
146
|
+
switch (key) {
|
|
147
|
+
case HTTP2_HEADER_HOST:
|
|
148
|
+
key = HTTP2_HEADER_AUTHORITY;
|
|
149
|
+
value = /^http\:\/\/|^https\:\/\//.test(value) ? parse(value).host : value;
|
|
150
|
+
break;
|
|
151
|
+
default:
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
http2Headers[key] = value;
|
|
155
|
+
}
|
|
156
|
+
return http2Headers;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
Request.prototype.setHeader = function (name, value) {
|
|
160
|
+
this._headers[name.toLowerCase()] = value;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
Request.prototype.getHeader = function (name) {
|
|
164
|
+
return this._headers[name.toLowerCase()];
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
Request.prototype.write = function (data, encoding) {
|
|
168
|
+
const frame = this.getFrame();
|
|
169
|
+
return frame.write(data, encoding);
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
Request.prototype.pipe = function (stream, options) {
|
|
173
|
+
const frame = this.getFrame();
|
|
174
|
+
return frame.pipe(stream, options);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
Request.prototype.end = function (data) {
|
|
178
|
+
const frame = this.getFrame();
|
|
179
|
+
frame.end(data);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
Request.prototype.abort = function (data) {
|
|
183
|
+
const frame = this.getFrame();
|
|
184
|
+
frame.close(NGHTTP2_CANCEL);
|
|
185
|
+
this.session.destroy();
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
exports.setProtocol = setProtocol;
|