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