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