Tizen 2.0 Release
[platform/framework/web/web-ui-fw.git] / libs / js / jquery-mobile-1.2.0 / node_modules / testswarm / node_modules / request / main.js
1 // Copyright 2010-2012 Mikeal Rogers
2 //
3 //    Licensed under the Apache License, Version 2.0 (the "License");
4 //    you may not use this file except in compliance with the License.
5 //    You may obtain a copy of the License at
6 //
7 //        http://www.apache.org/licenses/LICENSE-2.0
8 //
9 //    Unless required by applicable law or agreed to in writing, software
10 //    distributed under the License is distributed on an "AS IS" BASIS,
11 //    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 //    See the License for the specific language governing permissions and
13 //    limitations under the License.
14
15 var http = require('http')
16   , https = false
17   , tls = false
18   , url = require('url')
19   , util = require('util')
20   , stream = require('stream')
21   , qs = require('querystring')
22   , oauth = require('./oauth')
23   , uuid = require('./uuid')
24   , ForeverAgent = require('./forever')
25   , Cookie = require('./vendor/cookie')
26   , CookieJar = require('./vendor/cookie/jar')
27   , cookieJar = new CookieJar
28   , tunnel = require('./tunnel')
29   , aws = require('./aws')
30   
31   , mime = require('mime')
32   , FormData = require('form-data')
33   ;
34   
35 if (process.logging) {
36   var log = process.logging('request')
37 }
38
39 try {
40   https = require('https')
41 } catch (e) {}
42
43 try {
44   tls = require('tls')
45 } catch (e) {}
46
47 function toBase64 (str) {
48   return (new Buffer(str || "", "ascii")).toString("base64")
49 }
50
51 // Hacky fix for pre-0.4.4 https
52 if (https && !https.Agent) {
53   https.Agent = function (options) {
54     http.Agent.call(this, options)
55   }
56   util.inherits(https.Agent, http.Agent)
57   https.Agent.prototype._getConnection = function (host, port, cb) {
58     var s = tls.connect(port, host, this.options, function () {
59       // do other checks here?
60       if (cb) cb()
61     })
62     return s
63   }
64 }
65
66 function isReadStream (rs) {
67   if (rs.readable && rs.path && rs.mode) {
68     return true
69   }
70 }
71
72 function copy (obj) {
73   var o = {}
74   Object.keys(obj).forEach(function (i) {
75     o[i] = obj[i]
76   })
77   return o
78 }
79
80 var isUrl = /^https?:/
81
82 var globalPool = {}
83
84 function Request (options) {
85   stream.Stream.call(this)
86   this.readable = true
87   this.writable = true
88
89   if (typeof options === 'string') {
90     options = {uri:options}
91   }
92   
93   var reserved = Object.keys(Request.prototype)
94   for (var i in options) {
95     if (reserved.indexOf(i) === -1) {
96       this[i] = options[i]
97     } else {
98       if (typeof options[i] === 'function') {
99         delete options[i]
100       }
101     }
102   }
103   options = copy(options)
104   
105   this.init(options)
106 }
107 util.inherits(Request, stream.Stream)
108 Request.prototype.init = function (options) {
109   var self = this
110   
111   if (!options) options = {}
112   if (process.env.NODE_DEBUG && /request/.test(process.env.NODE_DEBUG)) console.error('REQUEST', options)
113   if (!self.pool && self.pool !== false) self.pool = globalPool
114   self.dests = []
115   self.__isRequestRequest = true
116   
117   // Protect against double callback
118   if (!self._callback && self.callback) {
119     self._callback = self.callback
120     self.callback = function () {
121       if (self._callbackCalled) return // Print a warning maybe?
122       self._callback.apply(self, arguments)
123       self._callbackCalled = true
124     }
125     self.on('error', self.callback.bind())
126     self.on('complete', self.callback.bind(self, null))
127   }
128
129   if (self.url) {
130     // People use this property instead all the time so why not just support it.
131     self.uri = self.url
132     delete self.url
133   }
134
135   if (!self.uri) {
136     // this will throw if unhandled but is handleable when in a redirect
137     return self.emit('error', new Error("options.uri is a required argument"))
138   } else {
139     if (typeof self.uri == "string") self.uri = url.parse(self.uri)
140   }
141   if (self.proxy) {
142     if (typeof self.proxy == 'string') self.proxy = url.parse(self.proxy)
143
144     // do the HTTP CONNECT dance using koichik/node-tunnel
145     if (http.globalAgent && self.uri.protocol === "https:") {
146       var tunnelFn = self.proxy.protocol === "http:"
147                    ? tunnel.httpsOverHttp : tunnel.httpsOverHttps
148
149       var tunnelOptions = { proxy: { host: self.proxy.hostname
150                                    , port: +self.proxy.port
151                                    , proxyAuth: self.proxy.auth }
152                           , ca: this.ca }
153
154       self.agent = tunnelFn(tunnelOptions)
155       self.tunnel = true
156     }
157   }
158
159   if (!self.uri.host || !self.uri.pathname) {
160     // Invalid URI: it may generate lot of bad errors, like "TypeError: Cannot call method 'indexOf' of undefined" in CookieJar
161     // Detect and reject it as soon as possible
162     var faultyUri = url.format(self.uri)
163     var message = 'Invalid URI "' + faultyUri + '"'
164     if (Object.keys(options).length === 0) {
165       // No option ? This can be the sign of a redirect
166       // As this is a case where the user cannot do anything (he didn't call request directly with this URL)
167       // he should be warned that it can be caused by a redirection (can save some hair)
168       message += '. This can be caused by a crappy redirection.'
169     }
170     self.emit('error', new Error(message))
171     return // This error was fatal
172   }
173
174   self._redirectsFollowed = self._redirectsFollowed || 0
175   self.maxRedirects = (self.maxRedirects !== undefined) ? self.maxRedirects : 10
176   self.followRedirect = (self.followRedirect !== undefined) ? self.followRedirect : true
177   self.followAllRedirects = (self.followAllRedirects !== undefined) ? self.followAllRedirects : false
178   if (self.followRedirect || self.followAllRedirects)
179     self.redirects = self.redirects || []
180
181   self.headers = self.headers ? copy(self.headers) : {}
182
183   self.setHost = false
184   if (!self.headers.host) {
185     self.headers.host = self.uri.hostname
186     if (self.uri.port) {
187       if ( !(self.uri.port === 80 && self.uri.protocol === 'http:') &&
188            !(self.uri.port === 443 && self.uri.protocol === 'https:') )
189       self.headers.host += (':'+self.uri.port)
190     }
191     self.setHost = true
192   }
193   
194   self.jar(self._jar || options.jar)
195
196   if (!self.uri.pathname) {self.uri.pathname = '/'}
197   if (!self.uri.port) {
198     if (self.uri.protocol == 'http:') {self.uri.port = 80}
199     else if (self.uri.protocol == 'https:') {self.uri.port = 443}
200   }
201
202   if (self.proxy && !self.tunnel) {
203     self.port = self.proxy.port
204     self.host = self.proxy.hostname
205   } else {
206     self.port = self.uri.port
207     self.host = self.uri.hostname
208   }
209
210   self.clientErrorHandler = function (error) {
211     if (self._aborted) return
212     
213     if (self.setHost) delete self.headers.host
214     if (self.req._reusedSocket && error.code === 'ECONNRESET'
215         && self.agent.addRequestNoreuse) {
216       self.agent = { addRequest: self.agent.addRequestNoreuse.bind(self.agent) }
217       self.start()
218       self.req.end()
219       return
220     }
221     if (self.timeout && self.timeoutTimer) {
222       clearTimeout(self.timeoutTimer)
223       self.timeoutTimer = null
224     }
225     self.emit('error', error)
226   }
227
228   self._parserErrorHandler = function (error) {
229     if (this.res) {
230       if (this.res.request) {
231         this.res.request.emit('error', error)
232       } else {
233         this.res.emit('error', error)
234       }
235     } else {
236       this._httpMessage.emit('error', error)
237     }
238   }
239
240   if (options.form) {
241     self.form(options.form)
242   }
243
244   if (options.oauth) {
245     self.oauth(options.oauth)
246   }
247   
248   if (options.aws) {
249     self.aws(options.aws)
250   }
251
252   if (self.uri.auth && !self.headers.authorization) {
253     self.headers.authorization = "Basic " + toBase64(self.uri.auth.split(':').map(function(item){ return qs.unescape(item)}).join(':'))
254   }
255   if (self.proxy && self.proxy.auth && !self.headers['proxy-authorization'] && !self.tunnel) {
256     self.headers['proxy-authorization'] = "Basic " + toBase64(self.proxy.auth.split(':').map(function(item){ return qs.unescape(item)}).join(':'))
257   }
258
259   if (options.qs) self.qs(options.qs)
260
261   if (self.uri.path) {
262     self.path = self.uri.path
263   } else {
264     self.path = self.uri.pathname + (self.uri.search || "")
265   }
266
267   if (self.path.length === 0) self.path = '/'
268
269   if (self.proxy && !self.tunnel) self.path = (self.uri.protocol + '//' + self.uri.host + self.path)
270
271   if (options.json) {
272     self.json(options.json)
273   } else if (options.multipart) {
274     self.boundary = uuid()
275     self.multipart(options.multipart)
276   }
277
278   if (self.body) {
279     var length = 0
280     if (!Buffer.isBuffer(self.body)) {
281       if (Array.isArray(self.body)) {
282         for (var i = 0; i < self.body.length; i++) {
283           length += self.body[i].length
284         }
285       } else {
286         self.body = new Buffer(self.body)
287         length = self.body.length
288       }
289     } else {
290       length = self.body.length
291     }
292     if (length) {
293       if(!self.headers['content-length'] && !self.headers['Content-Length'])
294       self.headers['content-length'] = length
295     } else {
296       throw new Error('Argument error, options.body.')
297     }
298   }
299
300   var protocol = self.proxy && !self.tunnel ? self.proxy.protocol : self.uri.protocol
301     , defaultModules = {'http:':http, 'https:':https}
302     , httpModules = self.httpModules || {}
303     ;
304   self.httpModule = httpModules[protocol] || defaultModules[protocol]
305
306   if (!self.httpModule) return this.emit('error', new Error("Invalid protocol"))
307
308   if (options.ca) self.ca = options.ca
309
310   if (!self.agent) {
311     if (options.agentOptions) self.agentOptions = options.agentOptions
312
313     if (options.agentClass) {
314       self.agentClass = options.agentClass
315     } else if (options.forever) {
316       self.agentClass = protocol === 'http:' ? ForeverAgent : ForeverAgent.SSL
317     } else {
318       self.agentClass = self.httpModule.Agent
319     }
320   }
321
322   if (self.pool === false) {
323     self.agent = false
324   } else {
325     self.agent = self.agent || self.getAgent()
326     if (self.maxSockets) {
327       // Don't use our pooling if node has the refactored client
328       self.agent.maxSockets = self.maxSockets
329     }
330     if (self.pool.maxSockets) {
331       // Don't use our pooling if node has the refactored client
332       self.agent.maxSockets = self.pool.maxSockets
333     }
334   }
335
336   self.once('pipe', function (src) {
337     if (self.ntick && self._started) throw new Error("You cannot pipe to this stream after the outbound request has started.")
338     self.src = src
339     if (isReadStream(src)) {
340       if (!self.headers['content-type'] && !self.headers['Content-Type'])
341         self.headers['content-type'] = mime.lookup(src.path)
342     } else {
343       if (src.headers) {
344         for (var i in src.headers) {
345           if (!self.headers[i]) {
346             self.headers[i] = src.headers[i]
347           }
348         }
349       }
350       if (self._json && !self.headers['content-type'] && !self.headers['Content-Type'])
351         self.headers['content-type'] = 'application/json'
352       if (src.method && !self.method) {
353         self.method = src.method
354       }
355     }
356
357     self.on('pipe', function () {
358       console.error("You have already piped to this stream. Pipeing twice is likely to break the request.")
359     })
360   })
361
362   process.nextTick(function () {
363     if (self._aborted) return
364     
365     if (self._form) {
366       self.setHeaders(self._form.getHeaders())
367       self._form.pipe(self)
368     }
369     if (self.body) {
370       if (Array.isArray(self.body)) {
371         self.body.forEach(function (part) {
372           self.write(part)
373         })
374       } else {
375         self.write(self.body)
376       }
377       self.end()
378     } else if (self.requestBodyStream) {
379       console.warn("options.requestBodyStream is deprecated, please pass the request object to stream.pipe.")
380       self.requestBodyStream.pipe(self)
381     } else if (!self.src) {
382       if (self.method !== 'GET' && typeof self.method !== 'undefined') {
383         self.headers['content-length'] = 0
384       }
385       self.end()
386     }
387     self.ntick = true
388   })
389 }
390
391 // Must call this when following a redirect from https to http or vice versa
392 // Attempts to keep everything as identical as possible, but update the
393 // httpModule, Tunneling agent, and/or Forever Agent in use.
394 Request.prototype._updateProtocol = function () {
395   var self = this
396   var protocol = self.uri.protocol
397
398   if (protocol === 'https:') {
399     // previously was doing http, now doing https
400     // if it's https, then we might need to tunnel now.
401     if (self.proxy) {
402       self.tunnel = true
403       var tunnelFn = self.proxy.protocol === 'http:'
404                    ? tunnel.httpsOverHttp : tunnel.httpsOverHttps
405       var tunnelOptions = { proxy: { host: self.proxy.hostname
406                                    , post: +self.proxy.port
407                                    , proxyAuth: self.proxy.auth }
408                           , ca: self.ca }
409       self.agent = tunnelFn(tunnelOptions)
410       return
411     }
412
413     self.httpModule = https
414     switch (self.agentClass) {
415       case ForeverAgent:
416         self.agentClass = ForeverAgent.SSL
417         break
418       case http.Agent:
419         self.agentClass = https.Agent
420         break
421       default:
422         // nothing we can do.  Just hope for the best.
423         return
424     }
425
426     // if there's an agent, we need to get a new one.
427     if (self.agent) self.agent = self.getAgent()
428
429   } else {
430     if (log) log('previously https, now http')
431     // previously was doing https, now doing http
432     // stop any tunneling.
433     if (self.tunnel) self.tunnel = false
434     self.httpModule = http
435     switch (self.agentClass) {
436       case ForeverAgent.SSL:
437         self.agentClass = ForeverAgent
438         break
439       case https.Agent:
440         self.agentClass = http.Agent
441         break
442       default:
443         // nothing we can do.  just hope for the best
444         return
445     }
446
447     // if there's an agent, then get a new one.
448     if (self.agent) {
449       self.agent = null
450       self.agent = self.getAgent()
451     }
452   }
453 }
454
455 Request.prototype.getAgent = function () {
456   var Agent = this.agentClass
457   var options = {}
458   if (this.agentOptions) {
459     for (var i in this.agentOptions) {
460       options[i] = this.agentOptions[i]
461     }
462   }
463   if (this.ca) options.ca = this.ca
464
465   var poolKey = ''
466
467   // different types of agents are in different pools
468   if (Agent !== this.httpModule.Agent) {
469     poolKey += Agent.name
470   }
471
472   if (!this.httpModule.globalAgent) {
473     // node 0.4.x
474     options.host = this.host
475     options.port = this.port
476     if (poolKey) poolKey += ':'
477     poolKey += this.host + ':' + this.port
478   }
479
480   // ca option is only relevant if proxy or destination are https
481   var proxy = this.proxy
482   if (typeof proxy === 'string') proxy = url.parse(proxy)
483   var caRelevant = (proxy && proxy.protocol === 'https:') || this.uri.protocol === 'https:'
484   if (options.ca && caRelevant) {
485     if (poolKey) poolKey += ':'
486     poolKey += options.ca
487   }
488
489   if (!poolKey && Agent === this.httpModule.Agent && this.httpModule.globalAgent) {
490     // not doing anything special.  Use the globalAgent
491     return this.httpModule.globalAgent
492   }
493
494   // we're using a stored agent.  Make sure it's protocol-specific
495   poolKey = this.uri.protocol + poolKey
496
497   // already generated an agent for this setting
498   if (this.pool[poolKey]) return this.pool[poolKey]
499
500   return this.pool[poolKey] = new Agent(options)
501 }
502
503 Request.prototype.start = function () {
504   var self = this
505
506   if (self._aborted) return
507
508   self._started = true
509   self.method = self.method || 'GET'
510   self.href = self.uri.href
511   if (log) log('%method %href', self)
512
513   if (self.src && self.src.stat && self.src.stat.size && !self.headers['content-length'] && !self.headers['Content-Length']) {
514     self.headers['content-length'] = self.src.stat.size
515   }
516   if (self._aws) {
517     self.aws(self._aws, true)
518   }
519   self.req = self.httpModule.request(self, function (response) {
520     if (response.connection.listeners('error').indexOf(self._parserErrorHandler) === -1) {
521       response.connection.once('error', self._parserErrorHandler)
522     }
523     if (self._aborted) return
524     if (self._paused) response.pause()
525
526     self.response = response
527     response.request = self
528     response.toJSON = toJSON
529
530     if (self.httpModule === https &&
531         self.strictSSL &&
532         !response.client.authorized) {
533       var sslErr = response.client.authorizationError
534       self.emit('error', new Error('SSL Error: '+ sslErr))
535       return
536     }
537
538     if (self.setHost) delete self.headers.host
539     if (self.timeout && self.timeoutTimer) {
540       clearTimeout(self.timeoutTimer)
541       self.timeoutTimer = null
542     }  
543
544     var addCookie = function (cookie) {
545       if (self._jar) self._jar.add(new Cookie(cookie))
546       else cookieJar.add(new Cookie(cookie))
547     }
548
549     if (response.headers['set-cookie'] && (!self._disableCookies)) {
550       if (Array.isArray(response.headers['set-cookie'])) response.headers['set-cookie'].forEach(addCookie)
551       else addCookie(response.headers['set-cookie'])
552     }
553
554     if (response.statusCode >= 300 && response.statusCode < 400  &&
555         (self.followAllRedirects ||
556          (self.followRedirect && (self.method !== 'PUT' && self.method !== 'POST' && self.method !== 'DELETE'))) &&
557         response.headers.location) {
558       if (self._redirectsFollowed >= self.maxRedirects) {
559         self.emit('error', new Error("Exceeded maxRedirects. Probably stuck in a redirect loop "+self.uri.href))
560         return
561       }
562       self._redirectsFollowed += 1
563
564       if (!isUrl.test(response.headers.location)) {
565         response.headers.location = url.resolve(self.uri.href, response.headers.location)
566       }
567
568       var uriPrev = self.uri
569       self.uri = url.parse(response.headers.location)
570
571       // handle the case where we change protocol from https to http or vice versa
572       if (self.uri.protocol !== uriPrev.protocol) {
573         self._updateProtocol()
574       }
575
576       self.redirects.push(
577         { statusCode : response.statusCode
578         , redirectUri: response.headers.location 
579         }
580       )
581       if (self.followAllRedirects) self.method = 'GET'
582       // self.method = 'GET' // Force all redirects to use GET || commented out fixes #215
583       delete self.src
584       delete self.req
585       delete self.agent
586       delete self._started
587       delete self.body
588       delete self._form
589       if (self.headers) {
590         delete self.headers.host
591         delete self.headers['content-type']
592         delete self.headers['content-length']
593       }
594       if (log) log('Redirect to %uri', self)
595       self.init()
596       return // Ignore the rest of the response
597     } else {
598       self._redirectsFollowed = self._redirectsFollowed || 0
599       // Be a good stream and emit end when the response is finished.
600       // Hack to emit end on close because of a core bug that never fires end
601       response.on('close', function () {
602         if (!self._ended) self.response.emit('end')
603       })
604
605       if (self.encoding) {
606         if (self.dests.length !== 0) {
607           console.error("Ingoring encoding parameter as this stream is being piped to another stream which makes the encoding option invalid.")
608         } else {
609           response.setEncoding(self.encoding)
610         }
611       }
612
613       self.dests.forEach(function (dest) {
614         self.pipeDest(dest)
615       })
616
617       response.on("data", function (chunk) {
618         self._destdata = true
619         self.emit("data", chunk)
620       })
621       response.on("end", function (chunk) {
622         self._ended = true
623         self.emit("end", chunk)
624       })
625       response.on("close", function () {self.emit("close")})
626
627       self.emit('response', response)
628
629       if (self.callback) {
630         var buffer = []
631         var bodyLen = 0
632         self.on("data", function (chunk) {
633           buffer.push(chunk)
634           bodyLen += chunk.length
635         })
636         self.on("end", function () {
637           if (self._aborted) return
638           
639           if (buffer.length && Buffer.isBuffer(buffer[0])) {
640             var body = new Buffer(bodyLen)
641             var i = 0
642             buffer.forEach(function (chunk) {
643               chunk.copy(body, i, 0, chunk.length)
644               i += chunk.length
645             })
646             if (self.encoding === null) {
647               response.body = body
648             } else {
649               response.body = body.toString(self.encoding)
650             }
651           } else if (buffer.length) {
652             response.body = buffer.join('')
653           }
654
655           if (self._json) {
656             try {
657               response.body = JSON.parse(response.body)
658             } catch (e) {}
659           }
660           
661           self.emit('complete', response, response.body)
662         })
663       }
664     }
665   })
666
667   if (self.timeout && !self.timeoutTimer) {
668     self.timeoutTimer = setTimeout(function () {
669       self.req.abort()
670       var e = new Error("ETIMEDOUT")
671       e.code = "ETIMEDOUT"
672       self.emit("error", e)
673     }, self.timeout)
674     
675     // Set additional timeout on socket - in case if remote
676     // server freeze after sending headers
677     if (self.req.setTimeout) { // only works on node 0.6+
678       self.req.setTimeout(self.timeout, function () {
679         if (self.req) {
680           self.req.abort()
681           var e = new Error("ESOCKETTIMEDOUT")
682           e.code = "ESOCKETTIMEDOUT"
683           self.emit("error", e)
684         }
685       })
686     }
687   }
688   
689   self.req.on('error', self.clientErrorHandler)
690   self.req.on('drain', function() {
691     self.emit('drain')
692   })
693   self.on('end', function() {
694     if ( self.req.connection ) self.req.connection.removeListener('error', self._parserErrorHandler)
695   })
696   self.emit('request', self.req)
697 }
698
699 Request.prototype.abort = function () {
700   this._aborted = true
701   
702   if (this.req) {
703     this.req.abort()
704   }
705   else if (this.response) {
706     this.response.abort()
707   }
708   
709   this.emit("abort")
710 }
711
712 Request.prototype.pipeDest = function (dest) {
713   var response = this.response
714   // Called after the response is received
715   if (dest.headers) {
716     dest.headers['content-type'] = response.headers['content-type']
717     if (response.headers['content-length']) {
718       dest.headers['content-length'] = response.headers['content-length']
719     }
720   }
721   if (dest.setHeader) {
722     for (var i in response.headers) {
723       dest.setHeader(i, response.headers[i])
724     }
725     dest.statusCode = response.statusCode
726   }
727   if (this.pipefilter) this.pipefilter(response, dest)
728 }
729
730 // Composable API
731 Request.prototype.setHeader = function (name, value, clobber) {
732   if (clobber === undefined) clobber = true
733   if (clobber || !this.headers.hasOwnProperty(name)) this.headers[name] = value
734   else this.headers[name] += ',' + value
735   return this
736 }
737 Request.prototype.setHeaders = function (headers) {
738   for (var i in headers) {this.setHeader(i, headers[i])}
739   return this
740 }
741 Request.prototype.qs = function (q, clobber) {
742   var base
743   if (!clobber && this.uri.query) base = qs.parse(this.uri.query)
744   else base = {}
745   
746   for (var i in q) {
747     base[i] = q[i]
748   }
749   
750   this.uri = url.parse(this.uri.href.split('?')[0] + '?' + qs.stringify(base))
751   this.url = this.uri
752   
753   return this
754 }
755 Request.prototype.form = function (form) {
756   if (form) {
757     this.headers['content-type'] = 'application/x-www-form-urlencoded; charset=utf-8'
758     this.body = qs.stringify(form).toString('utf8')
759     return this
760   } 
761   // create form-data object
762   this._form = new FormData()
763   return this._form
764 }
765 Request.prototype.multipart = function (multipart) {
766   var self = this
767   self.body = []
768
769   if (!self.headers['content-type']) {
770     self.headers['content-type'] = 'multipart/related; boundary=' + self.boundary
771   } else {
772     self.headers['content-type'] = self.headers['content-type'].split(';')[0] + '; boundary=' + self.boundary
773   }
774
775   if (!multipart.forEach) throw new Error('Argument error, options.multipart.')
776
777   if (self.preambleCRLF) {
778     self.body.push(new Buffer('\r\n'))
779   }
780   
781   multipart.forEach(function (part) {
782     var body = part.body
783     if(body == null) throw Error('Body attribute missing in multipart.')
784     delete part.body
785     var preamble = '--' + self.boundary + '\r\n'
786     Object.keys(part).forEach(function (key) {
787       preamble += key + ': ' + part[key] + '\r\n'
788     })
789     preamble += '\r\n'
790     self.body.push(new Buffer(preamble))
791     self.body.push(new Buffer(body))
792     self.body.push(new Buffer('\r\n'))
793   })
794   self.body.push(new Buffer('--' + self.boundary + '--'))
795   return self
796 }
797 Request.prototype.json = function (val) {
798   this.setHeader('accept', 'application/json')
799   this._json = true
800   if (typeof val === 'boolean') {
801     if (typeof this.body === 'object') {
802       this.setHeader('content-type', 'application/json')
803       this.body = JSON.stringify(this.body)
804     }
805   } else {
806     this.setHeader('content-type', 'application/json')
807     this.body = JSON.stringify(val)
808   }
809   return this
810 }
811 function getHeader(name, headers) {
812     var result, re, match
813     Object.keys(headers).forEach(function (key) {
814         re = new RegExp(name, 'i')
815         match = key.match(re)
816         if (match) result = headers[key]
817     })
818     return result
819 }
820 Request.prototype.aws = function (opts, now) {
821   if (!now) {
822     this._aws = opts
823     return this
824   }
825   var date = new Date()
826   this.setHeader('date', date.toUTCString())
827   var auth =
828     { key: opts.key
829     , secret: opts.secret
830     , verb: this.method.toUpperCase()
831     , date: date
832     , contentType: getHeader('content-type', this.headers) || ''
833     , md5: getHeader('content-md5', this.headers) || ''
834     , amazonHeaders: aws.canonicalizeHeaders(this.headers)
835     }
836   if (opts.bucket && this.path) {
837     auth.resource = '/' + opts.bucket + this.path
838   } else if (opts.bucket && !this.path) {
839     auth.resource = '/' + opts.bucket
840   } else if (!opts.bucket && this.path) {
841     auth.resource = this.path
842   } else if (!opts.bucket && !this.path) {
843     auth.resource = '/'
844   }
845   auth.resource = aws.canonicalizeResource(auth.resource)
846   this.setHeader('authorization', aws.authorization(auth))
847   
848   return this
849 }
850
851 Request.prototype.oauth = function (_oauth) {
852   var form
853   if (this.headers['content-type'] && 
854       this.headers['content-type'].slice(0, 'application/x-www-form-urlencoded'.length) ===
855         'application/x-www-form-urlencoded' 
856      ) {
857     form = qs.parse(this.body)
858   }
859   if (this.uri.query) {
860     form = qs.parse(this.uri.query)
861   } 
862   if (!form) form = {}
863   var oa = {}
864   for (var i in form) oa[i] = form[i]
865   for (var i in _oauth) oa['oauth_'+i] = _oauth[i]
866   if (!oa.oauth_version) oa.oauth_version = '1.0'
867   if (!oa.oauth_timestamp) oa.oauth_timestamp = Math.floor( (new Date()).getTime() / 1000 ).toString()
868   if (!oa.oauth_nonce) oa.oauth_nonce = uuid().replace(/-/g, '')
869   
870   oa.oauth_signature_method = 'HMAC-SHA1'
871   
872   var consumer_secret = oa.oauth_consumer_secret
873   delete oa.oauth_consumer_secret
874   var token_secret = oa.oauth_token_secret
875   delete oa.oauth_token_secret
876   
877   var baseurl = this.uri.protocol + '//' + this.uri.host + this.uri.pathname
878   var signature = oauth.hmacsign(this.method, baseurl, oa, consumer_secret, token_secret)
879   
880   // oa.oauth_signature = signature
881   for (var i in form) {
882     if ( i.slice(0, 'oauth_') in _oauth) {
883       // skip 
884     } else {
885       delete oa['oauth_'+i]
886       if (i !== 'x_auth_mode') delete oa[i]
887     }
888   }
889   this.headers.Authorization = 
890     'OAuth '+Object.keys(oa).sort().map(function (i) {return i+'="'+oauth.rfc3986(oa[i])+'"'}).join(',')
891   this.headers.Authorization += ',oauth_signature="' + oauth.rfc3986(signature) + '"'
892   return this
893 }
894 Request.prototype.jar = function (jar) {
895   var cookies
896   
897   if (this._redirectsFollowed === 0) {
898     this.originalCookieHeader = this.headers.cookie
899   }
900   
901   if (jar === false) {
902     // disable cookies
903     cookies = false
904     this._disableCookies = true
905   } else if (jar) {
906     // fetch cookie from the user defined cookie jar
907     cookies = jar.get({ url: this.uri.href })
908   } else {
909     // fetch cookie from the global cookie jar
910     cookies = cookieJar.get({ url: this.uri.href })
911   }
912   
913   if (cookies && cookies.length) {
914     var cookieString = cookies.map(function (c) {
915       return c.name + "=" + c.value
916     }).join("; ")
917
918     if (this.originalCookieHeader) {
919       // Don't overwrite existing Cookie header
920       this.headers.cookie = this.originalCookieHeader + '; ' + cookieString
921     } else {
922       this.headers.cookie = cookieString
923     }
924   }
925   this._jar = jar
926   return this
927 }
928
929
930 // Stream API
931 Request.prototype.pipe = function (dest, opts) {
932   if (this.response) {
933     if (this._destdata) {
934       throw new Error("You cannot pipe after data has been emitted from the response.")
935     } else if (this._ended) {
936       throw new Error("You cannot pipe after the response has been ended.")
937     } else {
938       stream.Stream.prototype.pipe.call(this, dest, opts)
939       this.pipeDest(dest)
940       return dest
941     }
942   } else {
943     this.dests.push(dest)
944     stream.Stream.prototype.pipe.call(this, dest, opts)
945     return dest
946   }
947 }
948 Request.prototype.write = function () {
949   if (!this._started) this.start()
950   return this.req.write.apply(this.req, arguments)
951 }
952 Request.prototype.end = function (chunk) {
953   if (chunk) this.write(chunk)
954   if (!this._started) this.start()
955   this.req.end()
956 }
957 Request.prototype.pause = function () {
958   if (!this.response) this._paused = true
959   else this.response.pause.apply(this.response, arguments)
960 }
961 Request.prototype.resume = function () {
962   if (!this.response) this._paused = false
963   else this.response.resume.apply(this.response, arguments)
964 }
965 Request.prototype.destroy = function () {
966   if (!this._ended) this.end()
967 }
968
969 // organize params for post, put, head, del
970 function initParams(uri, options, callback) {
971   if ((typeof options === 'function') && !callback) callback = options
972   if (options && typeof options === 'object') {
973     options.uri = uri
974   } else if (typeof uri === 'string') {
975     options = {uri:uri}
976   } else {
977     options = uri
978     uri = options.uri
979   }
980   return { uri: uri, options: options, callback: callback }
981 }
982
983 function request (uri, options, callback) {
984   if (typeof uri === 'undefined') throw new Error('undefined is not a valid uri or options object.')
985   if ((typeof options === 'function') && !callback) callback = options
986   if (options && typeof options === 'object') {
987     options.uri = uri
988   } else if (typeof uri === 'string') {
989     options = {uri:uri}
990   } else {
991     options = uri
992   }
993
994   if (callback) options.callback = callback
995   var r = new Request(options)
996   return r
997 }
998
999 module.exports = request
1000
1001 request.initParams = initParams
1002
1003 request.defaults = function (options, requester) {
1004   var def = function (method) {
1005     var d = function (uri, opts, callback) {
1006       var params = initParams(uri, opts, callback)
1007       for (var i in options) {
1008         if (params.options[i] === undefined) params.options[i] = options[i]
1009       }
1010       if(typeof requester === 'function') {
1011         if(method === request) {
1012           method = requester
1013         } else {
1014           params.options._requester = requester
1015         }
1016       }
1017       return method(params.options, params.callback)
1018     }
1019     return d
1020   }
1021   var de = def(request)
1022   de.get = def(request.get)
1023   de.post = def(request.post)
1024   de.put = def(request.put)
1025   de.head = def(request.head)
1026   de.del = def(request.del)
1027   de.cookie = def(request.cookie)
1028   de.jar = request.jar
1029   return de
1030 }
1031
1032 request.forever = function (agentOptions, optionsArg) {
1033   var options = {}
1034   if (optionsArg) {
1035     for (option in optionsArg) {
1036       options[option] = optionsArg[option]
1037     }
1038   }
1039   if (agentOptions) options.agentOptions = agentOptions
1040   options.forever = true
1041   return request.defaults(options)
1042 }
1043
1044 request.get = request
1045 request.post = function (uri, options, callback) {
1046   var params = initParams(uri, options, callback)
1047   params.options.method = 'POST'
1048   return request(params.uri || null, params.options, params.callback)
1049 }
1050 request.put = function (uri, options, callback) {
1051   var params = initParams(uri, options, callback)
1052   params.options.method = 'PUT'
1053   return request(params.uri || null, params.options, params.callback)
1054 }
1055 request.head = function (uri, options, callback) {
1056   var params = initParams(uri, options, callback)
1057   params.options.method = 'HEAD'
1058   if (params.options.body || 
1059       params.options.requestBodyStream || 
1060       (params.options.json && typeof params.options.json !== 'boolean') || 
1061       params.options.multipart) {
1062     throw new Error("HTTP HEAD requests MUST NOT include a request body.")
1063   }
1064   return request(params.uri || null, params.options, params.callback)
1065 }
1066 request.del = function (uri, options, callback) {
1067   var params = initParams(uri, options, callback)
1068   params.options.method = 'DELETE'
1069   if(typeof params.options._requester === 'function') {
1070     request = params.options._requester
1071   }
1072   return request(params.uri || null, params.options, params.callback)
1073 }
1074 request.jar = function () {
1075   return new CookieJar
1076 }
1077 request.cookie = function (str) {
1078   if (str && str.uri) str = str.uri
1079   if (typeof str !== 'string') throw new Error("The cookie function only accepts STRING as param")
1080   return new Cookie(str)
1081 }
1082
1083 // Safe toJSON
1084
1085 function getSafe (self, uuid) {  
1086   if (typeof self === 'object' || typeof self === 'function') var safe = {}
1087   if (Array.isArray(self)) var safe = []
1088
1089   var recurse = []
1090   
1091   Object.defineProperty(self, uuid, {})
1092   
1093   var attrs = Object.keys(self).filter(function (i) {
1094     if (i === uuid) return false 
1095     if ( (typeof self[i] !== 'object' && typeof self[i] !== 'function') || self[i] === null) return true
1096     return !(Object.getOwnPropertyDescriptor(self[i], uuid))
1097   })
1098   
1099   
1100   for (var i=0;i<attrs.length;i++) {
1101     if ( (typeof self[attrs[i]] !== 'object' && typeof self[attrs[i]] !== 'function') || 
1102           self[attrs[i]] === null
1103         ) {
1104       safe[attrs[i]] = self[attrs[i]]
1105     } else {
1106       recurse.push(attrs[i])
1107       Object.defineProperty(self[attrs[i]], uuid, {})
1108     }
1109   }
1110
1111   for (var i=0;i<recurse.length;i++) {
1112     safe[recurse[i]] = getSafe(self[recurse[i]], uuid)
1113   }
1114   
1115   return safe
1116 }
1117
1118 function toJSON () {
1119   return getSafe(this, (((1+Math.random())*0x10000)|0).toString(16))
1120 }
1121
1122 Request.prototype.toJSON = toJSON
1123