react / wstein / node_modules / jest-cli / node_modules / jsdom / node_modules / request / request.js
80676 views'use strict'12var http = require('http')3, https = require('https')4, url = require('url')5, util = require('util')6, stream = require('stream')7, zlib = require('zlib')8, helpers = require('./lib/helpers')9, bl = require('bl')10, hawk = require('hawk')11, aws = require('aws-sign2')12, httpSignature = require('http-signature')13, mime = require('mime-types')14, tunnel = require('tunnel-agent')15, stringstream = require('stringstream')16, caseless = require('caseless')17, ForeverAgent = require('forever-agent')18, FormData = require('form-data')19, cookies = require('./lib/cookies')20, copy = require('./lib/copy')21, getProxyFromURI = require('./lib/getProxyFromURI')22, Querystring = require('./lib/querystring').Querystring23, Har = require('./lib/har').Har24, Auth = require('./lib/auth').Auth25, OAuth = require('./lib/oauth').OAuth26, Multipart = require('./lib/multipart').Multipart27, Redirect = require('./lib/redirect').Redirect2829var safeStringify = helpers.safeStringify30, isReadStream = helpers.isReadStream31, toBase64 = helpers.toBase6432, defer = helpers.defer33, globalCookieJar = cookies.jar()343536var globalPool = {}3738var defaultProxyHeaderWhiteList = [39'accept',40'accept-charset',41'accept-encoding',42'accept-language',43'accept-ranges',44'cache-control',45'content-encoding',46'content-language',47'content-length',48'content-location',49'content-md5',50'content-range',51'content-type',52'connection',53'date',54'expect',55'max-forwards',56'pragma',57'referer',58'te',59'transfer-encoding',60'user-agent',61'via'62]6364var defaultProxyHeaderExclusiveList = [65'proxy-authorization'66]6768function filterForNonReserved(reserved, options) {69// Filter out properties that are not reserved.70// Reserved values are passed in at call site.7172var object = {}73for (var i in options) {74var notReserved = (reserved.indexOf(i) === -1)75if (notReserved) {76object[i] = options[i]77}78}79return object80}8182function filterOutReservedFunctions(reserved, options) {83// Filter out properties that are functions and are reserved.84// Reserved values are passed in at call site.8586var object = {}87for (var i in options) {88var isReserved = !(reserved.indexOf(i) === -1)89var isFunction = (typeof options[i] === 'function')90if (!(isReserved && isFunction)) {91object[i] = options[i]92}93}94return object9596}9798function constructProxyHost(uriObject) {99var port = uriObject.portA100, protocol = uriObject.protocol101, proxyHost = uriObject.hostname + ':'102103if (port) {104proxyHost += port105} else if (protocol === 'https:') {106proxyHost += '443'107} else {108proxyHost += '80'109}110111return proxyHost112}113114function constructProxyHeaderWhiteList(headers, proxyHeaderWhiteList) {115var whiteList = proxyHeaderWhiteList116.reduce(function (set, header) {117set[header.toLowerCase()] = true118return set119}, {})120121return Object.keys(headers)122.filter(function (header) {123return whiteList[header.toLowerCase()]124})125.reduce(function (set, header) {126set[header] = headers[header]127return set128}, {})129}130131function getTunnelOption(self, options) {132// Tunnel HTTPS by default, or if a previous request in the redirect chain133// was tunneled. Allow the user to override this setting.134135// If self.tunnel is already set (because this is a redirect), use the136// existing value.137if (typeof self.tunnel !== 'undefined') {138return self.tunnel139}140141// If options.tunnel is set (the user specified a value), use it.142if (typeof options.tunnel !== 'undefined') {143return options.tunnel144}145146// If the destination is HTTPS, tunnel.147if (self.uri.protocol === 'https:') {148return true149}150151// Otherwise, leave tunnel unset, because if a later request in the redirect152// chain is HTTPS then that request (and any subsequent ones) should be153// tunneled.154return undefined155}156157function constructTunnelOptions(request) {158var proxy = request.proxy159160var tunnelOptions = {161proxy : {162host : proxy.hostname,163port : +proxy.port,164proxyAuth : proxy.auth,165headers : request.proxyHeaders166},167headers : request.headers,168ca : request.ca,169cert : request.cert,170key : request.key,171passphrase : request.passphrase,172pfx : request.pfx,173ciphers : request.ciphers,174rejectUnauthorized : request.rejectUnauthorized,175secureOptions : request.secureOptions,176secureProtocol : request.secureProtocol177}178179return tunnelOptions180}181182function constructTunnelFnName(uri, proxy) {183var uriProtocol = (uri.protocol === 'https:' ? 'https' : 'http')184var proxyProtocol = (proxy.protocol === 'https:' ? 'Https' : 'Http')185return [uriProtocol, proxyProtocol].join('Over')186}187188function getTunnelFn(request) {189var uri = request.uri190var proxy = request.proxy191var tunnelFnName = constructTunnelFnName(uri, proxy)192return tunnel[tunnelFnName]193}194195// Function for properly handling a connection error196function connectionErrorHandler(error) {197var socket = this198if (socket.res) {199if (socket.res.request) {200socket.res.request.emit('error', error)201} else {202socket.res.emit('error', error)203}204} else {205socket._httpMessage.emit('error', error)206}207}208209// Return a simpler request object to allow serialization210function requestToJSON() {211var self = this212return {213uri: self.uri,214method: self.method,215headers: self.headers216}217}218219// Return a simpler response object to allow serialization220function responseToJSON() {221var self = this222return {223statusCode: self.statusCode,224body: self.body,225headers: self.headers,226request: requestToJSON.call(self.request)227}228}229230function Request (options) {231// if given the method property in options, set property explicitMethod to true232233// extend the Request instance with any non-reserved properties234// remove any reserved functions from the options object235// set Request instance to be readable and writable236// call init237238var self = this239240// start with HAR, then override with additional options241if (options.har) {242self._har = new Har(self)243options = self._har.options(options)244}245246stream.Stream.call(self)247var reserved = Object.keys(Request.prototype)248var nonReserved = filterForNonReserved(reserved, options)249250stream.Stream.call(self)251util._extend(self, nonReserved)252options = filterOutReservedFunctions(reserved, options)253254self.readable = true255self.writable = true256if (options.method) {257self.explicitMethod = true258}259self._qs = new Querystring(self)260self._auth = new Auth(self)261self._oauth = new OAuth(self)262self._multipart = new Multipart(self)263self._redirect = new Redirect(self)264self.init(options)265}266267util.inherits(Request, stream.Stream)268269// Debugging270Request.debug = process.env.NODE_DEBUG && /\brequest\b/.test(process.env.NODE_DEBUG)271function debug() {272if (Request.debug) {273console.error('REQUEST %s', util.format.apply(util, arguments))274}275}276Request.prototype.debug = debug277278Request.prototype.setupTunnel = function () {279var self = this280281if (typeof self.proxy === 'string') {282self.proxy = url.parse(self.proxy)283}284285if (!self.proxy || !self.tunnel) {286return false287}288289// Setup Proxy Header Exclusive List and White List290self.proxyHeaderExclusiveList = self.proxyHeaderExclusiveList || []291self.proxyHeaderWhiteList = self.proxyHeaderWhiteList || defaultProxyHeaderWhiteList292var proxyHeaderExclusiveList = self.proxyHeaderExclusiveList.concat(defaultProxyHeaderExclusiveList)293var proxyHeaderWhiteList = self.proxyHeaderWhiteList.concat(proxyHeaderExclusiveList)294295// Setup Proxy Headers and Proxy Headers Host296// Only send the Proxy White Listed Header names297self.proxyHeaders = constructProxyHeaderWhiteList(self.headers, proxyHeaderWhiteList)298self.proxyHeaders.host = constructProxyHost(self.uri)299proxyHeaderExclusiveList.forEach(self.removeHeader, self)300301// Set Agent from Tunnel Data302var tunnelFn = getTunnelFn(self)303var tunnelOptions = constructTunnelOptions(self)304self.agent = tunnelFn(tunnelOptions)305306return true307}308309Request.prototype.init = function (options) {310// init() contains all the code to setup the request object.311// the actual outgoing request is not started until start() is called312// this function is called from both the constructor and on redirect.313var self = this314if (!options) {315options = {}316}317self.headers = self.headers ? copy(self.headers) : {}318319// Delete headers with value undefined since they break320// ClientRequest.OutgoingMessage.setHeader in node 0.12321for (var headerName in self.headers) {322if (typeof self.headers[headerName] === 'undefined') {323delete self.headers[headerName]324}325}326327caseless.httpify(self, self.headers)328329if (!self.method) {330self.method = options.method || 'GET'331}332if (!self.localAddress) {333self.localAddress = options.localAddress334}335336self._qs.init(options)337338debug(options)339if (!self.pool && self.pool !== false) {340self.pool = globalPool341}342self.dests = self.dests || []343self.__isRequestRequest = true344345// Protect against double callback346if (!self._callback && self.callback) {347self._callback = self.callback348self.callback = function () {349if (self._callbackCalled) {350return // Print a warning maybe?351}352self._callbackCalled = true353self._callback.apply(self, arguments)354}355self.on('error', self.callback.bind())356self.on('complete', self.callback.bind(self, null))357}358359// People use this property instead all the time, so support it360if (!self.uri && self.url) {361self.uri = self.url362delete self.url363}364365// If there's a baseUrl, then use it as the base URL (i.e. uri must be366// specified as a relative path and is appended to baseUrl).367if (self.baseUrl) {368if (typeof self.baseUrl !== 'string') {369return self.emit('error', new Error('options.baseUrl must be a string'))370}371372if (typeof self.uri !== 'string') {373return self.emit('error', new Error('options.uri must be a string when using options.baseUrl'))374}375376if (self.uri.indexOf('//') === 0 || self.uri.indexOf('://') !== -1) {377return self.emit('error', new Error('options.uri must be a path when using options.baseUrl'))378}379380// Handle all cases to make sure that there's only one slash between381// baseUrl and uri.382var baseUrlEndsWithSlash = self.baseUrl.lastIndexOf('/') === self.baseUrl.length - 1383var uriStartsWithSlash = self.uri.indexOf('/') === 0384385if (baseUrlEndsWithSlash && uriStartsWithSlash) {386self.uri = self.baseUrl + self.uri.slice(1)387} else if (baseUrlEndsWithSlash || uriStartsWithSlash) {388self.uri = self.baseUrl + self.uri389} else if (self.uri === '') {390self.uri = self.baseUrl391} else {392self.uri = self.baseUrl + '/' + self.uri393}394delete self.baseUrl395}396397// A URI is needed by this point, emit error if we haven't been able to get one398if (!self.uri) {399return self.emit('error', new Error('options.uri is a required argument'))400}401402// If a string URI/URL was given, parse it into a URL object403if (typeof self.uri === 'string') {404self.uri = url.parse(self.uri)405}406407// DEPRECATED: Warning for users of the old Unix Sockets URL Scheme408if (self.uri.protocol === 'unix:') {409return self.emit('error', new Error('`unix://` URL scheme is no longer supported. Please use the format `http://unix:SOCKET:PATH`'))410}411412// Support Unix Sockets413if (self.uri.host === 'unix') {414// Get the socket & request paths from the URL415var unixParts = self.uri.path.split(':')416, host = unixParts[0]417, path = unixParts[1]418// Apply unix properties to request419self.socketPath = host420self.uri.pathname = path421self.uri.path = path422self.uri.host = host423self.uri.hostname = host424self.uri.isUnix = true425}426427if (self.strictSSL === false) {428self.rejectUnauthorized = false429}430431if (!self.uri.pathname) {self.uri.pathname = '/'}432433if (!(self.uri.host || (self.uri.hostname && self.uri.port)) && !self.uri.isUnix) {434// Invalid URI: it may generate lot of bad errors, like 'TypeError: Cannot call method `indexOf` of undefined' in CookieJar435// Detect and reject it as soon as possible436var faultyUri = url.format(self.uri)437var message = 'Invalid URI "' + faultyUri + '"'438if (Object.keys(options).length === 0) {439// No option ? This can be the sign of a redirect440// As this is a case where the user cannot do anything (they didn't call request directly with this URL)441// they should be warned that it can be caused by a redirection (can save some hair)442message += '. This can be caused by a crappy redirection.'443}444// This error was fatal445return self.emit('error', new Error(message))446}447448if (!self.hasOwnProperty('proxy')) {449self.proxy = getProxyFromURI(self.uri)450}451452self.tunnel = getTunnelOption(self, options)453if (self.proxy) {454self.setupTunnel()455}456457self._redirect.onRequest(options)458459self.setHost = false460if (!self.hasHeader('host')) {461var hostHeaderName = self.originalHostHeaderName || 'host'462self.setHeader(hostHeaderName, self.uri.hostname)463if (self.uri.port) {464if ( !(self.uri.port === 80 && self.uri.protocol === 'http:') &&465!(self.uri.port === 443 && self.uri.protocol === 'https:') ) {466self.setHeader(hostHeaderName, self.getHeader('host') + (':' + self.uri.port) )467}468}469self.setHost = true470}471472self.jar(self._jar || options.jar)473474if (!self.uri.port) {475if (self.uri.protocol === 'http:') {self.uri.port = 80}476else if (self.uri.protocol === 'https:') {self.uri.port = 443}477}478479if (self.proxy && !self.tunnel) {480self.port = self.proxy.port481self.host = self.proxy.hostname482} else {483self.port = self.uri.port484self.host = self.uri.hostname485}486487if (options.form) {488self.form(options.form)489}490491if (options.formData) {492var formData = options.formData493var requestForm = self.form()494var appendFormValue = function (key, value) {495if (value.hasOwnProperty('value') && value.hasOwnProperty('options')) {496requestForm.append(key, value.value, value.options)497} else {498requestForm.append(key, value)499}500}501for (var formKey in formData) {502if (formData.hasOwnProperty(formKey)) {503var formValue = formData[formKey]504if (formValue instanceof Array) {505for (var j = 0; j < formValue.length; j++) {506appendFormValue(formKey, formValue[j])507}508} else {509appendFormValue(formKey, formValue)510}511}512}513}514515if (options.qs) {516self.qs(options.qs)517}518519if (self.uri.path) {520self.path = self.uri.path521} else {522self.path = self.uri.pathname + (self.uri.search || '')523}524525if (self.path.length === 0) {526self.path = '/'527}528529// Auth must happen last in case signing is dependent on other headers530if (options.aws) {531self.aws(options.aws)532}533534if (options.hawk) {535self.hawk(options.hawk)536}537538if (options.httpSignature) {539self.httpSignature(options.httpSignature)540}541542if (options.auth) {543if (Object.prototype.hasOwnProperty.call(options.auth, 'username')) {544options.auth.user = options.auth.username545}546if (Object.prototype.hasOwnProperty.call(options.auth, 'password')) {547options.auth.pass = options.auth.password548}549550self.auth(551options.auth.user,552options.auth.pass,553options.auth.sendImmediately,554options.auth.bearer555)556}557558if (self.gzip && !self.hasHeader('accept-encoding')) {559self.setHeader('accept-encoding', 'gzip')560}561562if (self.uri.auth && !self.hasHeader('authorization')) {563var uriAuthPieces = self.uri.auth.split(':').map(function(item) {return self._qs.unescape(item)})564self.auth(uriAuthPieces[0], uriAuthPieces.slice(1).join(':'), true)565}566567if (!self.tunnel && self.proxy && self.proxy.auth && !self.hasHeader('proxy-authorization')) {568var proxyAuthPieces = self.proxy.auth.split(':').map(function(item) {return self._qs.unescape(item)})569var authHeader = 'Basic ' + toBase64(proxyAuthPieces.join(':'))570self.setHeader('proxy-authorization', authHeader)571}572573if (self.proxy && !self.tunnel) {574self.path = (self.uri.protocol + '//' + self.uri.host + self.path)575}576577if (options.json) {578self.json(options.json)579}580if (options.multipart) {581self.multipart(options.multipart)582}583584if (options.time) {585self.timing = true586self.elapsedTime = self.elapsedTime || 0587}588589if (self.body) {590var length = 0591if (!Buffer.isBuffer(self.body)) {592if (Array.isArray(self.body)) {593for (var i = 0; i < self.body.length; i++) {594length += self.body[i].length595}596} else {597self.body = new Buffer(self.body)598length = self.body.length599}600} else {601length = self.body.length602}603if (length) {604if (!self.hasHeader('content-length')) {605self.setHeader('content-length', length)606}607} else {608self.emit('error', new Error('Argument error, options.body.'))609}610}611612if (options.oauth) {613self.oauth(options.oauth)614} else if (self._oauth.params && self.hasHeader('authorization')) {615self.oauth(self._oauth.params)616}617618var protocol = self.proxy && !self.tunnel ? self.proxy.protocol : self.uri.protocol619, defaultModules = {'http:':http, 'https:':https}620, httpModules = self.httpModules || {}621622self.httpModule = httpModules[protocol] || defaultModules[protocol]623624if (!self.httpModule) {625return self.emit('error', new Error('Invalid protocol: ' + protocol))626}627628if (options.ca) {629self.ca = options.ca630}631632if (!self.agent) {633if (options.agentOptions) {634self.agentOptions = options.agentOptions635}636637if (options.agentClass) {638self.agentClass = options.agentClass639} else if (options.forever) {640self.agentClass = protocol === 'http:' ? ForeverAgent : ForeverAgent.SSL641} else {642self.agentClass = self.httpModule.Agent643}644}645646if (self.pool === false) {647self.agent = false648} else {649self.agent = self.agent || self.getNewAgent()650}651652self.on('pipe', function (src) {653if (self.ntick && self._started) {654self.emit('error', new Error('You cannot pipe to this stream after the outbound request has started.'))655}656self.src = src657if (isReadStream(src)) {658if (!self.hasHeader('content-type')) {659self.setHeader('content-type', mime.lookup(src.path))660}661} else {662if (src.headers) {663for (var i in src.headers) {664if (!self.hasHeader(i)) {665self.setHeader(i, src.headers[i])666}667}668}669if (self._json && !self.hasHeader('content-type')) {670self.setHeader('content-type', 'application/json')671}672if (src.method && !self.explicitMethod) {673self.method = src.method674}675}676677// self.on('pipe', function () {678// console.error('You have already piped to this stream. Pipeing twice is likely to break the request.')679// })680})681682defer(function () {683if (self._aborted) {684return685}686687var end = function () {688if (self._form) {689if (!self._auth.hasAuth) {690self._form.pipe(self)691}692else if (self._auth.hasAuth && self._auth.sentAuth) {693self._form.pipe(self)694}695}696if (self._multipart && self._multipart.chunked) {697self._multipart.body.pipe(self)698}699if (self.body) {700if (Array.isArray(self.body)) {701self.body.forEach(function (part) {702self.write(part)703})704} else {705self.write(self.body)706}707self.end()708} else if (self.requestBodyStream) {709console.warn('options.requestBodyStream is deprecated, please pass the request object to stream.pipe.')710self.requestBodyStream.pipe(self)711} else if (!self.src) {712if (self._auth.hasAuth && !self._auth.sentAuth) {713self.end()714return715}716if (self.method !== 'GET' && typeof self.method !== 'undefined') {717self.setHeader('content-length', 0)718}719self.end()720}721}722723if (self._form && !self.hasHeader('content-length')) {724// Before ending the request, we had to compute the length of the whole form, asyncly725self.setHeader(self._form.getHeaders())726self._form.getLength(function (err, length) {727if (!err) {728self.setHeader('content-length', length)729}730end()731})732} else {733end()734}735736self.ntick = true737})738739}740741// Must call this when following a redirect from https to http or vice versa742// Attempts to keep everything as identical as possible, but update the743// httpModule, Tunneling agent, and/or Forever Agent in use.744Request.prototype._updateProtocol = function () {745var self = this746var protocol = self.uri.protocol747748if (protocol === 'https:' || self.tunnel) {749// previously was doing http, now doing https750// if it's https, then we might need to tunnel now.751if (self.proxy) {752if (self.setupTunnel()) {753return754}755}756757self.httpModule = https758switch (self.agentClass) {759case ForeverAgent:760self.agentClass = ForeverAgent.SSL761break762case http.Agent:763self.agentClass = https.Agent764break765default:766// nothing we can do. Just hope for the best.767return768}769770// if there's an agent, we need to get a new one.771if (self.agent) {772self.agent = self.getNewAgent()773}774775} else {776// previously was doing https, now doing http777self.httpModule = http778switch (self.agentClass) {779case ForeverAgent.SSL:780self.agentClass = ForeverAgent781break782case https.Agent:783self.agentClass = http.Agent784break785default:786// nothing we can do. just hope for the best787return788}789790// if there's an agent, then get a new one.791if (self.agent) {792self.agent = null793self.agent = self.getNewAgent()794}795}796}797798Request.prototype.getNewAgent = function () {799var self = this800var Agent = self.agentClass801var options = {}802if (self.agentOptions) {803for (var i in self.agentOptions) {804options[i] = self.agentOptions[i]805}806}807if (self.ca) {808options.ca = self.ca809}810if (self.ciphers) {811options.ciphers = self.ciphers812}813if (self.secureProtocol) {814options.secureProtocol = self.secureProtocol815}816if (self.secureOptions) {817options.secureOptions = self.secureOptions818}819if (typeof self.rejectUnauthorized !== 'undefined') {820options.rejectUnauthorized = self.rejectUnauthorized821}822823if (self.cert && self.key) {824options.key = self.key825options.cert = self.cert826}827828if (self.pfx) {829options.pfx = self.pfx830}831832if (self.passphrase) {833options.passphrase = self.passphrase834}835836var poolKey = ''837838// different types of agents are in different pools839if (Agent !== self.httpModule.Agent) {840poolKey += Agent.name841}842843// ca option is only relevant if proxy or destination are https844var proxy = self.proxy845if (typeof proxy === 'string') {846proxy = url.parse(proxy)847}848var isHttps = (proxy && proxy.protocol === 'https:') || this.uri.protocol === 'https:'849850if (isHttps) {851if (options.ca) {852if (poolKey) {853poolKey += ':'854}855poolKey += options.ca856}857858if (typeof options.rejectUnauthorized !== 'undefined') {859if (poolKey) {860poolKey += ':'861}862poolKey += options.rejectUnauthorized863}864865if (options.cert) {866if (poolKey) {867poolKey += ':'868}869poolKey += options.cert.toString('ascii') + options.key.toString('ascii')870}871872if (options.pfx) {873if (poolKey) {874poolKey += ':'875}876poolKey += options.pfx.toString('ascii')877}878879if (options.ciphers) {880if (poolKey) {881poolKey += ':'882}883poolKey += options.ciphers884}885886if (options.secureProtocol) {887if (poolKey) {888poolKey += ':'889}890poolKey += options.secureProtocol891}892893if (options.secureOptions) {894if (poolKey) {895poolKey += ':'896}897poolKey += options.secureOptions898}899}900901if (self.pool === globalPool && !poolKey && Object.keys(options).length === 0 && self.httpModule.globalAgent) {902// not doing anything special. Use the globalAgent903return self.httpModule.globalAgent904}905906// we're using a stored agent. Make sure it's protocol-specific907poolKey = self.uri.protocol + poolKey908909// generate a new agent for this setting if none yet exists910if (!self.pool[poolKey]) {911self.pool[poolKey] = new Agent(options)912// properly set maxSockets on new agents913if (self.pool.maxSockets) {914self.pool[poolKey].maxSockets = self.pool.maxSockets915}916}917918return self.pool[poolKey]919}920921Request.prototype.start = function () {922// start() is called once we are ready to send the outgoing HTTP request.923// this is usually called on the first write(), end() or on nextTick()924var self = this925926if (self._aborted) {927return928}929930self._started = true931self.method = self.method || 'GET'932self.href = self.uri.href933934if (self.src && self.src.stat && self.src.stat.size && !self.hasHeader('content-length')) {935self.setHeader('content-length', self.src.stat.size)936}937if (self._aws) {938self.aws(self._aws, true)939}940941// We have a method named auth, which is completely different from the http.request942// auth option. If we don't remove it, we're gonna have a bad time.943var reqOptions = copy(self)944delete reqOptions.auth945946debug('make request', self.uri.href)947948self.req = self.httpModule.request(reqOptions)949950if (self.timing) {951self.startTime = new Date().getTime()952}953954if (self.timeout && !self.timeoutTimer) {955var timeout = self.timeout < 0 ? 0 : self.timeout956self.timeoutTimer = setTimeout(function () {957self.abort()958var e = new Error('ETIMEDOUT')959e.code = 'ETIMEDOUT'960self.emit('error', e)961}, timeout)962963// Set additional timeout on socket - in case if remote964// server freeze after sending headers965if (self.req.setTimeout) { // only works on node 0.6+966self.req.setTimeout(timeout, function () {967if (self.req) {968self.req.abort()969var e = new Error('ESOCKETTIMEDOUT')970e.code = 'ESOCKETTIMEDOUT'971self.emit('error', e)972}973})974}975}976977self.req.on('response', self.onRequestResponse.bind(self))978self.req.on('error', self.onRequestError.bind(self))979self.req.on('drain', function() {980self.emit('drain')981})982self.req.on('socket', function(socket) {983self.emit('socket', socket)984})985986self.on('end', function() {987if ( self.req.connection ) {988self.req.connection.removeListener('error', connectionErrorHandler)989}990})991self.emit('request', self.req)992}993994Request.prototype.onRequestError = function (error) {995var self = this996if (self._aborted) {997return998}999if (self.req && self.req._reusedSocket && error.code === 'ECONNRESET'1000&& self.agent.addRequestNoreuse) {1001self.agent = { addRequest: self.agent.addRequestNoreuse.bind(self.agent) }1002self.start()1003self.req.end()1004return1005}1006if (self.timeout && self.timeoutTimer) {1007clearTimeout(self.timeoutTimer)1008self.timeoutTimer = null1009}1010self.emit('error', error)1011}10121013Request.prototype.onRequestResponse = function (response) {1014var self = this1015debug('onRequestResponse', self.uri.href, response.statusCode, response.headers)1016response.on('end', function() {1017if (self.timing) {1018self.elapsedTime += (new Date().getTime() - self.startTime)1019debug('elapsed time', self.elapsedTime)1020response.elapsedTime = self.elapsedTime1021}1022debug('response end', self.uri.href, response.statusCode, response.headers)1023})10241025// The check on response.connection is a workaround for browserify.1026if (response.connection && response.connection.listeners('error').indexOf(connectionErrorHandler) === -1) {1027response.connection.setMaxListeners(0)1028response.connection.once('error', connectionErrorHandler)1029}1030if (self._aborted) {1031debug('aborted', self.uri.href)1032response.resume()1033return1034}10351036self.response = response1037response.request = self1038response.toJSON = responseToJSON10391040// XXX This is different on 0.10, because SSL is strict by default1041if (self.httpModule === https &&1042self.strictSSL && (!response.hasOwnProperty('client') ||1043!response.client.authorized)) {1044debug('strict ssl error', self.uri.href)1045var sslErr = response.hasOwnProperty('client') ? response.client.authorizationError : self.uri.href + ' does not support SSL'1046self.emit('error', new Error('SSL Error: ' + sslErr))1047return1048}10491050// Save the original host before any redirect (if it changes, we need to1051// remove any authorization headers). Also remember the case of the header1052// name because lots of broken servers expect Host instead of host and we1053// want the caller to be able to specify this.1054self.originalHost = self.getHeader('host')1055if (!self.originalHostHeaderName) {1056self.originalHostHeaderName = self.hasHeader('host')1057}1058if (self.setHost) {1059self.removeHeader('host')1060}1061if (self.timeout && self.timeoutTimer) {1062clearTimeout(self.timeoutTimer)1063self.timeoutTimer = null1064}10651066var targetCookieJar = (self._jar && self._jar.setCookie) ? self._jar : globalCookieJar1067var addCookie = function (cookie) {1068//set the cookie if it's domain in the href's domain.1069try {1070targetCookieJar.setCookie(cookie, self.uri.href, {ignoreError: true})1071} catch (e) {1072self.emit('error', e)1073}1074}10751076response.caseless = caseless(response.headers)10771078if (response.caseless.has('set-cookie') && (!self._disableCookies)) {1079var headerName = response.caseless.has('set-cookie')1080if (Array.isArray(response.headers[headerName])) {1081response.headers[headerName].forEach(addCookie)1082} else {1083addCookie(response.headers[headerName])1084}1085}10861087if (self._redirect.onResponse(response)) {1088return // Ignore the rest of the response1089} else {1090// Be a good stream and emit end when the response is finished.1091// Hack to emit end on close because of a core bug that never fires end1092response.on('close', function () {1093if (!self._ended) {1094self.response.emit('end')1095}1096})10971098response.on('end', function () {1099self._ended = true1100})11011102var responseContent1103if (self.gzip) {1104var contentEncoding = response.headers['content-encoding'] || 'identity'1105contentEncoding = contentEncoding.trim().toLowerCase()11061107if (contentEncoding === 'gzip') {1108responseContent = zlib.createGunzip()1109response.pipe(responseContent)1110} else {1111// Since previous versions didn't check for Content-Encoding header,1112// ignore any invalid values to preserve backwards-compatibility1113if (contentEncoding !== 'identity') {1114debug('ignoring unrecognized Content-Encoding ' + contentEncoding)1115}1116responseContent = response1117}1118} else {1119responseContent = response1120}11211122if (self.encoding) {1123if (self.dests.length !== 0) {1124console.error('Ignoring encoding parameter as this stream is being piped to another stream which makes the encoding option invalid.')1125} else if (responseContent.setEncoding) {1126responseContent.setEncoding(self.encoding)1127} else {1128// Should only occur on node pre-v0.9.4 (joyent/node@9b5abe5) with1129// zlib streams.1130// If/When support for 0.9.4 is dropped, this should be unnecessary.1131responseContent = responseContent.pipe(stringstream(self.encoding))1132}1133}11341135if (self._paused) {1136responseContent.pause()1137}11381139self.responseContent = responseContent11401141self.emit('response', response)11421143self.dests.forEach(function (dest) {1144self.pipeDest(dest)1145})11461147responseContent.on('data', function (chunk) {1148self._destdata = true1149self.emit('data', chunk)1150})1151responseContent.on('end', function (chunk) {1152self.emit('end', chunk)1153})1154responseContent.on('error', function (error) {1155self.emit('error', error)1156})1157responseContent.on('close', function () {self.emit('close')})11581159if (self.callback) {1160var buffer = bl()1161, strings = []11621163self.on('data', function (chunk) {1164if (Buffer.isBuffer(chunk)) {1165buffer.append(chunk)1166} else {1167strings.push(chunk)1168}1169})1170self.on('end', function () {1171debug('end event', self.uri.href)1172if (self._aborted) {1173debug('aborted', self.uri.href)1174return1175}11761177if (buffer.length) {1178debug('has body', self.uri.href, buffer.length)1179if (self.encoding === null) {1180// response.body = buffer1181// can't move to this until https://github.com/rvagg/bl/issues/131182response.body = buffer.slice()1183} else {1184response.body = buffer.toString(self.encoding)1185}1186} else if (strings.length) {1187// The UTF8 BOM [0xEF,0xBB,0xBF] is converted to [0xFE,0xFF] in the JS UTC16/UCS2 representation.1188// Strip this value out when the encoding is set to 'utf8', as upstream consumers won't expect it and it breaks JSON.parse().1189if (self.encoding === 'utf8' && strings[0].length > 0 && strings[0][0] === '\uFEFF') {1190strings[0] = strings[0].substring(1)1191}1192response.body = strings.join('')1193}11941195if (self._json) {1196try {1197response.body = JSON.parse(response.body, self._jsonReviver)1198} catch (e) {1199// empty1200}1201}1202debug('emitting complete', self.uri.href)1203if (typeof response.body === 'undefined' && !self._json) {1204response.body = self.encoding === null ? new Buffer(0) : ''1205}1206self.emit('complete', response, response.body)1207})1208}1209//if no callback1210else {1211self.on('end', function () {1212if (self._aborted) {1213debug('aborted', self.uri.href)1214return1215}1216self.emit('complete', response)1217})1218}1219}1220debug('finish init function', self.uri.href)1221}12221223Request.prototype.abort = function () {1224var self = this1225self._aborted = true12261227if (self.req) {1228self.req.abort()1229}1230else if (self.response) {1231self.response.abort()1232}12331234self.emit('abort')1235}12361237Request.prototype.pipeDest = function (dest) {1238var self = this1239var response = self.response1240// Called after the response is received1241if (dest.headers && !dest.headersSent) {1242if (response.caseless.has('content-type')) {1243var ctname = response.caseless.has('content-type')1244if (dest.setHeader) {1245dest.setHeader(ctname, response.headers[ctname])1246}1247else {1248dest.headers[ctname] = response.headers[ctname]1249}1250}12511252if (response.caseless.has('content-length')) {1253var clname = response.caseless.has('content-length')1254if (dest.setHeader) {1255dest.setHeader(clname, response.headers[clname])1256} else {1257dest.headers[clname] = response.headers[clname]1258}1259}1260}1261if (dest.setHeader && !dest.headersSent) {1262for (var i in response.headers) {1263// If the response content is being decoded, the Content-Encoding header1264// of the response doesn't represent the piped content, so don't pass it.1265if (!self.gzip || i !== 'content-encoding') {1266dest.setHeader(i, response.headers[i])1267}1268}1269dest.statusCode = response.statusCode1270}1271if (self.pipefilter) {1272self.pipefilter(response, dest)1273}1274}12751276Request.prototype.qs = function (q, clobber) {1277var self = this1278var base1279if (!clobber && self.uri.query) {1280base = self._qs.parse(self.uri.query)1281} else {1282base = {}1283}12841285for (var i in q) {1286base[i] = q[i]1287}12881289if (self._qs.stringify(base) === '') {1290return self1291}12921293var qs = self._qs.stringify(base)12941295self.uri = url.parse(self.uri.href.split('?')[0] + '?' + qs)1296self.url = self.uri1297self.path = self.uri.path12981299return self1300}1301Request.prototype.form = function (form) {1302var self = this1303if (form) {1304self.setHeader('content-type', 'application/x-www-form-urlencoded')1305self.body = (typeof form === 'string')1306? self._qs.rfc3986(form.toString('utf8'))1307: self._qs.stringify(form).toString('utf8')1308return self1309}1310// create form-data object1311self._form = new FormData()1312self._form.on('error', function(err) {1313err.message = 'form-data: ' + err.message1314self.emit('error', err)1315self.abort()1316})1317return self._form1318}1319Request.prototype.multipart = function (multipart) {1320var self = this13211322self._multipart.onRequest(multipart)13231324if (!self._multipart.chunked) {1325self.body = self._multipart.body1326}13271328return self1329}1330Request.prototype.json = function (val) {1331var self = this13321333if (!self.hasHeader('accept')) {1334self.setHeader('accept', 'application/json')1335}13361337self._json = true1338if (typeof val === 'boolean') {1339if (self.body !== undefined) {1340if (!/^application\/x-www-form-urlencoded\b/.test(self.getHeader('content-type'))) {1341self.body = safeStringify(self.body)1342} else {1343self.body = self._qs.rfc3986(self.body)1344}1345if (!self.hasHeader('content-type')) {1346self.setHeader('content-type', 'application/json')1347}1348}1349} else {1350self.body = safeStringify(val)1351if (!self.hasHeader('content-type')) {1352self.setHeader('content-type', 'application/json')1353}1354}13551356if (typeof self.jsonReviver === 'function') {1357self._jsonReviver = self.jsonReviver1358}13591360return self1361}1362Request.prototype.getHeader = function (name, headers) {1363var self = this1364var result, re, match1365if (!headers) {1366headers = self.headers1367}1368Object.keys(headers).forEach(function (key) {1369if (key.length !== name.length) {1370return1371}1372re = new RegExp(name, 'i')1373match = key.match(re)1374if (match) {1375result = headers[key]1376}1377})1378return result1379}13801381Request.prototype.auth = function (user, pass, sendImmediately, bearer) {1382var self = this13831384self._auth.onRequest(user, pass, sendImmediately, bearer)13851386return self1387}1388Request.prototype.aws = function (opts, now) {1389var self = this13901391if (!now) {1392self._aws = opts1393return self1394}1395var date = new Date()1396self.setHeader('date', date.toUTCString())1397var auth =1398{ key: opts.key1399, secret: opts.secret1400, verb: self.method.toUpperCase()1401, date: date1402, contentType: self.getHeader('content-type') || ''1403, md5: self.getHeader('content-md5') || ''1404, amazonHeaders: aws.canonicalizeHeaders(self.headers)1405}1406var path = self.uri.path1407if (opts.bucket && path) {1408auth.resource = '/' + opts.bucket + path1409} else if (opts.bucket && !path) {1410auth.resource = '/' + opts.bucket1411} else if (!opts.bucket && path) {1412auth.resource = path1413} else if (!opts.bucket && !path) {1414auth.resource = '/'1415}1416auth.resource = aws.canonicalizeResource(auth.resource)1417self.setHeader('authorization', aws.authorization(auth))14181419return self1420}1421Request.prototype.httpSignature = function (opts) {1422var self = this1423httpSignature.signRequest({1424getHeader: function(header) {1425return self.getHeader(header, self.headers)1426},1427setHeader: function(header, value) {1428self.setHeader(header, value)1429},1430method: self.method,1431path: self.path1432}, opts)1433debug('httpSignature authorization', self.getHeader('authorization'))14341435return self1436}1437Request.prototype.hawk = function (opts) {1438var self = this1439self.setHeader('Authorization', hawk.client.header(self.uri, self.method, opts).field)1440}1441Request.prototype.oauth = function (_oauth) {1442var self = this14431444self._oauth.onRequest(_oauth)14451446return self1447}14481449Request.prototype.jar = function (jar) {1450var self = this1451var cookies14521453if (self._redirect.redirectsFollowed === 0) {1454self.originalCookieHeader = self.getHeader('cookie')1455}14561457if (!jar) {1458// disable cookies1459cookies = false1460self._disableCookies = true1461} else {1462var targetCookieJar = (jar && jar.getCookieString) ? jar : globalCookieJar1463var urihref = self.uri.href1464//fetch cookie in the Specified host1465if (targetCookieJar) {1466cookies = targetCookieJar.getCookieString(urihref)1467}1468}14691470//if need cookie and cookie is not empty1471if (cookies && cookies.length) {1472if (self.originalCookieHeader) {1473// Don't overwrite existing Cookie header1474self.setHeader('cookie', self.originalCookieHeader + '; ' + cookies)1475} else {1476self.setHeader('cookie', cookies)1477}1478}1479self._jar = jar1480return self1481}148214831484// Stream API1485Request.prototype.pipe = function (dest, opts) {1486var self = this14871488if (self.response) {1489if (self._destdata) {1490self.emit('error', new Error('You cannot pipe after data has been emitted from the response.'))1491} else if (self._ended) {1492self.emit('error', new Error('You cannot pipe after the response has been ended.'))1493} else {1494stream.Stream.prototype.pipe.call(self, dest, opts)1495self.pipeDest(dest)1496return dest1497}1498} else {1499self.dests.push(dest)1500stream.Stream.prototype.pipe.call(self, dest, opts)1501return dest1502}1503}1504Request.prototype.write = function () {1505var self = this1506if (!self._started) {1507self.start()1508}1509return self.req.write.apply(self.req, arguments)1510}1511Request.prototype.end = function (chunk) {1512var self = this1513if (chunk) {1514self.write(chunk)1515}1516if (!self._started) {1517self.start()1518}1519self.req.end()1520}1521Request.prototype.pause = function () {1522var self = this1523if (!self.responseContent) {1524self._paused = true1525} else {1526self.responseContent.pause.apply(self.responseContent, arguments)1527}1528}1529Request.prototype.resume = function () {1530var self = this1531if (!self.responseContent) {1532self._paused = false1533} else {1534self.responseContent.resume.apply(self.responseContent, arguments)1535}1536}1537Request.prototype.destroy = function () {1538var self = this1539if (!self._ended) {1540self.end()1541} else if (self.response) {1542self.response.destroy()1543}1544}15451546Request.defaultProxyHeaderWhiteList =1547defaultProxyHeaderWhiteList.slice()15481549Request.defaultProxyHeaderExclusiveList =1550defaultProxyHeaderExclusiveList.slice()15511552// Exports15531554Request.prototype.toJSON = requestToJSON1555module.exports = Request155615571558