diff options
Diffstat (limited to 'node_modules/http2-wrapper/source/client-request.js')
-rw-r--r-- | node_modules/http2-wrapper/source/client-request.js | 445 |
1 files changed, 0 insertions, 445 deletions
diff --git a/node_modules/http2-wrapper/source/client-request.js b/node_modules/http2-wrapper/source/client-request.js deleted file mode 100644 index b712967..0000000 --- a/node_modules/http2-wrapper/source/client-request.js +++ /dev/null @@ -1,445 +0,0 @@ -'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; |