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