mirror of
				https://github.com/cp6/my-idlers.git
				synced 2025-11-04 08:09:09 +00:00 
			
		
		
		
	
		
			
	
	
		
			446 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
		
		
			
		
	
	
			446 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 
								 | 
							
								'use strict';
							 | 
						||
| 
								 | 
							
								const http2 = require('http2');
							 | 
						||
| 
								 | 
							
								const {Writable} = require('stream');
							 | 
						||
| 
								 | 
							
								const {Agent, globalAgent} = require('./agent');
							 | 
						||
| 
								 | 
							
								const IncomingMessage = require('./incoming-message');
							 | 
						||
| 
								 | 
							
								const urlToOptions = require('./utils/url-to-options');
							 | 
						||
| 
								 | 
							
								const proxyEvents = require('./utils/proxy-events');
							 | 
						||
| 
								 | 
							
								const isRequestPseudoHeader = require('./utils/is-request-pseudo-header');
							 | 
						||
| 
								 | 
							
								const {
							 | 
						||
| 
								 | 
							
									ERR_INVALID_ARG_TYPE,
							 | 
						||
| 
								 | 
							
									ERR_INVALID_PROTOCOL,
							 | 
						||
| 
								 | 
							
									ERR_HTTP_HEADERS_SENT,
							 | 
						||
| 
								 | 
							
									ERR_INVALID_HTTP_TOKEN,
							 | 
						||
| 
								 | 
							
									ERR_HTTP_INVALID_HEADER_VALUE,
							 | 
						||
| 
								 | 
							
									ERR_INVALID_CHAR
							 | 
						||
| 
								 | 
							
								} = require('./utils/errors');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const {
							 | 
						||
| 
								 | 
							
									HTTP2_HEADER_STATUS,
							 | 
						||
| 
								 | 
							
									HTTP2_HEADER_METHOD,
							 | 
						||
| 
								 | 
							
									HTTP2_HEADER_PATH,
							 | 
						||
| 
								 | 
							
									HTTP2_METHOD_CONNECT
							 | 
						||
| 
								 | 
							
								} = http2.constants;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const kHeaders = Symbol('headers');
							 | 
						||
| 
								 | 
							
								const kOrigin = Symbol('origin');
							 | 
						||
| 
								 | 
							
								const kSession = Symbol('session');
							 | 
						||
| 
								 | 
							
								const kOptions = Symbol('options');
							 | 
						||
| 
								 | 
							
								const kFlushedHeaders = Symbol('flushedHeaders');
							 | 
						||
| 
								 | 
							
								const kJobs = Symbol('jobs');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const isValidHttpToken = /^[\^`\-\w!#$%&*+.|~]+$/;
							 | 
						||
| 
								 | 
							
								const isInvalidHeaderValue = /[^\t\u0020-\u007E\u0080-\u00FF]/;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class ClientRequest extends Writable {
							 | 
						||
| 
								 | 
							
									constructor(input, options, callback) {
							 | 
						||
| 
								 | 
							
										super({
							 | 
						||
| 
								 | 
							
											autoDestroy: false
							 | 
						||
| 
								 | 
							
										});
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										const hasInput = typeof input === 'string' || input instanceof URL;
							 | 
						||
| 
								 | 
							
										if (hasInput) {
							 | 
						||
| 
								 | 
							
											input = urlToOptions(input instanceof URL ? input : new URL(input));
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if (typeof options === 'function' || options === undefined) {
							 | 
						||
| 
								 | 
							
											// (options, callback)
							 | 
						||
| 
								 | 
							
											callback = options;
							 | 
						||
| 
								 | 
							
											options = hasInput ? input : {...input};
							 | 
						||
| 
								 | 
							
										} else {
							 | 
						||
| 
								 | 
							
											// (input, options, callback)
							 | 
						||
| 
								 | 
							
											options = {...input, ...options};
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if (options.h2session) {
							 | 
						||
| 
								 | 
							
											this[kSession] = options.h2session;
							 | 
						||
| 
								 | 
							
										} else if (options.agent === false) {
							 | 
						||
| 
								 | 
							
											this.agent = new Agent({maxFreeSessions: 0});
							 | 
						||
| 
								 | 
							
										} else if (typeof options.agent === 'undefined' || options.agent === null) {
							 | 
						||
| 
								 | 
							
											if (typeof options.createConnection === 'function') {
							 | 
						||
| 
								 | 
							
												// This is a workaround - we don't have to create the session on our own.
							 | 
						||
| 
								 | 
							
												this.agent = new Agent({maxFreeSessions: 0});
							 | 
						||
| 
								 | 
							
												this.agent.createConnection = options.createConnection;
							 | 
						||
| 
								 | 
							
											} else {
							 | 
						||
| 
								 | 
							
												this.agent = globalAgent;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										} else if (typeof options.agent.request === 'function') {
							 | 
						||
| 
								 | 
							
											this.agent = options.agent;
							 | 
						||
| 
								 | 
							
										} else {
							 | 
						||
| 
								 | 
							
											throw new ERR_INVALID_ARG_TYPE('options.agent', ['Agent-like Object', 'undefined', 'false'], options.agent);
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if (options.protocol && options.protocol !== 'https:') {
							 | 
						||
| 
								 | 
							
											throw new ERR_INVALID_PROTOCOL(options.protocol, 'https:');
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										const port = options.port || options.defaultPort || (this.agent && this.agent.defaultPort) || 443;
							 | 
						||
| 
								 | 
							
										const host = options.hostname || options.host || 'localhost';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Don't enforce the origin via options. It may be changed in an Agent.
							 | 
						||
| 
								 | 
							
										delete options.hostname;
							 | 
						||
| 
								 | 
							
										delete options.host;
							 | 
						||
| 
								 | 
							
										delete options.port;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										const {timeout} = options;
							 | 
						||
| 
								 | 
							
										options.timeout = undefined;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										this[kHeaders] = Object.create(null);
							 | 
						||
| 
								 | 
							
										this[kJobs] = [];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										this.socket = null;
							 | 
						||
| 
								 | 
							
										this.connection = null;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										this.method = options.method || 'GET';
							 | 
						||
| 
								 | 
							
										this.path = options.path;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										this.res = null;
							 | 
						||
| 
								 | 
							
										this.aborted = false;
							 | 
						||
| 
								 | 
							
										this.reusedSocket = false;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if (options.headers) {
							 | 
						||
| 
								 | 
							
											for (const [header, value] of Object.entries(options.headers)) {
							 | 
						||
| 
								 | 
							
												this.setHeader(header, value);
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if (options.auth && !('authorization' in this[kHeaders])) {
							 | 
						||
| 
								 | 
							
											this[kHeaders].authorization = 'Basic ' + Buffer.from(options.auth).toString('base64');
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										options.session = options.tlsSession;
							 | 
						||
| 
								 | 
							
										options.path = options.socketPath;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										this[kOptions] = options;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Clients that generate HTTP/2 requests directly SHOULD use the :authority pseudo-header field instead of the Host header field.
							 | 
						||
| 
								 | 
							
										if (port === 443) {
							 | 
						||
| 
								 | 
							
											this[kOrigin] = `https://${host}`;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											if (!(':authority' in this[kHeaders])) {
							 | 
						||
| 
								 | 
							
												this[kHeaders][':authority'] = host;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										} else {
							 | 
						||
| 
								 | 
							
											this[kOrigin] = `https://${host}:${port}`;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											if (!(':authority' in this[kHeaders])) {
							 | 
						||
| 
								 | 
							
												this[kHeaders][':authority'] = `${host}:${port}`;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if (timeout) {
							 | 
						||
| 
								 | 
							
											this.setTimeout(timeout);
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if (callback) {
							 | 
						||
| 
								 | 
							
											this.once('response', callback);
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										this[kFlushedHeaders] = false;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									get method() {
							 | 
						||
| 
								 | 
							
										return this[kHeaders][HTTP2_HEADER_METHOD];
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									set method(value) {
							 | 
						||
| 
								 | 
							
										if (value) {
							 | 
						||
| 
								 | 
							
											this[kHeaders][HTTP2_HEADER_METHOD] = value.toUpperCase();
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									get path() {
							 | 
						||
| 
								 | 
							
										return this[kHeaders][HTTP2_HEADER_PATH];
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									set path(value) {
							 | 
						||
| 
								 | 
							
										if (value) {
							 | 
						||
| 
								 | 
							
											this[kHeaders][HTTP2_HEADER_PATH] = value;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									get _mustNotHaveABody() {
							 | 
						||
| 
								 | 
							
										return this.method === 'GET' || this.method === 'HEAD' || this.method === 'DELETE';
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									_write(chunk, encoding, callback) {
							 | 
						||
| 
								 | 
							
										// https://github.com/nodejs/node/blob/654df09ae0c5e17d1b52a900a545f0664d8c7627/lib/internal/http2/util.js#L148-L156
							 | 
						||
| 
								 | 
							
										if (this._mustNotHaveABody) {
							 | 
						||
| 
								 | 
							
											callback(new Error('The GET, HEAD and DELETE methods must NOT have a body'));
							 | 
						||
| 
								 | 
							
											/* istanbul ignore next: Node.js 12 throws directly */
							 | 
						||
| 
								 | 
							
											return;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										this.flushHeaders();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										const callWrite = () => this._request.write(chunk, encoding, callback);
							 | 
						||
| 
								 | 
							
										if (this._request) {
							 | 
						||
| 
								 | 
							
											callWrite();
							 | 
						||
| 
								 | 
							
										} else {
							 | 
						||
| 
								 | 
							
											this[kJobs].push(callWrite);
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									_final(callback) {
							 | 
						||
| 
								 | 
							
										if (this.destroyed) {
							 | 
						||
| 
								 | 
							
											return;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										this.flushHeaders();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										const callEnd = () => {
							 | 
						||
| 
								 | 
							
											// For GET, HEAD and DELETE
							 | 
						||
| 
								 | 
							
											if (this._mustNotHaveABody) {
							 | 
						||
| 
								 | 
							
												callback();
							 | 
						||
| 
								 | 
							
												return;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											this._request.end(callback);
							 | 
						||
| 
								 | 
							
										};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if (this._request) {
							 | 
						||
| 
								 | 
							
											callEnd();
							 | 
						||
| 
								 | 
							
										} else {
							 | 
						||
| 
								 | 
							
											this[kJobs].push(callEnd);
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									abort() {
							 | 
						||
| 
								 | 
							
										if (this.res && this.res.complete) {
							 | 
						||
| 
								 | 
							
											return;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if (!this.aborted) {
							 | 
						||
| 
								 | 
							
											process.nextTick(() => this.emit('abort'));
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										this.aborted = true;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										this.destroy();
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									_destroy(error, callback) {
							 | 
						||
| 
								 | 
							
										if (this.res) {
							 | 
						||
| 
								 | 
							
											this.res._dump();
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if (this._request) {
							 | 
						||
| 
								 | 
							
											this._request.destroy();
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										callback(error);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									async flushHeaders() {
							 | 
						||
| 
								 | 
							
										if (this[kFlushedHeaders] || this.destroyed) {
							 | 
						||
| 
								 | 
							
											return;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										this[kFlushedHeaders] = true;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										const isConnectMethod = this.method === HTTP2_METHOD_CONNECT;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// The real magic is here
							 | 
						||
| 
								 | 
							
										const onStream = stream => {
							 | 
						||
| 
								 | 
							
											this._request = stream;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											if (this.destroyed) {
							 | 
						||
| 
								 | 
							
												stream.destroy();
							 | 
						||
| 
								 | 
							
												return;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											// Forwards `timeout`, `continue`, `close` and `error` events to this instance.
							 | 
						||
| 
								 | 
							
											if (!isConnectMethod) {
							 | 
						||
| 
								 | 
							
												proxyEvents(stream, this, ['timeout', 'continue', 'close', 'error']);
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											// Wait for the `finish` event. We don't want to emit the `response` event
							 | 
						||
| 
								 | 
							
											// before `request.end()` is called.
							 | 
						||
| 
								 | 
							
											const waitForEnd = fn => {
							 | 
						||
| 
								 | 
							
												return (...args) => {
							 | 
						||
| 
								 | 
							
													if (!this.writable && !this.destroyed) {
							 | 
						||
| 
								 | 
							
														fn(...args);
							 | 
						||
| 
								 | 
							
													} else {
							 | 
						||
| 
								 | 
							
														this.once('finish', () => {
							 | 
						||
| 
								 | 
							
															fn(...args);
							 | 
						||
| 
								 | 
							
														});
							 | 
						||
| 
								 | 
							
													}
							 | 
						||
| 
								 | 
							
												};
							 | 
						||
| 
								 | 
							
											};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											// This event tells we are ready to listen for the data.
							 | 
						||
| 
								 | 
							
											stream.once('response', waitForEnd((headers, flags, rawHeaders) => {
							 | 
						||
| 
								 | 
							
												// If we were to emit raw request stream, it would be as fast as the native approach.
							 | 
						||
| 
								 | 
							
												// Note that wrapping the raw stream in a Proxy instance won't improve the performance (already tested it).
							 | 
						||
| 
								 | 
							
												const response = new IncomingMessage(this.socket, stream.readableHighWaterMark);
							 | 
						||
| 
								 | 
							
												this.res = response;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												response.req = this;
							 | 
						||
| 
								 | 
							
												response.statusCode = headers[HTTP2_HEADER_STATUS];
							 | 
						||
| 
								 | 
							
												response.headers = headers;
							 | 
						||
| 
								 | 
							
												response.rawHeaders = rawHeaders;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												response.once('end', () => {
							 | 
						||
| 
								 | 
							
													if (this.aborted) {
							 | 
						||
| 
								 | 
							
														response.aborted = true;
							 | 
						||
| 
								 | 
							
														response.emit('aborted');
							 | 
						||
| 
								 | 
							
													} else {
							 | 
						||
| 
								 | 
							
														response.complete = true;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
														// Has no effect, just be consistent with the Node.js behavior
							 | 
						||
| 
								 | 
							
														response.socket = null;
							 | 
						||
| 
								 | 
							
														response.connection = null;
							 | 
						||
| 
								 | 
							
													}
							 | 
						||
| 
								 | 
							
												});
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												if (isConnectMethod) {
							 | 
						||
| 
								 | 
							
													response.upgrade = true;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
													// The HTTP1 API says the socket is detached here,
							 | 
						||
| 
								 | 
							
													// but we can't do that so we pass the original HTTP2 request.
							 | 
						||
| 
								 | 
							
													if (this.emit('connect', response, stream, Buffer.alloc(0))) {
							 | 
						||
| 
								 | 
							
														this.emit('close');
							 | 
						||
| 
								 | 
							
													} else {
							 | 
						||
| 
								 | 
							
														// No listeners attached, destroy the original request.
							 | 
						||
| 
								 | 
							
														stream.destroy();
							 | 
						||
| 
								 | 
							
													}
							 | 
						||
| 
								 | 
							
												} else {
							 | 
						||
| 
								 | 
							
													// Forwards data
							 | 
						||
| 
								 | 
							
													stream.on('data', chunk => {
							 | 
						||
| 
								 | 
							
														if (!response._dumped && !response.push(chunk)) {
							 | 
						||
| 
								 | 
							
															stream.pause();
							 | 
						||
| 
								 | 
							
														}
							 | 
						||
| 
								 | 
							
													});
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
													stream.once('end', () => {
							 | 
						||
| 
								 | 
							
														response.push(null);
							 | 
						||
| 
								 | 
							
													});
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
													if (!this.emit('response', response)) {
							 | 
						||
| 
								 | 
							
														// No listeners attached, dump the response.
							 | 
						||
| 
								 | 
							
														response._dump();
							 | 
						||
| 
								 | 
							
													}
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
											}));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											// Emits `information` event
							 | 
						||
| 
								 | 
							
											stream.once('headers', waitForEnd(
							 | 
						||
| 
								 | 
							
												headers => this.emit('information', {statusCode: headers[HTTP2_HEADER_STATUS]})
							 | 
						||
| 
								 | 
							
											));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											stream.once('trailers', waitForEnd((trailers, flags, rawTrailers) => {
							 | 
						||
| 
								 | 
							
												const {res} = this;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												// Assigns trailers to the response object.
							 | 
						||
| 
								 | 
							
												res.trailers = trailers;
							 | 
						||
| 
								 | 
							
												res.rawTrailers = rawTrailers;
							 | 
						||
| 
								 | 
							
											}));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											const {socket} = stream.session;
							 | 
						||
| 
								 | 
							
											this.socket = socket;
							 | 
						||
| 
								 | 
							
											this.connection = socket;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											for (const job of this[kJobs]) {
							 | 
						||
| 
								 | 
							
												job();
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											this.emit('socket', this.socket);
							 | 
						||
| 
								 | 
							
										};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Makes a HTTP2 request
							 | 
						||
| 
								 | 
							
										if (this[kSession]) {
							 | 
						||
| 
								 | 
							
											try {
							 | 
						||
| 
								 | 
							
												onStream(this[kSession].request(this[kHeaders]));
							 | 
						||
| 
								 | 
							
											} catch (error) {
							 | 
						||
| 
								 | 
							
												this.emit('error', error);
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										} else {
							 | 
						||
| 
								 | 
							
											this.reusedSocket = true;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											try {
							 | 
						||
| 
								 | 
							
												onStream(await this.agent.request(this[kOrigin], this[kOptions], this[kHeaders]));
							 | 
						||
| 
								 | 
							
											} catch (error) {
							 | 
						||
| 
								 | 
							
												this.emit('error', error);
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									getHeader(name) {
							 | 
						||
| 
								 | 
							
										if (typeof name !== 'string') {
							 | 
						||
| 
								 | 
							
											throw new ERR_INVALID_ARG_TYPE('name', 'string', name);
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										return this[kHeaders][name.toLowerCase()];
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									get headersSent() {
							 | 
						||
| 
								 | 
							
										return this[kFlushedHeaders];
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									removeHeader(name) {
							 | 
						||
| 
								 | 
							
										if (typeof name !== 'string') {
							 | 
						||
| 
								 | 
							
											throw new ERR_INVALID_ARG_TYPE('name', 'string', name);
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if (this.headersSent) {
							 | 
						||
| 
								 | 
							
											throw new ERR_HTTP_HEADERS_SENT('remove');
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										delete this[kHeaders][name.toLowerCase()];
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									setHeader(name, value) {
							 | 
						||
| 
								 | 
							
										if (this.headersSent) {
							 | 
						||
| 
								 | 
							
											throw new ERR_HTTP_HEADERS_SENT('set');
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if (typeof name !== 'string' || (!isValidHttpToken.test(name) && !isRequestPseudoHeader(name))) {
							 | 
						||
| 
								 | 
							
											throw new ERR_INVALID_HTTP_TOKEN('Header name', name);
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if (typeof value === 'undefined') {
							 | 
						||
| 
								 | 
							
											throw new ERR_HTTP_INVALID_HEADER_VALUE(value, name);
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if (isInvalidHeaderValue.test(value)) {
							 | 
						||
| 
								 | 
							
											throw new ERR_INVALID_CHAR('header content', name);
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										this[kHeaders][name.toLowerCase()] = value;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									setNoDelay() {
							 | 
						||
| 
								 | 
							
										// HTTP2 sockets cannot be malformed, do nothing.
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									setSocketKeepAlive() {
							 | 
						||
| 
								 | 
							
										// HTTP2 sockets cannot be malformed, do nothing.
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									setTimeout(ms, callback) {
							 | 
						||
| 
								 | 
							
										const applyTimeout = () => this._request.setTimeout(ms, callback);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if (this._request) {
							 | 
						||
| 
								 | 
							
											applyTimeout();
							 | 
						||
| 
								 | 
							
										} else {
							 | 
						||
| 
								 | 
							
											this[kJobs].push(applyTimeout);
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										return this;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									get maxHeadersCount() {
							 | 
						||
| 
								 | 
							
										if (!this.destroyed && this._request) {
							 | 
						||
| 
								 | 
							
											return this._request.session.localSettings.maxHeaderListSize;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										return undefined;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									set maxHeadersCount(_value) {
							 | 
						||
| 
								 | 
							
										// Updating HTTP2 settings would affect all requests, do nothing.
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								module.exports = ClientRequest;
							 |