request.js 49 KB

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