1// Copyright Joyent, Inc. and other Node contributors. 2// 3// Permission is hereby granted, free of charge, to any person obtaining a 4// copy of this software and associated documentation files (the 5// "Software"), to deal in the Software without restriction, including 6// without limitation the rights to use, copy, modify, merge, publish, 7// distribute, sublicense, and/or sell copies of the Software, and to permit 8// persons to whom the Software is furnished to do so, subject to the 9// following conditions: 10// 11// The above copyright notice and this permission notice shall be included 12// in all copies or substantial portions of the Software. 13// 14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20// USE OR OTHER DEALINGS IN THE SOFTWARE. 21 22'use strict'; 23 24const { 25 ArrayIsArray, 26 Boolean, 27 Error, 28 FunctionPrototypeCall, 29 NumberIsFinite, 30 ObjectAssign, 31 ObjectKeys, 32 ObjectSetPrototypeOf, 33 ReflectApply, 34 RegExpPrototypeExec, 35 String, 36 StringPrototypeCharCodeAt, 37 StringPrototypeIncludes, 38 StringPrototypeIndexOf, 39 StringPrototypeToUpperCase, 40 Symbol, 41 TypedArrayPrototypeSlice, 42} = primordials; 43 44const net = require('net'); 45const assert = require('internal/assert'); 46const { 47 kEmptyObject, 48 once, 49} = require('internal/util'); 50const { 51 _checkIsHttpToken: checkIsHttpToken, 52 freeParser, 53 parsers, 54 HTTPParser, 55 isLenient, 56 prepareError, 57} = require('_http_common'); 58const { 59 kUniqueHeaders, 60 parseUniqueHeadersOption, 61 OutgoingMessage, 62} = require('_http_outgoing'); 63const Agent = require('_http_agent'); 64const { Buffer } = require('buffer'); 65const { defaultTriggerAsyncIdScope } = require('internal/async_hooks'); 66const { URL, urlToHttpOptions, isURL } = require('internal/url'); 67const { 68 kOutHeaders, 69 kNeedDrain, 70 isTraceHTTPEnabled, 71 traceBegin, 72 traceEnd, 73 getNextTraceEventId, 74} = require('internal/http'); 75const { connResetException, codes } = require('internal/errors'); 76const { 77 ERR_HTTP_HEADERS_SENT, 78 ERR_INVALID_ARG_TYPE, 79 ERR_INVALID_HTTP_TOKEN, 80 ERR_INVALID_PROTOCOL, 81 ERR_UNESCAPED_CHARACTERS, 82} = codes; 83const { 84 validateInteger, 85 validateBoolean, 86} = require('internal/validators'); 87const { getTimerDuration } = require('internal/timers'); 88const { 89 DTRACE_HTTP_CLIENT_REQUEST, 90 DTRACE_HTTP_CLIENT_RESPONSE, 91} = require('internal/dtrace'); 92 93const { 94 hasObserver, 95 startPerf, 96 stopPerf, 97} = require('internal/perf/observe'); 98 99const kClientRequestStatistics = Symbol('ClientRequestStatistics'); 100 101const dc = require('diagnostics_channel'); 102const onClientRequestStartChannel = dc.channel('http.client.request.start'); 103const onClientResponseFinishChannel = dc.channel('http.client.response.finish'); 104 105const { addAbortSignal, finished } = require('stream'); 106 107let debug = require('internal/util/debuglog').debuglog('http', (fn) => { 108 debug = fn; 109}); 110 111const INVALID_PATH_REGEX = /[^\u0021-\u00ff]/; 112const kError = Symbol('kError'); 113 114const kLenientAll = HTTPParser.kLenientAll | 0; 115const kLenientNone = HTTPParser.kLenientNone | 0; 116 117const HTTP_CLIENT_TRACE_EVENT_NAME = 'http.client.request'; 118 119function validateHost(host, name) { 120 if (host !== null && host !== undefined && typeof host !== 'string') { 121 throw new ERR_INVALID_ARG_TYPE(`options.${name}`, 122 ['string', 'undefined', 'null'], 123 host); 124 } 125 return host; 126} 127 128class HTTPClientAsyncResource { 129 constructor(type, req) { 130 this.type = type; 131 this.req = req; 132 } 133} 134 135function ClientRequest(input, options, cb) { 136 FunctionPrototypeCall(OutgoingMessage, this); 137 138 if (typeof input === 'string') { 139 const urlStr = input; 140 input = urlToHttpOptions(new URL(urlStr)); 141 } else if (isURL(input)) { 142 // url.URL instance 143 input = urlToHttpOptions(input); 144 } else { 145 cb = options; 146 options = input; 147 input = null; 148 } 149 150 if (typeof options === 'function') { 151 cb = options; 152 options = input || kEmptyObject; 153 } else { 154 options = ObjectAssign(input || {}, options); 155 } 156 157 let agent = options.agent; 158 const defaultAgent = options._defaultAgent || Agent.globalAgent; 159 if (agent === false) { 160 agent = new defaultAgent.constructor(); 161 } else if (agent === null || agent === undefined) { 162 if (typeof options.createConnection !== 'function') { 163 agent = defaultAgent; 164 } 165 // Explicitly pass through this statement as agent will not be used 166 // when createConnection is provided. 167 } else if (typeof agent.addRequest !== 'function') { 168 throw new ERR_INVALID_ARG_TYPE('options.agent', 169 ['Agent-like Object', 'undefined', 'false'], 170 agent); 171 } 172 this.agent = agent; 173 174 const protocol = options.protocol || defaultAgent.protocol; 175 let expectedProtocol = defaultAgent.protocol; 176 if (this.agent && this.agent.protocol) 177 expectedProtocol = this.agent.protocol; 178 179 if (options.path) { 180 const path = String(options.path); 181 if (RegExpPrototypeExec(INVALID_PATH_REGEX, path) !== null) { 182 debug('Path contains unescaped characters: "%s"', path); 183 throw new ERR_UNESCAPED_CHARACTERS('Request path'); 184 } 185 } 186 187 if (protocol !== expectedProtocol) { 188 throw new ERR_INVALID_PROTOCOL(protocol, expectedProtocol); 189 } 190 191 const defaultPort = options.defaultPort || 192 (this.agent && this.agent.defaultPort); 193 194 const port = options.port = options.port || defaultPort || 80; 195 const host = options.host = validateHost(options.hostname, 'hostname') || 196 validateHost(options.host, 'host') || 'localhost'; 197 198 const setHost = (options.setHost === undefined || Boolean(options.setHost)); 199 200 this.socketPath = options.socketPath; 201 202 if (options.timeout !== undefined) 203 this.timeout = getTimerDuration(options.timeout, 'timeout'); 204 205 const signal = options.signal; 206 if (signal) { 207 addAbortSignal(signal, this); 208 } 209 let method = options.method; 210 const methodIsString = (typeof method === 'string'); 211 if (method !== null && method !== undefined && !methodIsString) { 212 throw new ERR_INVALID_ARG_TYPE('options.method', 'string', method); 213 } 214 215 if (methodIsString && method) { 216 if (!checkIsHttpToken(method)) { 217 throw new ERR_INVALID_HTTP_TOKEN('Method', method); 218 } 219 method = this.method = StringPrototypeToUpperCase(method); 220 } else { 221 method = this.method = 'GET'; 222 } 223 224 const maxHeaderSize = options.maxHeaderSize; 225 if (maxHeaderSize !== undefined) 226 validateInteger(maxHeaderSize, 'maxHeaderSize', 0); 227 this.maxHeaderSize = maxHeaderSize; 228 229 const insecureHTTPParser = options.insecureHTTPParser; 230 if (insecureHTTPParser !== undefined) { 231 validateBoolean(insecureHTTPParser, 'options.insecureHTTPParser'); 232 } 233 234 this.insecureHTTPParser = insecureHTTPParser; 235 236 if (options.joinDuplicateHeaders !== undefined) { 237 validateBoolean(options.joinDuplicateHeaders, 'options.joinDuplicateHeaders'); 238 } 239 240 this.joinDuplicateHeaders = options.joinDuplicateHeaders; 241 242 this.path = options.path || '/'; 243 if (cb) { 244 this.once('response', cb); 245 } 246 247 if (method === 'GET' || 248 method === 'HEAD' || 249 method === 'DELETE' || 250 method === 'OPTIONS' || 251 method === 'TRACE' || 252 method === 'CONNECT') { 253 this.useChunkedEncodingByDefault = false; 254 } else { 255 this.useChunkedEncodingByDefault = true; 256 } 257 258 this._ended = false; 259 this.res = null; 260 this.aborted = false; 261 this.timeoutCb = null; 262 this.upgradeOrConnect = false; 263 this.parser = null; 264 this.maxHeadersCount = null; 265 this.reusedSocket = false; 266 this.host = host; 267 this.protocol = protocol; 268 269 if (this.agent) { 270 // If there is an agent we should default to Connection:keep-alive, 271 // but only if the Agent will actually reuse the connection! 272 // If it's not a keepAlive agent, and the maxSockets==Infinity, then 273 // there's never a case where this socket will actually be reused 274 if (!this.agent.keepAlive && !NumberIsFinite(this.agent.maxSockets)) { 275 this._last = true; 276 this.shouldKeepAlive = false; 277 } else { 278 this._last = false; 279 this.shouldKeepAlive = true; 280 } 281 } 282 283 const headersArray = ArrayIsArray(options.headers); 284 if (!headersArray) { 285 if (options.headers) { 286 const keys = ObjectKeys(options.headers); 287 // Retain for(;;) loop for performance reasons 288 // Refs: https://github.com/nodejs/node/pull/30958 289 for (let i = 0; i < keys.length; i++) { 290 const key = keys[i]; 291 this.setHeader(key, options.headers[key]); 292 } 293 } 294 295 if (host && !this.getHeader('host') && setHost) { 296 let hostHeader = host; 297 298 // For the Host header, ensure that IPv6 addresses are enclosed 299 // in square brackets, as defined by URI formatting 300 // https://tools.ietf.org/html/rfc3986#section-3.2.2 301 const posColon = StringPrototypeIndexOf(hostHeader, ':'); 302 if (posColon !== -1 && 303 StringPrototypeIncludes(hostHeader, ':', posColon + 1) && 304 StringPrototypeCharCodeAt(hostHeader, 0) !== 91/* '[' */) { 305 hostHeader = `[${hostHeader}]`; 306 } 307 308 if (port && +port !== defaultPort) { 309 hostHeader += ':' + port; 310 } 311 this.setHeader('Host', hostHeader); 312 } 313 314 if (options.auth && !this.getHeader('Authorization')) { 315 this.setHeader('Authorization', 'Basic ' + 316 Buffer.from(options.auth).toString('base64')); 317 } 318 319 if (this.getHeader('expect')) { 320 if (this._header) { 321 throw new ERR_HTTP_HEADERS_SENT('render'); 322 } 323 324 this._storeHeader(this.method + ' ' + this.path + ' HTTP/1.1\r\n', 325 this[kOutHeaders]); 326 } 327 } else { 328 this._storeHeader(this.method + ' ' + this.path + ' HTTP/1.1\r\n', 329 options.headers); 330 } 331 332 this[kUniqueHeaders] = parseUniqueHeadersOption(options.uniqueHeaders); 333 334 let optsWithoutSignal = options; 335 if (optsWithoutSignal.signal) { 336 optsWithoutSignal = ObjectAssign({}, options); 337 delete optsWithoutSignal.signal; 338 } 339 340 // initiate connection 341 if (this.agent) { 342 this.agent.addRequest(this, optsWithoutSignal); 343 } else { 344 // No agent, default to Connection:close. 345 this._last = true; 346 this.shouldKeepAlive = false; 347 if (typeof optsWithoutSignal.createConnection === 'function') { 348 const oncreate = once((err, socket) => { 349 if (err) { 350 process.nextTick(() => this.emit('error', err)); 351 } else { 352 this.onSocket(socket); 353 } 354 }); 355 356 try { 357 const newSocket = optsWithoutSignal.createConnection(optsWithoutSignal, 358 oncreate); 359 if (newSocket) { 360 oncreate(null, newSocket); 361 } 362 } catch (err) { 363 oncreate(err); 364 } 365 } else { 366 debug('CLIENT use net.createConnection', optsWithoutSignal); 367 this.onSocket(net.createConnection(optsWithoutSignal)); 368 } 369 } 370} 371ObjectSetPrototypeOf(ClientRequest.prototype, OutgoingMessage.prototype); 372ObjectSetPrototypeOf(ClientRequest, OutgoingMessage); 373 374ClientRequest.prototype._finish = function _finish() { 375 DTRACE_HTTP_CLIENT_REQUEST(this, this.socket); 376 FunctionPrototypeCall(OutgoingMessage.prototype._finish, this); 377 if (hasObserver('http')) { 378 startPerf(this, kClientRequestStatistics, { 379 type: 'http', 380 name: 'HttpClient', 381 detail: { 382 req: { 383 method: this.method, 384 url: `${this.protocol}//${this.host}${this.path}`, 385 headers: typeof this.getHeaders === 'function' ? this.getHeaders() : {}, 386 }, 387 }, 388 }); 389 } 390 if (onClientRequestStartChannel.hasSubscribers) { 391 onClientRequestStartChannel.publish({ 392 request: this, 393 }); 394 } 395 if (isTraceHTTPEnabled()) { 396 this._traceEventId = getNextTraceEventId(); 397 traceBegin(HTTP_CLIENT_TRACE_EVENT_NAME, this._traceEventId); 398 } 399}; 400 401ClientRequest.prototype._implicitHeader = function _implicitHeader() { 402 if (this._header) { 403 throw new ERR_HTTP_HEADERS_SENT('render'); 404 } 405 this._storeHeader(this.method + ' ' + this.path + ' HTTP/1.1\r\n', 406 this[kOutHeaders]); 407}; 408 409ClientRequest.prototype.abort = function abort() { 410 if (this.aborted) { 411 return; 412 } 413 this.aborted = true; 414 process.nextTick(emitAbortNT, this); 415 this.destroy(); 416}; 417 418ClientRequest.prototype.destroy = function destroy(err) { 419 if (this.destroyed) { 420 return this; 421 } 422 this.destroyed = true; 423 424 // If we're aborting, we don't care about any more response data. 425 if (this.res) { 426 this.res._dump(); 427 } 428 429 this[kError] = err; 430 this.socket?.destroy(err); 431 432 return this; 433}; 434 435function emitAbortNT(req) { 436 req.emit('abort'); 437} 438 439function ondrain() { 440 const msg = this._httpMessage; 441 if (msg && !msg.finished && msg[kNeedDrain]) { 442 msg[kNeedDrain] = false; 443 msg.emit('drain'); 444 } 445} 446 447function socketCloseListener() { 448 const socket = this; 449 const req = socket._httpMessage; 450 debug('HTTP socket close'); 451 452 // NOTE: It's important to get parser here, because it could be freed by 453 // the `socketOnData`. 454 const parser = socket.parser; 455 const res = req.res; 456 457 req.destroyed = true; 458 if (res) { 459 // Socket closed before we emitted 'end' below. 460 if (!res.complete) { 461 res.destroy(connResetException('aborted')); 462 } 463 req._closed = true; 464 req.emit('close'); 465 if (!res.aborted && res.readable) { 466 res.push(null); 467 } 468 } else { 469 if (!req.socket._hadError) { 470 // This socket error fired before we started to 471 // receive a response. The error needs to 472 // fire on the request. 473 req.socket._hadError = true; 474 req.emit('error', connResetException('socket hang up')); 475 } 476 req._closed = true; 477 req.emit('close'); 478 } 479 480 // Too bad. That output wasn't getting written. 481 // This is pretty terrible that it doesn't raise an error. 482 // Fixed better in v0.10 483 if (req.outputData) 484 req.outputData.length = 0; 485 486 if (parser) { 487 parser.finish(); 488 freeParser(parser, req, socket); 489 } 490} 491 492function socketErrorListener(err) { 493 const socket = this; 494 const req = socket._httpMessage; 495 debug('SOCKET ERROR:', err.message, err.stack); 496 497 if (req) { 498 // For Safety. Some additional errors might fire later on 499 // and we need to make sure we don't double-fire the error event. 500 req.socket._hadError = true; 501 req.emit('error', err); 502 } 503 504 const parser = socket.parser; 505 if (parser) { 506 parser.finish(); 507 freeParser(parser, req, socket); 508 } 509 510 // Ensure that no further data will come out of the socket 511 socket.removeListener('data', socketOnData); 512 socket.removeListener('end', socketOnEnd); 513 socket.destroy(); 514} 515 516function socketOnEnd() { 517 const socket = this; 518 const req = this._httpMessage; 519 const parser = this.parser; 520 521 if (!req.res && !req.socket._hadError) { 522 // If we don't have a response then we know that the socket 523 // ended prematurely and we need to emit an error on the request. 524 req.socket._hadError = true; 525 req.emit('error', connResetException('socket hang up')); 526 } 527 if (parser) { 528 parser.finish(); 529 freeParser(parser, req, socket); 530 } 531 socket.destroy(); 532} 533 534function socketOnData(d) { 535 const socket = this; 536 const req = this._httpMessage; 537 const parser = this.parser; 538 539 assert(parser && parser.socket === socket); 540 541 const ret = parser.execute(d); 542 if (ret instanceof Error) { 543 prepareError(ret, parser, d); 544 debug('parse error', ret); 545 freeParser(parser, req, socket); 546 socket.removeListener('data', socketOnData); 547 socket.removeListener('end', socketOnEnd); 548 socket.destroy(); 549 req.socket._hadError = true; 550 req.emit('error', ret); 551 } else if (parser.incoming && parser.incoming.upgrade) { 552 // Upgrade (if status code 101) or CONNECT 553 const bytesParsed = ret; 554 const res = parser.incoming; 555 req.res = res; 556 557 socket.removeListener('data', socketOnData); 558 socket.removeListener('end', socketOnEnd); 559 socket.removeListener('drain', ondrain); 560 561 if (req.timeoutCb) socket.removeListener('timeout', req.timeoutCb); 562 socket.removeListener('timeout', responseOnTimeout); 563 564 parser.finish(); 565 freeParser(parser, req, socket); 566 567 const bodyHead = TypedArrayPrototypeSlice(d, bytesParsed, d.length); 568 569 const eventName = req.method === 'CONNECT' ? 'connect' : 'upgrade'; 570 if (req.listenerCount(eventName) > 0) { 571 req.upgradeOrConnect = true; 572 573 // detach the socket 574 socket.emit('agentRemove'); 575 socket.removeListener('close', socketCloseListener); 576 socket.removeListener('error', socketErrorListener); 577 578 socket._httpMessage = null; 579 socket.readableFlowing = null; 580 581 req.emit(eventName, res, socket, bodyHead); 582 req.destroyed = true; 583 req._closed = true; 584 req.emit('close'); 585 } else { 586 // Requested Upgrade or used CONNECT method, but have no handler. 587 socket.destroy(); 588 } 589 } else if (parser.incoming && parser.incoming.complete && 590 // When the status code is informational (100, 102-199), 591 // the server will send a final response after this client 592 // sends a request body, so we must not free the parser. 593 // 101 (Switching Protocols) and all other status codes 594 // should be processed normally. 595 !statusIsInformational(parser.incoming.statusCode)) { 596 socket.removeListener('data', socketOnData); 597 socket.removeListener('end', socketOnEnd); 598 socket.removeListener('drain', ondrain); 599 freeParser(parser, req, socket); 600 } 601} 602 603function statusIsInformational(status) { 604 // 100 (Continue) RFC7231 Section 6.2.1 605 // 102 (Processing) RFC2518 606 // 103 (Early Hints) RFC8297 607 // 104-199 (Unassigned) 608 return (status < 200 && status >= 100 && status !== 101); 609} 610 611// client 612function parserOnIncomingClient(res, shouldKeepAlive) { 613 const socket = this.socket; 614 const req = socket._httpMessage; 615 616 debug('AGENT incoming response!'); 617 618 if (req.res) { 619 // We already have a response object, this means the server 620 // sent a double response. 621 socket.destroy(); 622 return 0; // No special treatment. 623 } 624 req.res = res; 625 626 // Skip body and treat as Upgrade. 627 if (res.upgrade) 628 return 2; 629 630 // Responses to CONNECT request is handled as Upgrade. 631 const method = req.method; 632 if (method === 'CONNECT') { 633 res.upgrade = true; 634 return 2; // Skip body and treat as Upgrade. 635 } 636 637 if (statusIsInformational(res.statusCode)) { 638 // Restart the parser, as this is a 1xx informational message. 639 req.res = null; // Clear res so that we don't hit double-responses. 640 // Maintain compatibility by sending 100-specific events 641 if (res.statusCode === 100) { 642 req.emit('continue'); 643 } 644 // Send information events to all 1xx responses except 101 Upgrade. 645 req.emit('information', { 646 statusCode: res.statusCode, 647 statusMessage: res.statusMessage, 648 httpVersion: res.httpVersion, 649 httpVersionMajor: res.httpVersionMajor, 650 httpVersionMinor: res.httpVersionMinor, 651 headers: res.headers, 652 rawHeaders: res.rawHeaders, 653 }); 654 655 return 1; // Skip body but don't treat as Upgrade. 656 } 657 658 if (req.shouldKeepAlive && !shouldKeepAlive && !req.upgradeOrConnect) { 659 // Server MUST respond with Connection:keep-alive for us to enable it. 660 // If we've been upgraded (via WebSockets) we also shouldn't try to 661 // keep the connection open. 662 req.shouldKeepAlive = false; 663 } 664 665 DTRACE_HTTP_CLIENT_RESPONSE(socket, req); 666 if (req[kClientRequestStatistics] && hasObserver('http')) { 667 stopPerf(req, kClientRequestStatistics, { 668 detail: { 669 res: { 670 statusCode: res.statusCode, 671 statusMessage: res.statusMessage, 672 headers: res.headers, 673 }, 674 }, 675 }); 676 } 677 if (onClientResponseFinishChannel.hasSubscribers) { 678 onClientResponseFinishChannel.publish({ 679 request: req, 680 response: res, 681 }); 682 } 683 if (isTraceHTTPEnabled() && typeof req._traceEventId === 'number') { 684 traceEnd(HTTP_CLIENT_TRACE_EVENT_NAME, req._traceEventId, { 685 path: req.path, 686 statusCode: res.statusCode, 687 }); 688 } 689 req.res = res; 690 res.req = req; 691 692 // Add our listener first, so that we guarantee socket cleanup 693 res.on('end', responseOnEnd); 694 req.on('finish', requestOnFinish); 695 socket.on('timeout', responseOnTimeout); 696 697 // If the user did not listen for the 'response' event, then they 698 // can't possibly read the data, so we ._dump() it into the void 699 // so that the socket doesn't hang there in a paused state. 700 if (req.aborted || !req.emit('response', res)) 701 res._dump(); 702 703 if (method === 'HEAD') 704 return 1; // Skip body but don't treat as Upgrade. 705 706 if (res.statusCode === 304) { 707 res.complete = true; 708 return 1; // Skip body as there won't be any 709 } 710 711 return 0; // No special treatment. 712} 713 714// client 715function responseKeepAlive(req) { 716 const socket = req.socket; 717 718 debug('AGENT socket keep-alive'); 719 if (req.timeoutCb) { 720 socket.setTimeout(0, req.timeoutCb); 721 req.timeoutCb = null; 722 } 723 socket.removeListener('close', socketCloseListener); 724 socket.removeListener('error', socketErrorListener); 725 socket.removeListener('data', socketOnData); 726 socket.removeListener('end', socketOnEnd); 727 728 // TODO(ronag): Between here and emitFreeNT the socket 729 // has no 'error' handler. 730 731 // There are cases where _handle === null. Avoid those. Passing undefined to 732 // nextTick() will call getDefaultTriggerAsyncId() to retrieve the id. 733 const asyncId = socket._handle ? socket._handle.getAsyncId() : undefined; 734 // Mark this socket as available, AFTER user-added end 735 // handlers have a chance to run. 736 defaultTriggerAsyncIdScope(asyncId, process.nextTick, emitFreeNT, req); 737 738 req.destroyed = true; 739 if (req.res) { 740 // Detach socket from IncomingMessage to avoid destroying the freed 741 // socket in IncomingMessage.destroy(). 742 req.res.socket = null; 743 } 744} 745 746function responseOnEnd() { 747 const req = this.req; 748 const socket = req.socket; 749 750 if (socket) { 751 if (req.timeoutCb) socket.removeListener('timeout', emitRequestTimeout); 752 socket.removeListener('timeout', responseOnTimeout); 753 } 754 755 req._ended = true; 756 757 if (!req.shouldKeepAlive) { 758 if (socket.writable) { 759 debug('AGENT socket.destroySoon()'); 760 if (typeof socket.destroySoon === 'function') 761 socket.destroySoon(); 762 else 763 socket.end(); 764 } 765 assert(!socket.writable); 766 } else if (req.writableFinished && !this.aborted) { 767 assert(req.finished); 768 // We can assume `req.finished` means all data has been written since: 769 // - `'responseOnEnd'` means we have been assigned a socket. 770 // - when we have a socket we write directly to it without buffering. 771 // - `req.finished` means `end()` has been called and no further data. 772 // can be written 773 // In addition, `req.writableFinished` means all data written has been 774 // accepted by the kernel. (i.e. the `req.socket` is drained).Without 775 // this constraint, we may assign a non drained socket to a request. 776 responseKeepAlive(req); 777 } 778} 779 780function responseOnTimeout() { 781 const req = this._httpMessage; 782 if (!req) return; 783 const res = req.res; 784 if (!res) return; 785 res.emit('timeout'); 786} 787 788// This function is necessary in the case where we receive the entire response 789// from the server before we finish sending out the request. 790function requestOnFinish() { 791 const req = this; 792 793 if (req.shouldKeepAlive && req._ended) 794 responseKeepAlive(req); 795} 796 797function emitFreeNT(req) { 798 req._closed = true; 799 req.emit('close'); 800 if (req.socket) { 801 req.socket.emit('free'); 802 } 803} 804 805function tickOnSocket(req, socket) { 806 const parser = parsers.alloc(); 807 req.socket = socket; 808 const lenient = req.insecureHTTPParser === undefined ? 809 isLenient() : req.insecureHTTPParser; 810 parser.initialize(HTTPParser.RESPONSE, 811 new HTTPClientAsyncResource('HTTPINCOMINGMESSAGE', req), 812 req.maxHeaderSize || 0, 813 lenient ? kLenientAll : kLenientNone); 814 parser.socket = socket; 815 parser.outgoing = req; 816 req.parser = parser; 817 818 socket.parser = parser; 819 socket._httpMessage = req; 820 821 // Propagate headers limit from request object to parser 822 if (typeof req.maxHeadersCount === 'number') { 823 parser.maxHeaderPairs = req.maxHeadersCount << 1; 824 } 825 826 parser.joinDuplicateHeaders = req.joinDuplicateHeaders; 827 828 parser.onIncoming = parserOnIncomingClient; 829 socket.on('error', socketErrorListener); 830 socket.on('data', socketOnData); 831 socket.on('end', socketOnEnd); 832 socket.on('close', socketCloseListener); 833 socket.on('drain', ondrain); 834 835 if ( 836 req.timeout !== undefined || 837 (req.agent && req.agent.options && req.agent.options.timeout) 838 ) { 839 listenSocketTimeout(req); 840 } 841 req.emit('socket', socket); 842} 843 844function emitRequestTimeout() { 845 const req = this._httpMessage; 846 if (req) { 847 req.emit('timeout'); 848 } 849} 850 851function listenSocketTimeout(req) { 852 if (req.timeoutCb) { 853 return; 854 } 855 // Set timeoutCb so it will get cleaned up on request end. 856 req.timeoutCb = emitRequestTimeout; 857 // Delegate socket timeout event. 858 if (req.socket) { 859 req.socket.once('timeout', emitRequestTimeout); 860 } else { 861 req.on('socket', (socket) => { 862 socket.once('timeout', emitRequestTimeout); 863 }); 864 } 865} 866 867ClientRequest.prototype.onSocket = function onSocket(socket, err) { 868 // TODO(ronag): Between here and onSocketNT the socket 869 // has no 'error' handler. 870 process.nextTick(onSocketNT, this, socket, err); 871}; 872 873function onSocketNT(req, socket, err) { 874 if (req.destroyed || err) { 875 req.destroyed = true; 876 877 function _destroy(req, err) { 878 if (!req.aborted && !err) { 879 err = connResetException('socket hang up'); 880 } 881 if (err) { 882 req.emit('error', err); 883 } 884 req._closed = true; 885 req.emit('close'); 886 } 887 888 if (socket) { 889 if (!err && req.agent && !socket.destroyed) { 890 socket.emit('free'); 891 } else { 892 finished(socket.destroy(err || req[kError]), (er) => { 893 if (er?.code === 'ERR_STREAM_PREMATURE_CLOSE') { 894 er = null; 895 } 896 _destroy(req, er || err); 897 }); 898 return; 899 } 900 } 901 902 _destroy(req, err || req[kError]); 903 } else { 904 tickOnSocket(req, socket); 905 req._flush(); 906 } 907} 908 909ClientRequest.prototype._deferToConnect = _deferToConnect; 910function _deferToConnect(method, arguments_) { 911 // This function is for calls that need to happen once the socket is 912 // assigned to this request and writable. It's an important promisy 913 // thing for all the socket calls that happen either now 914 // (when a socket is assigned) or in the future (when a socket gets 915 // assigned out of the pool and is eventually writable). 916 917 const callSocketMethod = () => { 918 if (method) 919 ReflectApply(this.socket[method], this.socket, arguments_); 920 }; 921 922 const onSocket = () => { 923 if (this.socket.writable) { 924 callSocketMethod(); 925 } else { 926 this.socket.once('connect', callSocketMethod); 927 } 928 }; 929 930 if (!this.socket) { 931 this.once('socket', onSocket); 932 } else { 933 onSocket(); 934 } 935} 936 937ClientRequest.prototype.setTimeout = function setTimeout(msecs, callback) { 938 if (this._ended) { 939 return this; 940 } 941 942 listenSocketTimeout(this); 943 msecs = getTimerDuration(msecs, 'msecs'); 944 if (callback) this.once('timeout', callback); 945 946 if (this.socket) { 947 setSocketTimeout(this.socket, msecs); 948 } else { 949 this.once('socket', (sock) => setSocketTimeout(sock, msecs)); 950 } 951 952 return this; 953}; 954 955function setSocketTimeout(sock, msecs) { 956 if (sock.connecting) { 957 sock.once('connect', function() { 958 sock.setTimeout(msecs); 959 }); 960 } else { 961 sock.setTimeout(msecs); 962 } 963} 964 965ClientRequest.prototype.setNoDelay = function setNoDelay(noDelay) { 966 this._deferToConnect('setNoDelay', [noDelay]); 967}; 968 969ClientRequest.prototype.setSocketKeepAlive = 970 function setSocketKeepAlive(enable, initialDelay) { 971 this._deferToConnect('setKeepAlive', [enable, initialDelay]); 972 }; 973 974ClientRequest.prototype.clearTimeout = function clearTimeout(cb) { 975 this.setTimeout(0, cb); 976}; 977 978module.exports = { 979 ClientRequest, 980}; 981