@wiajs/request 3.0.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/LICENSE +19 -0
- package/README.md +1133 -0
- package/index.js +1 -0
- package/lib/ZlibTransform.js +24 -0
- package/lib/caseless.js +100 -0
- package/lib/index.js +104 -0
- package/lib/request.js +828 -0
- package/lib/utils.js +226 -0
- package/package.json +55 -0
- package/request.js +1553 -0
- package/src/ZlibTransform.js +27 -0
- package/src/caseless.js +118 -0
- package/src/index.js +122 -0
- package/src/request.js +967 -0
- package/src/utils.js +274 -0
package/request.js
ADDED
|
@@ -0,0 +1,1553 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
var http = require('http')
|
|
4
|
+
var https = require('https')
|
|
5
|
+
var url = require('url')
|
|
6
|
+
var util = require('util')
|
|
7
|
+
var stream = require('stream')
|
|
8
|
+
var zlib = require('zlib')
|
|
9
|
+
var aws2 = require('aws-sign2')
|
|
10
|
+
var aws4 = require('aws4')
|
|
11
|
+
var httpSignature = require('http-signature')
|
|
12
|
+
var mime = require('mime-types')
|
|
13
|
+
var caseless = require('caseless')
|
|
14
|
+
var ForeverAgent = require('forever-agent')
|
|
15
|
+
var FormData = require('form-data')
|
|
16
|
+
var extend = require('extend')
|
|
17
|
+
var isstream = require('isstream')
|
|
18
|
+
var isTypedArray = require('is-typedarray').strict
|
|
19
|
+
var helpers = require('./lib/helpers')
|
|
20
|
+
var cookies = require('./lib/cookies')
|
|
21
|
+
var getProxyFromURI = require('./lib/getProxyFromURI')
|
|
22
|
+
var Querystring = require('./lib/querystring').Querystring
|
|
23
|
+
var Har = require('./lib/har').Har
|
|
24
|
+
var Auth = require('./lib/auth').Auth
|
|
25
|
+
var OAuth = require('./lib/oauth').OAuth
|
|
26
|
+
var hawk = require('./lib/hawk')
|
|
27
|
+
var Multipart = require('./lib/multipart').Multipart
|
|
28
|
+
var Redirect = require('./lib/redirect').Redirect
|
|
29
|
+
var Tunnel = require('./lib/tunnel').Tunnel
|
|
30
|
+
var now = require('performance-now')
|
|
31
|
+
var Buffer = require('safe-buffer').Buffer
|
|
32
|
+
|
|
33
|
+
var safeStringify = helpers.safeStringify
|
|
34
|
+
var isReadStream = helpers.isReadStream
|
|
35
|
+
var toBase64 = helpers.toBase64
|
|
36
|
+
var defer = helpers.defer
|
|
37
|
+
var copy = helpers.copy
|
|
38
|
+
var version = helpers.version
|
|
39
|
+
var globalCookieJar = cookies.jar()
|
|
40
|
+
|
|
41
|
+
var globalPool = {}
|
|
42
|
+
|
|
43
|
+
function filterForNonReserved (reserved, options) {
|
|
44
|
+
// Filter out properties that are not reserved.
|
|
45
|
+
// Reserved values are passed in at call site.
|
|
46
|
+
|
|
47
|
+
var object = {}
|
|
48
|
+
for (var i in options) {
|
|
49
|
+
var notReserved = (reserved.indexOf(i) === -1)
|
|
50
|
+
if (notReserved) {
|
|
51
|
+
object[i] = options[i]
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return object
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function filterOutReservedFunctions (reserved, options) {
|
|
58
|
+
// Filter out properties that are functions and are reserved.
|
|
59
|
+
// Reserved values are passed in at call site.
|
|
60
|
+
|
|
61
|
+
var object = {}
|
|
62
|
+
for (var i in options) {
|
|
63
|
+
var isReserved = !(reserved.indexOf(i) === -1)
|
|
64
|
+
var isFunction = (typeof options[i] === 'function')
|
|
65
|
+
if (!(isReserved && isFunction)) {
|
|
66
|
+
object[i] = options[i]
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return object
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Return a simpler request object to allow serialization
|
|
73
|
+
function requestToJSON () {
|
|
74
|
+
var self = this
|
|
75
|
+
return {
|
|
76
|
+
uri: self.uri,
|
|
77
|
+
method: self.method,
|
|
78
|
+
headers: self.headers
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Return a simpler response object to allow serialization
|
|
83
|
+
function responseToJSON () {
|
|
84
|
+
var self = this
|
|
85
|
+
return {
|
|
86
|
+
statusCode: self.statusCode,
|
|
87
|
+
body: self.body,
|
|
88
|
+
headers: self.headers,
|
|
89
|
+
request: requestToJSON.call(self.request)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function Request (options) {
|
|
94
|
+
// if given the method property in options, set property explicitMethod to true
|
|
95
|
+
|
|
96
|
+
// extend the Request instance with any non-reserved properties
|
|
97
|
+
// remove any reserved functions from the options object
|
|
98
|
+
// set Request instance to be readable and writable
|
|
99
|
+
// call init
|
|
100
|
+
|
|
101
|
+
var self = this
|
|
102
|
+
|
|
103
|
+
// start with HAR, then override with additional options
|
|
104
|
+
if (options.har) {
|
|
105
|
+
self._har = new Har(self)
|
|
106
|
+
options = self._har.options(options)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
stream.Stream.call(self)
|
|
110
|
+
var reserved = Object.keys(Request.prototype)
|
|
111
|
+
var nonReserved = filterForNonReserved(reserved, options)
|
|
112
|
+
|
|
113
|
+
extend(self, nonReserved)
|
|
114
|
+
options = filterOutReservedFunctions(reserved, options)
|
|
115
|
+
|
|
116
|
+
self.readable = true
|
|
117
|
+
self.writable = true
|
|
118
|
+
if (options.method) {
|
|
119
|
+
self.explicitMethod = true
|
|
120
|
+
}
|
|
121
|
+
self._qs = new Querystring(self)
|
|
122
|
+
self._auth = new Auth(self)
|
|
123
|
+
self._oauth = new OAuth(self)
|
|
124
|
+
self._multipart = new Multipart(self)
|
|
125
|
+
self._redirect = new Redirect(self)
|
|
126
|
+
self._tunnel = new Tunnel(self)
|
|
127
|
+
self.init(options)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
util.inherits(Request, stream.Stream)
|
|
131
|
+
|
|
132
|
+
// Debugging
|
|
133
|
+
Request.debug = process.env.NODE_DEBUG && /\brequest\b/.test(process.env.NODE_DEBUG)
|
|
134
|
+
function debug () {
|
|
135
|
+
if (Request.debug) {
|
|
136
|
+
console.error('REQUEST %s', util.format.apply(util, arguments))
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
Request.prototype.debug = debug
|
|
140
|
+
|
|
141
|
+
Request.prototype.init = function (options) {
|
|
142
|
+
// init() contains all the code to setup the request object.
|
|
143
|
+
// the actual outgoing request is not started until start() is called
|
|
144
|
+
// this function is called from both the constructor and on redirect.
|
|
145
|
+
var self = this
|
|
146
|
+
if (!options) {
|
|
147
|
+
options = {}
|
|
148
|
+
}
|
|
149
|
+
self.headers = self.headers ? copy(self.headers) : {}
|
|
150
|
+
|
|
151
|
+
// Delete headers with value undefined since they break
|
|
152
|
+
// ClientRequest.OutgoingMessage.setHeader in node 0.12
|
|
153
|
+
for (var headerName in self.headers) {
|
|
154
|
+
if (typeof self.headers[headerName] === 'undefined') {
|
|
155
|
+
delete self.headers[headerName]
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
caseless.httpify(self, self.headers)
|
|
160
|
+
|
|
161
|
+
if (!self.method) {
|
|
162
|
+
self.method = options.method || 'GET'
|
|
163
|
+
}
|
|
164
|
+
if (!self.localAddress) {
|
|
165
|
+
self.localAddress = options.localAddress
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
self._qs.init(options)
|
|
169
|
+
|
|
170
|
+
debug(options)
|
|
171
|
+
if (!self.pool && self.pool !== false) {
|
|
172
|
+
self.pool = globalPool
|
|
173
|
+
}
|
|
174
|
+
self.dests = self.dests || []
|
|
175
|
+
self.__isRequestRequest = true
|
|
176
|
+
|
|
177
|
+
// Protect against double callback
|
|
178
|
+
if (!self._callback && self.callback) {
|
|
179
|
+
self._callback = self.callback
|
|
180
|
+
self.callback = function () {
|
|
181
|
+
if (self._callbackCalled) {
|
|
182
|
+
return // Print a warning maybe?
|
|
183
|
+
}
|
|
184
|
+
self._callbackCalled = true
|
|
185
|
+
self._callback.apply(self, arguments)
|
|
186
|
+
}
|
|
187
|
+
self.on('error', self.callback.bind())
|
|
188
|
+
self.on('complete', self.callback.bind(self, null))
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// People use this property instead all the time, so support it
|
|
192
|
+
if (!self.uri && self.url) {
|
|
193
|
+
self.uri = self.url
|
|
194
|
+
delete self.url
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// If there's a baseUrl, then use it as the base URL (i.e. uri must be
|
|
198
|
+
// specified as a relative path and is appended to baseUrl).
|
|
199
|
+
if (self.baseUrl) {
|
|
200
|
+
if (typeof self.baseUrl !== 'string') {
|
|
201
|
+
return self.emit('error', new Error('options.baseUrl must be a string'))
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (typeof self.uri !== 'string') {
|
|
205
|
+
return self.emit('error', new Error('options.uri must be a string when using options.baseUrl'))
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (self.uri.indexOf('//') === 0 || self.uri.indexOf('://') !== -1) {
|
|
209
|
+
return self.emit('error', new Error('options.uri must be a path when using options.baseUrl'))
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Handle all cases to make sure that there's only one slash between
|
|
213
|
+
// baseUrl and uri.
|
|
214
|
+
var baseUrlEndsWithSlash = self.baseUrl.lastIndexOf('/') === self.baseUrl.length - 1
|
|
215
|
+
var uriStartsWithSlash = self.uri.indexOf('/') === 0
|
|
216
|
+
|
|
217
|
+
if (baseUrlEndsWithSlash && uriStartsWithSlash) {
|
|
218
|
+
self.uri = self.baseUrl + self.uri.slice(1)
|
|
219
|
+
} else if (baseUrlEndsWithSlash || uriStartsWithSlash) {
|
|
220
|
+
self.uri = self.baseUrl + self.uri
|
|
221
|
+
} else if (self.uri === '') {
|
|
222
|
+
self.uri = self.baseUrl
|
|
223
|
+
} else {
|
|
224
|
+
self.uri = self.baseUrl + '/' + self.uri
|
|
225
|
+
}
|
|
226
|
+
delete self.baseUrl
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// A URI is needed by this point, emit error if we haven't been able to get one
|
|
230
|
+
if (!self.uri) {
|
|
231
|
+
return self.emit('error', new Error('options.uri is a required argument'))
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// If a string URI/URL was given, parse it into a URL object
|
|
235
|
+
if (typeof self.uri === 'string') {
|
|
236
|
+
self.uri = url.parse(self.uri)
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Some URL objects are not from a URL parsed string and need href added
|
|
240
|
+
if (!self.uri.href) {
|
|
241
|
+
self.uri.href = url.format(self.uri)
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// DEPRECATED: Warning for users of the old Unix Sockets URL Scheme
|
|
245
|
+
if (self.uri.protocol === 'unix:') {
|
|
246
|
+
return self.emit('error', new Error('`unix://` URL scheme is no longer supported. Please use the format `http://unix:SOCKET:PATH`'))
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Support Unix Sockets
|
|
250
|
+
if (self.uri.host === 'unix') {
|
|
251
|
+
self.enableUnixSocket()
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (self.strictSSL === false) {
|
|
255
|
+
self.rejectUnauthorized = false
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if (!self.uri.pathname) { self.uri.pathname = '/' }
|
|
259
|
+
|
|
260
|
+
if (!(self.uri.host || (self.uri.hostname && self.uri.port)) && !self.uri.isUnix) {
|
|
261
|
+
// Invalid URI: it may generate lot of bad errors, like 'TypeError: Cannot call method `indexOf` of undefined' in CookieJar
|
|
262
|
+
// Detect and reject it as soon as possible
|
|
263
|
+
var faultyUri = url.format(self.uri)
|
|
264
|
+
var message = 'Invalid URI "' + faultyUri + '"'
|
|
265
|
+
if (Object.keys(options).length === 0) {
|
|
266
|
+
// No option ? This can be the sign of a redirect
|
|
267
|
+
// As this is a case where the user cannot do anything (they didn't call request directly with this URL)
|
|
268
|
+
// they should be warned that it can be caused by a redirection (can save some hair)
|
|
269
|
+
message += '. This can be caused by a crappy redirection.'
|
|
270
|
+
}
|
|
271
|
+
// This error was fatal
|
|
272
|
+
self.abort()
|
|
273
|
+
return self.emit('error', new Error(message))
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
if (!self.hasOwnProperty('proxy')) {
|
|
277
|
+
self.proxy = getProxyFromURI(self.uri)
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
self.tunnel = self._tunnel.isEnabled()
|
|
281
|
+
if (self.proxy) {
|
|
282
|
+
self._tunnel.setup(options)
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
self._redirect.onRequest(options)
|
|
286
|
+
|
|
287
|
+
self.setHost = false
|
|
288
|
+
if (!self.hasHeader('host')) {
|
|
289
|
+
var hostHeaderName = self.originalHostHeaderName || 'host'
|
|
290
|
+
self.setHeader(hostHeaderName, self.uri.host)
|
|
291
|
+
// Drop :port suffix from Host header if known protocol.
|
|
292
|
+
if (self.uri.port) {
|
|
293
|
+
if ((self.uri.port === '80' && self.uri.protocol === 'http:') ||
|
|
294
|
+
(self.uri.port === '443' && self.uri.protocol === 'https:')) {
|
|
295
|
+
self.setHeader(hostHeaderName, self.uri.hostname)
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
self.setHost = true
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
self.jar(self._jar || options.jar)
|
|
302
|
+
|
|
303
|
+
if (!self.uri.port) {
|
|
304
|
+
if (self.uri.protocol === 'http:') { self.uri.port = 80 } else if (self.uri.protocol === 'https:') { self.uri.port = 443 }
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
if (self.proxy && !self.tunnel) {
|
|
308
|
+
self.port = self.proxy.port
|
|
309
|
+
self.host = self.proxy.hostname
|
|
310
|
+
} else {
|
|
311
|
+
self.port = self.uri.port
|
|
312
|
+
self.host = self.uri.hostname
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
if (options.form) {
|
|
316
|
+
self.form(options.form)
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
if (options.formData) {
|
|
320
|
+
var formData = options.formData
|
|
321
|
+
var requestForm = self.form()
|
|
322
|
+
var appendFormValue = function (key, value) {
|
|
323
|
+
if (value && value.hasOwnProperty('value') && value.hasOwnProperty('options')) {
|
|
324
|
+
requestForm.append(key, value.value, value.options)
|
|
325
|
+
} else {
|
|
326
|
+
requestForm.append(key, value)
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
for (var formKey in formData) {
|
|
330
|
+
if (formData.hasOwnProperty(formKey)) {
|
|
331
|
+
var formValue = formData[formKey]
|
|
332
|
+
if (formValue instanceof Array) {
|
|
333
|
+
for (var j = 0; j < formValue.length; j++) {
|
|
334
|
+
appendFormValue(formKey, formValue[j])
|
|
335
|
+
}
|
|
336
|
+
} else {
|
|
337
|
+
appendFormValue(formKey, formValue)
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
if (options.qs) {
|
|
344
|
+
self.qs(options.qs)
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
if (self.uri.path) {
|
|
348
|
+
self.path = self.uri.path
|
|
349
|
+
} else {
|
|
350
|
+
self.path = self.uri.pathname + (self.uri.search || '')
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
if (self.path.length === 0) {
|
|
354
|
+
self.path = '/'
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// Auth must happen last in case signing is dependent on other headers
|
|
358
|
+
if (options.aws) {
|
|
359
|
+
self.aws(options.aws)
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
if (options.hawk) {
|
|
363
|
+
self.hawk(options.hawk)
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
if (options.httpSignature) {
|
|
367
|
+
self.httpSignature(options.httpSignature)
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
if (options.auth) {
|
|
371
|
+
if (Object.prototype.hasOwnProperty.call(options.auth, 'username')) {
|
|
372
|
+
options.auth.user = options.auth.username
|
|
373
|
+
}
|
|
374
|
+
if (Object.prototype.hasOwnProperty.call(options.auth, 'password')) {
|
|
375
|
+
options.auth.pass = options.auth.password
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
self.auth(
|
|
379
|
+
options.auth.user,
|
|
380
|
+
options.auth.pass,
|
|
381
|
+
options.auth.sendImmediately,
|
|
382
|
+
options.auth.bearer
|
|
383
|
+
)
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
if (self.gzip && !self.hasHeader('accept-encoding')) {
|
|
387
|
+
self.setHeader('accept-encoding', 'gzip, deflate')
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
if (self.uri.auth && !self.hasHeader('authorization')) {
|
|
391
|
+
var uriAuthPieces = self.uri.auth.split(':').map(function (item) { return self._qs.unescape(item) })
|
|
392
|
+
self.auth(uriAuthPieces[0], uriAuthPieces.slice(1).join(':'), true)
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
if (!self.tunnel && self.proxy && self.proxy.auth && !self.hasHeader('proxy-authorization')) {
|
|
396
|
+
var proxyAuthPieces = self.proxy.auth.split(':').map(function (item) { return self._qs.unescape(item) })
|
|
397
|
+
var authHeader = 'Basic ' + toBase64(proxyAuthPieces.join(':'))
|
|
398
|
+
self.setHeader('proxy-authorization', authHeader)
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
if (self.proxy && !self.tunnel) {
|
|
402
|
+
self.path = (self.uri.protocol + '//' + self.uri.host + self.path)
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
if (options.json) {
|
|
406
|
+
self.json(options.json)
|
|
407
|
+
}
|
|
408
|
+
if (options.multipart) {
|
|
409
|
+
self.multipart(options.multipart)
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
if (options.time) {
|
|
413
|
+
self.timing = true
|
|
414
|
+
|
|
415
|
+
// NOTE: elapsedTime is deprecated in favor of .timings
|
|
416
|
+
self.elapsedTime = self.elapsedTime || 0
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
function setContentLength () {
|
|
420
|
+
if (isTypedArray(self.body)) {
|
|
421
|
+
self.body = Buffer.from(self.body)
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
if (!self.hasHeader('content-length')) {
|
|
425
|
+
var length
|
|
426
|
+
if (typeof self.body === 'string') {
|
|
427
|
+
length = Buffer.byteLength(self.body)
|
|
428
|
+
} else if (Array.isArray(self.body)) {
|
|
429
|
+
length = self.body.reduce(function (a, b) { return a + b.length }, 0)
|
|
430
|
+
} else {
|
|
431
|
+
length = self.body.length
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
if (length) {
|
|
435
|
+
self.setHeader('content-length', length)
|
|
436
|
+
} else {
|
|
437
|
+
self.emit('error', new Error('Argument error, options.body.'))
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
if (self.body && !isstream(self.body)) {
|
|
442
|
+
setContentLength()
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
if (options.oauth) {
|
|
446
|
+
self.oauth(options.oauth)
|
|
447
|
+
} else if (self._oauth.params && self.hasHeader('authorization')) {
|
|
448
|
+
self.oauth(self._oauth.params)
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
var protocol = self.proxy && !self.tunnel ? self.proxy.protocol : self.uri.protocol
|
|
452
|
+
var defaultModules = {'http:': http, 'https:': https}
|
|
453
|
+
var httpModules = self.httpModules || {}
|
|
454
|
+
|
|
455
|
+
self.httpModule = httpModules[protocol] || defaultModules[protocol]
|
|
456
|
+
|
|
457
|
+
if (!self.httpModule) {
|
|
458
|
+
return self.emit('error', new Error('Invalid protocol: ' + protocol))
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
if (options.ca) {
|
|
462
|
+
self.ca = options.ca
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
if (!self.agent) {
|
|
466
|
+
if (options.agentOptions) {
|
|
467
|
+
self.agentOptions = options.agentOptions
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
if (options.agentClass) {
|
|
471
|
+
self.agentClass = options.agentClass
|
|
472
|
+
} else if (options.forever) {
|
|
473
|
+
var v = version()
|
|
474
|
+
// use ForeverAgent in node 0.10- only
|
|
475
|
+
if (v.major === 0 && v.minor <= 10) {
|
|
476
|
+
self.agentClass = protocol === 'http:' ? ForeverAgent : ForeverAgent.SSL
|
|
477
|
+
} else {
|
|
478
|
+
self.agentClass = self.httpModule.Agent
|
|
479
|
+
self.agentOptions = self.agentOptions || {}
|
|
480
|
+
self.agentOptions.keepAlive = true
|
|
481
|
+
}
|
|
482
|
+
} else {
|
|
483
|
+
self.agentClass = self.httpModule.Agent
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
if (self.pool === false) {
|
|
488
|
+
self.agent = false
|
|
489
|
+
} else {
|
|
490
|
+
self.agent = self.agent || self.getNewAgent()
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
self.on('pipe', function (src) {
|
|
494
|
+
if (self.ntick && self._started) {
|
|
495
|
+
self.emit('error', new Error('You cannot pipe to this stream after the outbound request has started.'))
|
|
496
|
+
}
|
|
497
|
+
self.src = src
|
|
498
|
+
if (isReadStream(src)) {
|
|
499
|
+
if (!self.hasHeader('content-type')) {
|
|
500
|
+
self.setHeader('content-type', mime.lookup(src.path))
|
|
501
|
+
}
|
|
502
|
+
} else {
|
|
503
|
+
if (src.headers) {
|
|
504
|
+
for (var i in src.headers) {
|
|
505
|
+
if (!self.hasHeader(i)) {
|
|
506
|
+
self.setHeader(i, src.headers[i])
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
if (self._json && !self.hasHeader('content-type')) {
|
|
511
|
+
self.setHeader('content-type', 'application/json')
|
|
512
|
+
}
|
|
513
|
+
if (src.method && !self.explicitMethod) {
|
|
514
|
+
self.method = src.method
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
// self.on('pipe', function () {
|
|
519
|
+
// console.error('You have already piped to this stream. Pipeing twice is likely to break the request.')
|
|
520
|
+
// })
|
|
521
|
+
})
|
|
522
|
+
|
|
523
|
+
defer(function () {
|
|
524
|
+
if (self._aborted) {
|
|
525
|
+
return
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
var end = function () {
|
|
529
|
+
if (self._form) {
|
|
530
|
+
if (!self._auth.hasAuth) {
|
|
531
|
+
self._form.pipe(self)
|
|
532
|
+
} else if (self._auth.hasAuth && self._auth.sentAuth) {
|
|
533
|
+
self._form.pipe(self)
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
if (self._multipart && self._multipart.chunked) {
|
|
537
|
+
self._multipart.body.pipe(self)
|
|
538
|
+
}
|
|
539
|
+
if (self.body) {
|
|
540
|
+
if (isstream(self.body)) {
|
|
541
|
+
self.body.pipe(self)
|
|
542
|
+
} else {
|
|
543
|
+
setContentLength()
|
|
544
|
+
if (Array.isArray(self.body)) {
|
|
545
|
+
self.body.forEach(function (part) {
|
|
546
|
+
self.write(part)
|
|
547
|
+
})
|
|
548
|
+
} else {
|
|
549
|
+
self.write(self.body)
|
|
550
|
+
}
|
|
551
|
+
self.end()
|
|
552
|
+
}
|
|
553
|
+
} else if (self.requestBodyStream) {
|
|
554
|
+
console.warn('options.requestBodyStream is deprecated, please pass the request object to stream.pipe.')
|
|
555
|
+
self.requestBodyStream.pipe(self)
|
|
556
|
+
} else if (!self.src) {
|
|
557
|
+
if (self._auth.hasAuth && !self._auth.sentAuth) {
|
|
558
|
+
self.end()
|
|
559
|
+
return
|
|
560
|
+
}
|
|
561
|
+
if (self.method !== 'GET' && typeof self.method !== 'undefined') {
|
|
562
|
+
self.setHeader('content-length', 0)
|
|
563
|
+
}
|
|
564
|
+
self.end()
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
if (self._form && !self.hasHeader('content-length')) {
|
|
569
|
+
// Before ending the request, we had to compute the length of the whole form, asyncly
|
|
570
|
+
self.setHeader(self._form.getHeaders(), true)
|
|
571
|
+
self._form.getLength(function (err, length) {
|
|
572
|
+
if (!err && !isNaN(length)) {
|
|
573
|
+
self.setHeader('content-length', length)
|
|
574
|
+
}
|
|
575
|
+
end()
|
|
576
|
+
})
|
|
577
|
+
} else {
|
|
578
|
+
end()
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
self.ntick = true
|
|
582
|
+
})
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
Request.prototype.getNewAgent = function () {
|
|
586
|
+
var self = this
|
|
587
|
+
var Agent = self.agentClass
|
|
588
|
+
var options = {}
|
|
589
|
+
if (self.agentOptions) {
|
|
590
|
+
for (var i in self.agentOptions) {
|
|
591
|
+
options[i] = self.agentOptions[i]
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
if (self.ca) {
|
|
595
|
+
options.ca = self.ca
|
|
596
|
+
}
|
|
597
|
+
if (self.ciphers) {
|
|
598
|
+
options.ciphers = self.ciphers
|
|
599
|
+
}
|
|
600
|
+
if (self.secureProtocol) {
|
|
601
|
+
options.secureProtocol = self.secureProtocol
|
|
602
|
+
}
|
|
603
|
+
if (self.secureOptions) {
|
|
604
|
+
options.secureOptions = self.secureOptions
|
|
605
|
+
}
|
|
606
|
+
if (typeof self.rejectUnauthorized !== 'undefined') {
|
|
607
|
+
options.rejectUnauthorized = self.rejectUnauthorized
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
if (self.cert && self.key) {
|
|
611
|
+
options.key = self.key
|
|
612
|
+
options.cert = self.cert
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
if (self.pfx) {
|
|
616
|
+
options.pfx = self.pfx
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
if (self.passphrase) {
|
|
620
|
+
options.passphrase = self.passphrase
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
var poolKey = ''
|
|
624
|
+
|
|
625
|
+
// different types of agents are in different pools
|
|
626
|
+
if (Agent !== self.httpModule.Agent) {
|
|
627
|
+
poolKey += Agent.name
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
// ca option is only relevant if proxy or destination are https
|
|
631
|
+
var proxy = self.proxy
|
|
632
|
+
if (typeof proxy === 'string') {
|
|
633
|
+
proxy = url.parse(proxy)
|
|
634
|
+
}
|
|
635
|
+
var isHttps = (proxy && proxy.protocol === 'https:') || this.uri.protocol === 'https:'
|
|
636
|
+
|
|
637
|
+
if (isHttps) {
|
|
638
|
+
if (options.ca) {
|
|
639
|
+
if (poolKey) {
|
|
640
|
+
poolKey += ':'
|
|
641
|
+
}
|
|
642
|
+
poolKey += options.ca
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
if (typeof options.rejectUnauthorized !== 'undefined') {
|
|
646
|
+
if (poolKey) {
|
|
647
|
+
poolKey += ':'
|
|
648
|
+
}
|
|
649
|
+
poolKey += options.rejectUnauthorized
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
if (options.cert) {
|
|
653
|
+
if (poolKey) {
|
|
654
|
+
poolKey += ':'
|
|
655
|
+
}
|
|
656
|
+
poolKey += options.cert.toString('ascii') + options.key.toString('ascii')
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
if (options.pfx) {
|
|
660
|
+
if (poolKey) {
|
|
661
|
+
poolKey += ':'
|
|
662
|
+
}
|
|
663
|
+
poolKey += options.pfx.toString('ascii')
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
if (options.ciphers) {
|
|
667
|
+
if (poolKey) {
|
|
668
|
+
poolKey += ':'
|
|
669
|
+
}
|
|
670
|
+
poolKey += options.ciphers
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
if (options.secureProtocol) {
|
|
674
|
+
if (poolKey) {
|
|
675
|
+
poolKey += ':'
|
|
676
|
+
}
|
|
677
|
+
poolKey += options.secureProtocol
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
if (options.secureOptions) {
|
|
681
|
+
if (poolKey) {
|
|
682
|
+
poolKey += ':'
|
|
683
|
+
}
|
|
684
|
+
poolKey += options.secureOptions
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
if (self.pool === globalPool && !poolKey && Object.keys(options).length === 0 && self.httpModule.globalAgent) {
|
|
689
|
+
// not doing anything special. Use the globalAgent
|
|
690
|
+
return self.httpModule.globalAgent
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
// we're using a stored agent. Make sure it's protocol-specific
|
|
694
|
+
poolKey = self.uri.protocol + poolKey
|
|
695
|
+
|
|
696
|
+
// generate a new agent for this setting if none yet exists
|
|
697
|
+
if (!self.pool[poolKey]) {
|
|
698
|
+
self.pool[poolKey] = new Agent(options)
|
|
699
|
+
// properly set maxSockets on new agents
|
|
700
|
+
if (self.pool.maxSockets) {
|
|
701
|
+
self.pool[poolKey].maxSockets = self.pool.maxSockets
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
return self.pool[poolKey]
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
Request.prototype.start = function () {
|
|
709
|
+
// start() is called once we are ready to send the outgoing HTTP request.
|
|
710
|
+
// this is usually called on the first write(), end() or on nextTick()
|
|
711
|
+
var self = this
|
|
712
|
+
|
|
713
|
+
if (self.timing) {
|
|
714
|
+
// All timings will be relative to this request's startTime. In order to do this,
|
|
715
|
+
// we need to capture the wall-clock start time (via Date), immediately followed
|
|
716
|
+
// by the high-resolution timer (via now()). While these two won't be set
|
|
717
|
+
// at the _exact_ same time, they should be close enough to be able to calculate
|
|
718
|
+
// high-resolution, monotonically non-decreasing timestamps relative to startTime.
|
|
719
|
+
var startTime = new Date().getTime()
|
|
720
|
+
var startTimeNow = now()
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
if (self._aborted) {
|
|
724
|
+
return
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
self._started = true
|
|
728
|
+
self.method = self.method || 'GET'
|
|
729
|
+
self.href = self.uri.href
|
|
730
|
+
|
|
731
|
+
if (self.src && self.src.stat && self.src.stat.size && !self.hasHeader('content-length')) {
|
|
732
|
+
self.setHeader('content-length', self.src.stat.size)
|
|
733
|
+
}
|
|
734
|
+
if (self._aws) {
|
|
735
|
+
self.aws(self._aws, true)
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
// We have a method named auth, which is completely different from the http.request
|
|
739
|
+
// auth option. If we don't remove it, we're gonna have a bad time.
|
|
740
|
+
var reqOptions = copy(self)
|
|
741
|
+
delete reqOptions.auth
|
|
742
|
+
|
|
743
|
+
debug('make request', self.uri.href)
|
|
744
|
+
|
|
745
|
+
// node v6.8.0 now supports a `timeout` value in `http.request()`, but we
|
|
746
|
+
// should delete it for now since we handle timeouts manually for better
|
|
747
|
+
// consistency with node versions before v6.8.0
|
|
748
|
+
delete reqOptions.timeout
|
|
749
|
+
|
|
750
|
+
try {
|
|
751
|
+
self.req = self.httpModule.request(reqOptions)
|
|
752
|
+
} catch (err) {
|
|
753
|
+
self.emit('error', err)
|
|
754
|
+
return
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
if (self.timing) {
|
|
758
|
+
self.startTime = startTime
|
|
759
|
+
self.startTimeNow = startTimeNow
|
|
760
|
+
|
|
761
|
+
// Timing values will all be relative to startTime (by comparing to startTimeNow
|
|
762
|
+
// so we have an accurate clock)
|
|
763
|
+
self.timings = {}
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
var timeout
|
|
767
|
+
if (self.timeout && !self.timeoutTimer) {
|
|
768
|
+
if (self.timeout < 0) {
|
|
769
|
+
timeout = 0
|
|
770
|
+
} else if (typeof self.timeout === 'number' && isFinite(self.timeout)) {
|
|
771
|
+
timeout = self.timeout
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
self.req.on('response', self.onRequestResponse.bind(self))
|
|
776
|
+
self.req.on('error', self.onRequestError.bind(self))
|
|
777
|
+
self.req.on('drain', function () {
|
|
778
|
+
self.emit('drain')
|
|
779
|
+
})
|
|
780
|
+
|
|
781
|
+
self.req.on('socket', function (socket) {
|
|
782
|
+
// `._connecting` was the old property which was made public in node v6.1.0
|
|
783
|
+
var isConnecting = socket._connecting || socket.connecting
|
|
784
|
+
if (self.timing) {
|
|
785
|
+
self.timings.socket = now() - self.startTimeNow
|
|
786
|
+
|
|
787
|
+
if (isConnecting) {
|
|
788
|
+
var onLookupTiming = function () {
|
|
789
|
+
self.timings.lookup = now() - self.startTimeNow
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
var onConnectTiming = function () {
|
|
793
|
+
self.timings.connect = now() - self.startTimeNow
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
socket.once('lookup', onLookupTiming)
|
|
797
|
+
socket.once('connect', onConnectTiming)
|
|
798
|
+
|
|
799
|
+
// clean up timing event listeners if needed on error
|
|
800
|
+
self.req.once('error', function () {
|
|
801
|
+
socket.removeListener('lookup', onLookupTiming)
|
|
802
|
+
socket.removeListener('connect', onConnectTiming)
|
|
803
|
+
})
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
var setReqTimeout = function () {
|
|
808
|
+
// This timeout sets the amount of time to wait *between* bytes sent
|
|
809
|
+
// from the server once connected.
|
|
810
|
+
//
|
|
811
|
+
// In particular, it's useful for erroring if the server fails to send
|
|
812
|
+
// data halfway through streaming a response.
|
|
813
|
+
self.req.setTimeout(timeout, function () {
|
|
814
|
+
if (self.req) {
|
|
815
|
+
self.abort()
|
|
816
|
+
var e = new Error('ESOCKETTIMEDOUT')
|
|
817
|
+
e.code = 'ESOCKETTIMEDOUT'
|
|
818
|
+
e.connect = false
|
|
819
|
+
self.emit('error', e)
|
|
820
|
+
}
|
|
821
|
+
})
|
|
822
|
+
}
|
|
823
|
+
if (timeout !== undefined) {
|
|
824
|
+
// Only start the connection timer if we're actually connecting a new
|
|
825
|
+
// socket, otherwise if we're already connected (because this is a
|
|
826
|
+
// keep-alive connection) do not bother. This is important since we won't
|
|
827
|
+
// get a 'connect' event for an already connected socket.
|
|
828
|
+
if (isConnecting) {
|
|
829
|
+
var onReqSockConnect = function () {
|
|
830
|
+
socket.removeListener('connect', onReqSockConnect)
|
|
831
|
+
self.clearTimeout()
|
|
832
|
+
setReqTimeout()
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
socket.on('connect', onReqSockConnect)
|
|
836
|
+
|
|
837
|
+
self.req.on('error', function (err) { // eslint-disable-line handle-callback-err
|
|
838
|
+
socket.removeListener('connect', onReqSockConnect)
|
|
839
|
+
})
|
|
840
|
+
|
|
841
|
+
// Set a timeout in memory - this block will throw if the server takes more
|
|
842
|
+
// than `timeout` to write the HTTP status and headers (corresponding to
|
|
843
|
+
// the on('response') event on the client). NB: this measures wall-clock
|
|
844
|
+
// time, not the time between bytes sent by the server.
|
|
845
|
+
self.timeoutTimer = setTimeout(function () {
|
|
846
|
+
socket.removeListener('connect', onReqSockConnect)
|
|
847
|
+
self.abort()
|
|
848
|
+
var e = new Error('ETIMEDOUT')
|
|
849
|
+
e.code = 'ETIMEDOUT'
|
|
850
|
+
e.connect = true
|
|
851
|
+
self.emit('error', e)
|
|
852
|
+
}, timeout)
|
|
853
|
+
} else {
|
|
854
|
+
// We're already connected
|
|
855
|
+
setReqTimeout()
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
self.emit('socket', socket)
|
|
859
|
+
})
|
|
860
|
+
|
|
861
|
+
self.emit('request', self.req)
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
Request.prototype.onRequestError = function (error) {
|
|
865
|
+
var self = this
|
|
866
|
+
if (self._aborted) {
|
|
867
|
+
return
|
|
868
|
+
}
|
|
869
|
+
if (self.req && self.req._reusedSocket && error.code === 'ECONNRESET' &&
|
|
870
|
+
self.agent.addRequestNoreuse) {
|
|
871
|
+
self.agent = { addRequest: self.agent.addRequestNoreuse.bind(self.agent) }
|
|
872
|
+
self.start()
|
|
873
|
+
self.req.end()
|
|
874
|
+
return
|
|
875
|
+
}
|
|
876
|
+
self.clearTimeout()
|
|
877
|
+
self.emit('error', error)
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
Request.prototype.onRequestResponse = function (response) {
|
|
881
|
+
var self = this
|
|
882
|
+
|
|
883
|
+
if (self.timing) {
|
|
884
|
+
self.timings.response = now() - self.startTimeNow
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
debug('onRequestResponse', self.uri.href, response.statusCode, response.headers)
|
|
888
|
+
response.on('end', function () {
|
|
889
|
+
if (self.timing) {
|
|
890
|
+
self.timings.end = now() - self.startTimeNow
|
|
891
|
+
response.timingStart = self.startTime
|
|
892
|
+
|
|
893
|
+
// fill in the blanks for any periods that didn't trigger, such as
|
|
894
|
+
// no lookup or connect due to keep alive
|
|
895
|
+
if (!self.timings.socket) {
|
|
896
|
+
self.timings.socket = 0
|
|
897
|
+
}
|
|
898
|
+
if (!self.timings.lookup) {
|
|
899
|
+
self.timings.lookup = self.timings.socket
|
|
900
|
+
}
|
|
901
|
+
if (!self.timings.connect) {
|
|
902
|
+
self.timings.connect = self.timings.lookup
|
|
903
|
+
}
|
|
904
|
+
if (!self.timings.response) {
|
|
905
|
+
self.timings.response = self.timings.connect
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
debug('elapsed time', self.timings.end)
|
|
909
|
+
|
|
910
|
+
// elapsedTime includes all redirects
|
|
911
|
+
self.elapsedTime += Math.round(self.timings.end)
|
|
912
|
+
|
|
913
|
+
// NOTE: elapsedTime is deprecated in favor of .timings
|
|
914
|
+
response.elapsedTime = self.elapsedTime
|
|
915
|
+
|
|
916
|
+
// timings is just for the final fetch
|
|
917
|
+
response.timings = self.timings
|
|
918
|
+
|
|
919
|
+
// pre-calculate phase timings as well
|
|
920
|
+
response.timingPhases = {
|
|
921
|
+
wait: self.timings.socket,
|
|
922
|
+
dns: self.timings.lookup - self.timings.socket,
|
|
923
|
+
tcp: self.timings.connect - self.timings.lookup,
|
|
924
|
+
firstByte: self.timings.response - self.timings.connect,
|
|
925
|
+
download: self.timings.end - self.timings.response,
|
|
926
|
+
total: self.timings.end
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
debug('response end', self.uri.href, response.statusCode, response.headers)
|
|
930
|
+
})
|
|
931
|
+
|
|
932
|
+
if (self._aborted) {
|
|
933
|
+
debug('aborted', self.uri.href)
|
|
934
|
+
response.resume()
|
|
935
|
+
return
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
self.response = response
|
|
939
|
+
response.request = self
|
|
940
|
+
response.toJSON = responseToJSON
|
|
941
|
+
|
|
942
|
+
// XXX This is different on 0.10, because SSL is strict by default
|
|
943
|
+
if (self.httpModule === https &&
|
|
944
|
+
self.strictSSL && (!response.hasOwnProperty('socket') ||
|
|
945
|
+
!response.socket.authorized)) {
|
|
946
|
+
debug('strict ssl error', self.uri.href)
|
|
947
|
+
var sslErr = response.hasOwnProperty('socket') ? response.socket.authorizationError : self.uri.href + ' does not support SSL'
|
|
948
|
+
self.emit('error', new Error('SSL Error: ' + sslErr))
|
|
949
|
+
return
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
// Save the original host before any redirect (if it changes, we need to
|
|
953
|
+
// remove any authorization headers). Also remember the case of the header
|
|
954
|
+
// name because lots of broken servers expect Host instead of host and we
|
|
955
|
+
// want the caller to be able to specify this.
|
|
956
|
+
self.originalHost = self.getHeader('host')
|
|
957
|
+
if (!self.originalHostHeaderName) {
|
|
958
|
+
self.originalHostHeaderName = self.hasHeader('host')
|
|
959
|
+
}
|
|
960
|
+
if (self.setHost) {
|
|
961
|
+
self.removeHeader('host')
|
|
962
|
+
}
|
|
963
|
+
self.clearTimeout()
|
|
964
|
+
|
|
965
|
+
var targetCookieJar = (self._jar && self._jar.setCookie) ? self._jar : globalCookieJar
|
|
966
|
+
var addCookie = function (cookie) {
|
|
967
|
+
// set the cookie if it's domain in the href's domain.
|
|
968
|
+
try {
|
|
969
|
+
targetCookieJar.setCookie(cookie, self.uri.href, {ignoreError: true})
|
|
970
|
+
} catch (e) {
|
|
971
|
+
self.emit('error', e)
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
response.caseless = caseless(response.headers)
|
|
976
|
+
|
|
977
|
+
if (response.caseless.has('set-cookie') && (!self._disableCookies)) {
|
|
978
|
+
var headerName = response.caseless.has('set-cookie')
|
|
979
|
+
if (Array.isArray(response.headers[headerName])) {
|
|
980
|
+
response.headers[headerName].forEach(addCookie)
|
|
981
|
+
} else {
|
|
982
|
+
addCookie(response.headers[headerName])
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
if (self._redirect.onResponse(response)) {
|
|
987
|
+
return // Ignore the rest of the response
|
|
988
|
+
} else {
|
|
989
|
+
// Be a good stream and emit end when the response is finished.
|
|
990
|
+
// Hack to emit end on close because of a core bug that never fires end
|
|
991
|
+
response.on('close', function () {
|
|
992
|
+
if (!self._ended) {
|
|
993
|
+
self.response.emit('end')
|
|
994
|
+
}
|
|
995
|
+
})
|
|
996
|
+
|
|
997
|
+
response.once('end', function () {
|
|
998
|
+
self._ended = true
|
|
999
|
+
})
|
|
1000
|
+
|
|
1001
|
+
var noBody = function (code) {
|
|
1002
|
+
return (
|
|
1003
|
+
self.method === 'HEAD' ||
|
|
1004
|
+
// Informational
|
|
1005
|
+
(code >= 100 && code < 200) ||
|
|
1006
|
+
// No Content
|
|
1007
|
+
code === 204 ||
|
|
1008
|
+
// Not Modified
|
|
1009
|
+
code === 304
|
|
1010
|
+
)
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
var responseContent
|
|
1014
|
+
if (self.gzip && !noBody(response.statusCode)) {
|
|
1015
|
+
var contentEncoding = response.headers['content-encoding'] || 'identity'
|
|
1016
|
+
contentEncoding = contentEncoding.trim().toLowerCase()
|
|
1017
|
+
|
|
1018
|
+
// Be more lenient with decoding compressed responses, since (very rarely)
|
|
1019
|
+
// servers send slightly invalid gzip responses that are still accepted
|
|
1020
|
+
// by common browsers.
|
|
1021
|
+
// Always using Z_SYNC_FLUSH is what cURL does.
|
|
1022
|
+
var zlibOptions = {
|
|
1023
|
+
flush: zlib.Z_SYNC_FLUSH,
|
|
1024
|
+
finishFlush: zlib.Z_SYNC_FLUSH
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
if (contentEncoding === 'gzip') {
|
|
1028
|
+
responseContent = zlib.createGunzip(zlibOptions)
|
|
1029
|
+
response.pipe(responseContent)
|
|
1030
|
+
} else if (contentEncoding === 'deflate') {
|
|
1031
|
+
responseContent = zlib.createInflate(zlibOptions)
|
|
1032
|
+
response.pipe(responseContent)
|
|
1033
|
+
} else {
|
|
1034
|
+
// Since previous versions didn't check for Content-Encoding header,
|
|
1035
|
+
// ignore any invalid values to preserve backwards-compatibility
|
|
1036
|
+
if (contentEncoding !== 'identity') {
|
|
1037
|
+
debug('ignoring unrecognized Content-Encoding ' + contentEncoding)
|
|
1038
|
+
}
|
|
1039
|
+
responseContent = response
|
|
1040
|
+
}
|
|
1041
|
+
} else {
|
|
1042
|
+
responseContent = response
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
if (self.encoding) {
|
|
1046
|
+
if (self.dests.length !== 0) {
|
|
1047
|
+
console.error('Ignoring encoding parameter as this stream is being piped to another stream which makes the encoding option invalid.')
|
|
1048
|
+
} else {
|
|
1049
|
+
responseContent.setEncoding(self.encoding)
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
if (self._paused) {
|
|
1054
|
+
responseContent.pause()
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
self.responseContent = responseContent
|
|
1058
|
+
|
|
1059
|
+
self.emit('response', response)
|
|
1060
|
+
|
|
1061
|
+
self.dests.forEach(function (dest) {
|
|
1062
|
+
self.pipeDest(dest)
|
|
1063
|
+
})
|
|
1064
|
+
|
|
1065
|
+
responseContent.on('data', function (chunk) {
|
|
1066
|
+
if (self.timing && !self.responseStarted) {
|
|
1067
|
+
self.responseStartTime = (new Date()).getTime()
|
|
1068
|
+
|
|
1069
|
+
// NOTE: responseStartTime is deprecated in favor of .timings
|
|
1070
|
+
response.responseStartTime = self.responseStartTime
|
|
1071
|
+
}
|
|
1072
|
+
self._destdata = true
|
|
1073
|
+
self.emit('data', chunk)
|
|
1074
|
+
})
|
|
1075
|
+
responseContent.once('end', function (chunk) {
|
|
1076
|
+
self.emit('end', chunk)
|
|
1077
|
+
})
|
|
1078
|
+
responseContent.on('error', function (error) {
|
|
1079
|
+
self.emit('error', error)
|
|
1080
|
+
})
|
|
1081
|
+
responseContent.on('close', function () { self.emit('close') })
|
|
1082
|
+
|
|
1083
|
+
if (self.callback) {
|
|
1084
|
+
self.readResponseBody(response)
|
|
1085
|
+
} else { // if no callback
|
|
1086
|
+
self.on('end', function () {
|
|
1087
|
+
if (self._aborted) {
|
|
1088
|
+
debug('aborted', self.uri.href)
|
|
1089
|
+
return
|
|
1090
|
+
}
|
|
1091
|
+
self.emit('complete', response)
|
|
1092
|
+
})
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
debug('finish init function', self.uri.href)
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
Request.prototype.readResponseBody = function (response) {
|
|
1099
|
+
var self = this
|
|
1100
|
+
debug("reading response's body")
|
|
1101
|
+
var buffers = []
|
|
1102
|
+
var bufferLength = 0
|
|
1103
|
+
var strings = []
|
|
1104
|
+
|
|
1105
|
+
self.on('data', function (chunk) {
|
|
1106
|
+
if (!Buffer.isBuffer(chunk)) {
|
|
1107
|
+
strings.push(chunk)
|
|
1108
|
+
} else if (chunk.length) {
|
|
1109
|
+
bufferLength += chunk.length
|
|
1110
|
+
buffers.push(chunk)
|
|
1111
|
+
}
|
|
1112
|
+
})
|
|
1113
|
+
self.on('end', function () {
|
|
1114
|
+
debug('end event', self.uri.href)
|
|
1115
|
+
if (self._aborted) {
|
|
1116
|
+
debug('aborted', self.uri.href)
|
|
1117
|
+
// `buffer` is defined in the parent scope and used in a closure it exists for the life of the request.
|
|
1118
|
+
// This can lead to leaky behavior if the user retains a reference to the request object.
|
|
1119
|
+
buffers = []
|
|
1120
|
+
bufferLength = 0
|
|
1121
|
+
return
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
if (bufferLength) {
|
|
1125
|
+
debug('has body', self.uri.href, bufferLength)
|
|
1126
|
+
response.body = Buffer.concat(buffers, bufferLength)
|
|
1127
|
+
if (self.encoding !== null) {
|
|
1128
|
+
response.body = response.body.toString(self.encoding)
|
|
1129
|
+
}
|
|
1130
|
+
// `buffer` is defined in the parent scope and used in a closure it exists for the life of the Request.
|
|
1131
|
+
// This can lead to leaky behavior if the user retains a reference to the request object.
|
|
1132
|
+
buffers = []
|
|
1133
|
+
bufferLength = 0
|
|
1134
|
+
} else if (strings.length) {
|
|
1135
|
+
// The UTF8 BOM [0xEF,0xBB,0xBF] is converted to [0xFE,0xFF] in the JS UTC16/UCS2 representation.
|
|
1136
|
+
// Strip this value out when the encoding is set to 'utf8', as upstream consumers won't expect it and it breaks JSON.parse().
|
|
1137
|
+
if (self.encoding === 'utf8' && strings[0].length > 0 && strings[0][0] === '\uFEFF') {
|
|
1138
|
+
strings[0] = strings[0].substring(1)
|
|
1139
|
+
}
|
|
1140
|
+
response.body = strings.join('')
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
if (self._json) {
|
|
1144
|
+
try {
|
|
1145
|
+
response.body = JSON.parse(response.body, self._jsonReviver)
|
|
1146
|
+
} catch (e) {
|
|
1147
|
+
debug('invalid JSON received', self.uri.href)
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
debug('emitting complete', self.uri.href)
|
|
1151
|
+
if (typeof response.body === 'undefined' && !self._json) {
|
|
1152
|
+
response.body = self.encoding === null ? Buffer.alloc(0) : ''
|
|
1153
|
+
}
|
|
1154
|
+
self.emit('complete', response, response.body)
|
|
1155
|
+
})
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
Request.prototype.abort = function () {
|
|
1159
|
+
var self = this
|
|
1160
|
+
self._aborted = true
|
|
1161
|
+
|
|
1162
|
+
if (self.req) {
|
|
1163
|
+
self.req.abort()
|
|
1164
|
+
} else if (self.response) {
|
|
1165
|
+
self.response.destroy()
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
self.clearTimeout()
|
|
1169
|
+
self.emit('abort')
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
Request.prototype.pipeDest = function (dest) {
|
|
1173
|
+
var self = this
|
|
1174
|
+
var response = self.response
|
|
1175
|
+
// Called after the response is received
|
|
1176
|
+
if (dest.headers && !dest.headersSent) {
|
|
1177
|
+
if (response.caseless.has('content-type')) {
|
|
1178
|
+
var ctname = response.caseless.has('content-type')
|
|
1179
|
+
if (dest.setHeader) {
|
|
1180
|
+
dest.setHeader(ctname, response.headers[ctname])
|
|
1181
|
+
} else {
|
|
1182
|
+
dest.headers[ctname] = response.headers[ctname]
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
if (response.caseless.has('content-length')) {
|
|
1187
|
+
var clname = response.caseless.has('content-length')
|
|
1188
|
+
if (dest.setHeader) {
|
|
1189
|
+
dest.setHeader(clname, response.headers[clname])
|
|
1190
|
+
} else {
|
|
1191
|
+
dest.headers[clname] = response.headers[clname]
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
if (dest.setHeader && !dest.headersSent) {
|
|
1196
|
+
for (var i in response.headers) {
|
|
1197
|
+
// If the response content is being decoded, the Content-Encoding header
|
|
1198
|
+
// of the response doesn't represent the piped content, so don't pass it.
|
|
1199
|
+
if (!self.gzip || i !== 'content-encoding') {
|
|
1200
|
+
dest.setHeader(i, response.headers[i])
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
dest.statusCode = response.statusCode
|
|
1204
|
+
}
|
|
1205
|
+
if (self.pipefilter) {
|
|
1206
|
+
self.pipefilter(response, dest)
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
Request.prototype.qs = function (q, clobber) {
|
|
1211
|
+
var self = this
|
|
1212
|
+
var base
|
|
1213
|
+
if (!clobber && self.uri.query) {
|
|
1214
|
+
base = self._qs.parse(self.uri.query)
|
|
1215
|
+
} else {
|
|
1216
|
+
base = {}
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
for (var i in q) {
|
|
1220
|
+
base[i] = q[i]
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
var qs = self._qs.stringify(base)
|
|
1224
|
+
|
|
1225
|
+
if (qs === '') {
|
|
1226
|
+
return self
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
self.uri = url.parse(self.uri.href.split('?')[0] + '?' + qs)
|
|
1230
|
+
self.url = self.uri
|
|
1231
|
+
self.path = self.uri.path
|
|
1232
|
+
|
|
1233
|
+
if (self.uri.host === 'unix') {
|
|
1234
|
+
self.enableUnixSocket()
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
return self
|
|
1238
|
+
}
|
|
1239
|
+
Request.prototype.form = function (form) {
|
|
1240
|
+
var self = this
|
|
1241
|
+
if (form) {
|
|
1242
|
+
if (!/^application\/x-www-form-urlencoded\b/.test(self.getHeader('content-type'))) {
|
|
1243
|
+
self.setHeader('content-type', 'application/x-www-form-urlencoded')
|
|
1244
|
+
}
|
|
1245
|
+
self.body = (typeof form === 'string')
|
|
1246
|
+
? self._qs.rfc3986(form.toString('utf8'))
|
|
1247
|
+
: self._qs.stringify(form).toString('utf8')
|
|
1248
|
+
return self
|
|
1249
|
+
}
|
|
1250
|
+
// create form-data object
|
|
1251
|
+
self._form = new FormData()
|
|
1252
|
+
self._form.on('error', function (err) {
|
|
1253
|
+
err.message = 'form-data: ' + err.message
|
|
1254
|
+
self.emit('error', err)
|
|
1255
|
+
self.abort()
|
|
1256
|
+
})
|
|
1257
|
+
return self._form
|
|
1258
|
+
}
|
|
1259
|
+
Request.prototype.multipart = function (multipart) {
|
|
1260
|
+
var self = this
|
|
1261
|
+
|
|
1262
|
+
self._multipart.onRequest(multipart)
|
|
1263
|
+
|
|
1264
|
+
if (!self._multipart.chunked) {
|
|
1265
|
+
self.body = self._multipart.body
|
|
1266
|
+
}
|
|
1267
|
+
|
|
1268
|
+
return self
|
|
1269
|
+
}
|
|
1270
|
+
Request.prototype.json = function (val) {
|
|
1271
|
+
var self = this
|
|
1272
|
+
|
|
1273
|
+
if (!self.hasHeader('accept')) {
|
|
1274
|
+
self.setHeader('accept', 'application/json')
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1277
|
+
if (typeof self.jsonReplacer === 'function') {
|
|
1278
|
+
self._jsonReplacer = self.jsonReplacer
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
self._json = true
|
|
1282
|
+
if (typeof val === 'boolean') {
|
|
1283
|
+
if (self.body !== undefined) {
|
|
1284
|
+
if (!/^application\/x-www-form-urlencoded\b/.test(self.getHeader('content-type'))) {
|
|
1285
|
+
self.body = safeStringify(self.body, self._jsonReplacer)
|
|
1286
|
+
} else {
|
|
1287
|
+
self.body = self._qs.rfc3986(self.body)
|
|
1288
|
+
}
|
|
1289
|
+
if (!self.hasHeader('content-type')) {
|
|
1290
|
+
self.setHeader('content-type', 'application/json')
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1293
|
+
} else {
|
|
1294
|
+
self.body = safeStringify(val, self._jsonReplacer)
|
|
1295
|
+
if (!self.hasHeader('content-type')) {
|
|
1296
|
+
self.setHeader('content-type', 'application/json')
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
if (typeof self.jsonReviver === 'function') {
|
|
1301
|
+
self._jsonReviver = self.jsonReviver
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
return self
|
|
1305
|
+
}
|
|
1306
|
+
Request.prototype.getHeader = function (name, headers) {
|
|
1307
|
+
var self = this
|
|
1308
|
+
var result, re, match
|
|
1309
|
+
if (!headers) {
|
|
1310
|
+
headers = self.headers
|
|
1311
|
+
}
|
|
1312
|
+
Object.keys(headers).forEach(function (key) {
|
|
1313
|
+
if (key.length !== name.length) {
|
|
1314
|
+
return
|
|
1315
|
+
}
|
|
1316
|
+
re = new RegExp(name, 'i')
|
|
1317
|
+
match = key.match(re)
|
|
1318
|
+
if (match) {
|
|
1319
|
+
result = headers[key]
|
|
1320
|
+
}
|
|
1321
|
+
})
|
|
1322
|
+
return result
|
|
1323
|
+
}
|
|
1324
|
+
Request.prototype.enableUnixSocket = function () {
|
|
1325
|
+
// Get the socket & request paths from the URL
|
|
1326
|
+
var unixParts = this.uri.path.split(':')
|
|
1327
|
+
var host = unixParts[0]
|
|
1328
|
+
var path = unixParts[1]
|
|
1329
|
+
// Apply unix properties to request
|
|
1330
|
+
this.socketPath = host
|
|
1331
|
+
this.uri.pathname = path
|
|
1332
|
+
this.uri.path = path
|
|
1333
|
+
this.uri.host = host
|
|
1334
|
+
this.uri.hostname = host
|
|
1335
|
+
this.uri.isUnix = true
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1338
|
+
Request.prototype.auth = function (user, pass, sendImmediately, bearer) {
|
|
1339
|
+
var self = this
|
|
1340
|
+
|
|
1341
|
+
self._auth.onRequest(user, pass, sendImmediately, bearer)
|
|
1342
|
+
|
|
1343
|
+
return self
|
|
1344
|
+
}
|
|
1345
|
+
Request.prototype.aws = function (opts, now) {
|
|
1346
|
+
var self = this
|
|
1347
|
+
|
|
1348
|
+
if (!now) {
|
|
1349
|
+
self._aws = opts
|
|
1350
|
+
return self
|
|
1351
|
+
}
|
|
1352
|
+
|
|
1353
|
+
if (opts.sign_version === 4 || opts.sign_version === '4') {
|
|
1354
|
+
// use aws4
|
|
1355
|
+
var options = {
|
|
1356
|
+
host: self.uri.host,
|
|
1357
|
+
path: self.uri.path,
|
|
1358
|
+
method: self.method,
|
|
1359
|
+
headers: self.headers,
|
|
1360
|
+
body: self.body
|
|
1361
|
+
}
|
|
1362
|
+
if (opts.service) {
|
|
1363
|
+
options.service = opts.service
|
|
1364
|
+
}
|
|
1365
|
+
var signRes = aws4.sign(options, {
|
|
1366
|
+
accessKeyId: opts.key,
|
|
1367
|
+
secretAccessKey: opts.secret,
|
|
1368
|
+
sessionToken: opts.session
|
|
1369
|
+
})
|
|
1370
|
+
self.setHeader('authorization', signRes.headers.Authorization)
|
|
1371
|
+
self.setHeader('x-amz-date', signRes.headers['X-Amz-Date'])
|
|
1372
|
+
if (signRes.headers['X-Amz-Security-Token']) {
|
|
1373
|
+
self.setHeader('x-amz-security-token', signRes.headers['X-Amz-Security-Token'])
|
|
1374
|
+
}
|
|
1375
|
+
} else {
|
|
1376
|
+
// default: use aws-sign2
|
|
1377
|
+
var date = new Date()
|
|
1378
|
+
self.setHeader('date', date.toUTCString())
|
|
1379
|
+
var auth = {
|
|
1380
|
+
key: opts.key,
|
|
1381
|
+
secret: opts.secret,
|
|
1382
|
+
verb: self.method.toUpperCase(),
|
|
1383
|
+
date: date,
|
|
1384
|
+
contentType: self.getHeader('content-type') || '',
|
|
1385
|
+
md5: self.getHeader('content-md5') || '',
|
|
1386
|
+
amazonHeaders: aws2.canonicalizeHeaders(self.headers)
|
|
1387
|
+
}
|
|
1388
|
+
var path = self.uri.path
|
|
1389
|
+
if (opts.bucket && path) {
|
|
1390
|
+
auth.resource = '/' + opts.bucket + path
|
|
1391
|
+
} else if (opts.bucket && !path) {
|
|
1392
|
+
auth.resource = '/' + opts.bucket
|
|
1393
|
+
} else if (!opts.bucket && path) {
|
|
1394
|
+
auth.resource = path
|
|
1395
|
+
} else if (!opts.bucket && !path) {
|
|
1396
|
+
auth.resource = '/'
|
|
1397
|
+
}
|
|
1398
|
+
auth.resource = aws2.canonicalizeResource(auth.resource)
|
|
1399
|
+
self.setHeader('authorization', aws2.authorization(auth))
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1402
|
+
return self
|
|
1403
|
+
}
|
|
1404
|
+
Request.prototype.httpSignature = function (opts) {
|
|
1405
|
+
var self = this
|
|
1406
|
+
httpSignature.signRequest({
|
|
1407
|
+
getHeader: function (header) {
|
|
1408
|
+
return self.getHeader(header, self.headers)
|
|
1409
|
+
},
|
|
1410
|
+
setHeader: function (header, value) {
|
|
1411
|
+
self.setHeader(header, value)
|
|
1412
|
+
},
|
|
1413
|
+
method: self.method,
|
|
1414
|
+
path: self.path
|
|
1415
|
+
}, opts)
|
|
1416
|
+
debug('httpSignature authorization', self.getHeader('authorization'))
|
|
1417
|
+
|
|
1418
|
+
return self
|
|
1419
|
+
}
|
|
1420
|
+
Request.prototype.hawk = function (opts) {
|
|
1421
|
+
var self = this
|
|
1422
|
+
self.setHeader('Authorization', hawk.header(self.uri, self.method, opts))
|
|
1423
|
+
}
|
|
1424
|
+
Request.prototype.oauth = function (_oauth) {
|
|
1425
|
+
var self = this
|
|
1426
|
+
|
|
1427
|
+
self._oauth.onRequest(_oauth)
|
|
1428
|
+
|
|
1429
|
+
return self
|
|
1430
|
+
}
|
|
1431
|
+
|
|
1432
|
+
Request.prototype.jar = function (jar) {
|
|
1433
|
+
var self = this
|
|
1434
|
+
var cookies
|
|
1435
|
+
|
|
1436
|
+
if (self._redirect.redirectsFollowed === 0) {
|
|
1437
|
+
self.originalCookieHeader = self.getHeader('cookie')
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1440
|
+
if (!jar) {
|
|
1441
|
+
// disable cookies
|
|
1442
|
+
cookies = false
|
|
1443
|
+
self._disableCookies = true
|
|
1444
|
+
} else {
|
|
1445
|
+
var targetCookieJar = jar.getCookieString ? jar : globalCookieJar
|
|
1446
|
+
var urihref = self.uri.href
|
|
1447
|
+
// fetch cookie in the Specified host
|
|
1448
|
+
if (targetCookieJar) {
|
|
1449
|
+
cookies = targetCookieJar.getCookieString(urihref)
|
|
1450
|
+
}
|
|
1451
|
+
}
|
|
1452
|
+
|
|
1453
|
+
// if need cookie and cookie is not empty
|
|
1454
|
+
if (cookies && cookies.length) {
|
|
1455
|
+
if (self.originalCookieHeader) {
|
|
1456
|
+
// Don't overwrite existing Cookie header
|
|
1457
|
+
self.setHeader('cookie', self.originalCookieHeader + '; ' + cookies)
|
|
1458
|
+
} else {
|
|
1459
|
+
self.setHeader('cookie', cookies)
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
self._jar = jar
|
|
1463
|
+
return self
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1466
|
+
// Stream API
|
|
1467
|
+
Request.prototype.pipe = function (dest, opts) {
|
|
1468
|
+
var self = this
|
|
1469
|
+
|
|
1470
|
+
if (self.response) {
|
|
1471
|
+
if (self._destdata) {
|
|
1472
|
+
self.emit('error', new Error('You cannot pipe after data has been emitted from the response.'))
|
|
1473
|
+
} else if (self._ended) {
|
|
1474
|
+
self.emit('error', new Error('You cannot pipe after the response has been ended.'))
|
|
1475
|
+
} else {
|
|
1476
|
+
stream.Stream.prototype.pipe.call(self, dest, opts)
|
|
1477
|
+
self.pipeDest(dest)
|
|
1478
|
+
return dest
|
|
1479
|
+
}
|
|
1480
|
+
} else {
|
|
1481
|
+
self.dests.push(dest)
|
|
1482
|
+
stream.Stream.prototype.pipe.call(self, dest, opts)
|
|
1483
|
+
return dest
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
Request.prototype.write = function () {
|
|
1487
|
+
var self = this
|
|
1488
|
+
if (self._aborted) { return }
|
|
1489
|
+
|
|
1490
|
+
if (!self._started) {
|
|
1491
|
+
self.start()
|
|
1492
|
+
}
|
|
1493
|
+
if (self.req) {
|
|
1494
|
+
return self.req.write.apply(self.req, arguments)
|
|
1495
|
+
}
|
|
1496
|
+
}
|
|
1497
|
+
Request.prototype.end = function (chunk) {
|
|
1498
|
+
var self = this
|
|
1499
|
+
if (self._aborted) { return }
|
|
1500
|
+
|
|
1501
|
+
if (chunk) {
|
|
1502
|
+
self.write(chunk)
|
|
1503
|
+
}
|
|
1504
|
+
if (!self._started) {
|
|
1505
|
+
self.start()
|
|
1506
|
+
}
|
|
1507
|
+
if (self.req) {
|
|
1508
|
+
self.req.end()
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
Request.prototype.pause = function () {
|
|
1512
|
+
var self = this
|
|
1513
|
+
if (!self.responseContent) {
|
|
1514
|
+
self._paused = true
|
|
1515
|
+
} else {
|
|
1516
|
+
self.responseContent.pause.apply(self.responseContent, arguments)
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
Request.prototype.resume = function () {
|
|
1520
|
+
var self = this
|
|
1521
|
+
if (!self.responseContent) {
|
|
1522
|
+
self._paused = false
|
|
1523
|
+
} else {
|
|
1524
|
+
self.responseContent.resume.apply(self.responseContent, arguments)
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
Request.prototype.destroy = function () {
|
|
1528
|
+
var self = this
|
|
1529
|
+
this.clearTimeout()
|
|
1530
|
+
if (!self._ended) {
|
|
1531
|
+
self.end()
|
|
1532
|
+
} else if (self.response) {
|
|
1533
|
+
self.response.destroy()
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
|
|
1537
|
+
Request.prototype.clearTimeout = function () {
|
|
1538
|
+
if (this.timeoutTimer) {
|
|
1539
|
+
clearTimeout(this.timeoutTimer)
|
|
1540
|
+
this.timeoutTimer = null
|
|
1541
|
+
}
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1544
|
+
Request.defaultProxyHeaderWhiteList =
|
|
1545
|
+
Tunnel.defaultProxyHeaderWhiteList.slice()
|
|
1546
|
+
|
|
1547
|
+
Request.defaultProxyHeaderExclusiveList =
|
|
1548
|
+
Tunnel.defaultProxyHeaderExclusiveList.slice()
|
|
1549
|
+
|
|
1550
|
+
// Exports
|
|
1551
|
+
|
|
1552
|
+
Request.prototype.toJSON = requestToJSON
|
|
1553
|
+
module.exports = Request
|