diff options
Diffstat (limited to 'node_modules/http2-wrapper/source')
-rw-r--r-- | node_modules/http2-wrapper/source/agent.js | 670 | ||||
-rw-r--r-- | node_modules/http2-wrapper/source/auto.js | 149 | ||||
-rw-r--r-- | node_modules/http2-wrapper/source/client-request.js | 445 | ||||
-rw-r--r-- | node_modules/http2-wrapper/source/incoming-message.js | 58 | ||||
-rw-r--r-- | node_modules/http2-wrapper/source/index.js | 28 | ||||
-rw-r--r-- | node_modules/http2-wrapper/source/utils/calculate-server-name.js | 27 | ||||
-rw-r--r-- | node_modules/http2-wrapper/source/utils/errors.js | 45 | ||||
-rw-r--r-- | node_modules/http2-wrapper/source/utils/is-request-pseudo-header.js | 13 | ||||
-rw-r--r-- | node_modules/http2-wrapper/source/utils/proxy-events.js | 7 | ||||
-rw-r--r-- | node_modules/http2-wrapper/source/utils/url-to-options.js | 25 |
10 files changed, 0 insertions, 1467 deletions
diff --git a/node_modules/http2-wrapper/source/agent.js b/node_modules/http2-wrapper/source/agent.js deleted file mode 100644 index b2b1cff..0000000 --- a/node_modules/http2-wrapper/source/agent.js +++ /dev/null @@ -1,670 +0,0 @@ -'use strict'; -const EventEmitter = require('events'); -const tls = require('tls'); -const http2 = require('http2'); -const QuickLRU = require('quick-lru'); - -const kCurrentStreamsCount = Symbol('currentStreamsCount'); -const kRequest = Symbol('request'); -const kOriginSet = Symbol('cachedOriginSet'); -const kGracefullyClosing = Symbol('gracefullyClosing'); - -const nameKeys = [ - // `http2.connect()` options - 'maxDeflateDynamicTableSize', - 'maxSessionMemory', - 'maxHeaderListPairs', - 'maxOutstandingPings', - 'maxReservedRemoteStreams', - 'maxSendHeaderBlockLength', - 'paddingStrategy', - - // `tls.connect()` options - 'localAddress', - 'path', - 'rejectUnauthorized', - 'minDHSize', - - // `tls.createSecureContext()` options - 'ca', - 'cert', - 'clientCertEngine', - 'ciphers', - 'key', - 'pfx', - 'servername', - 'minVersion', - 'maxVersion', - 'secureProtocol', - 'crl', - 'honorCipherOrder', - 'ecdhCurve', - 'dhparam', - 'secureOptions', - 'sessionIdContext' -]; - -const getSortedIndex = (array, value, compare) => { - let low = 0; - let high = array.length; - - while (low < high) { - const mid = (low + high) >>> 1; - - /* istanbul ignore next */ - if (compare(array[mid], value)) { - // This never gets called because we use descending sort. Better to have this anyway. - low = mid + 1; - } else { - high = mid; - } - } - - return low; -}; - -const compareSessions = (a, b) => { - return a.remoteSettings.maxConcurrentStreams > b.remoteSettings.maxConcurrentStreams; -}; - -// See https://tools.ietf.org/html/rfc8336 -const closeCoveredSessions = (where, session) => { - // Clients SHOULD NOT emit new requests on any connection whose Origin - // Set is a proper subset of another connection's Origin Set, and they - // SHOULD close it once all outstanding requests are satisfied. - for (const coveredSession of where) { - if ( - // The set is a proper subset when its length is less than the other set. - coveredSession[kOriginSet].length < session[kOriginSet].length && - - // And the other set includes all elements of the subset. - coveredSession[kOriginSet].every(origin => session[kOriginSet].includes(origin)) && - - // Makes sure that the session can handle all requests from the covered session. - coveredSession[kCurrentStreamsCount] + session[kCurrentStreamsCount] <= session.remoteSettings.maxConcurrentStreams - ) { - // This allows pending requests to finish and prevents making new requests. - gracefullyClose(coveredSession); - } - } -}; - -// This is basically inverted `closeCoveredSessions(...)`. -const closeSessionIfCovered = (where, coveredSession) => { - for (const session of where) { - if ( - coveredSession[kOriginSet].length < session[kOriginSet].length && - coveredSession[kOriginSet].every(origin => session[kOriginSet].includes(origin)) && - coveredSession[kCurrentStreamsCount] + session[kCurrentStreamsCount] <= session.remoteSettings.maxConcurrentStreams - ) { - gracefullyClose(coveredSession); - } - } -}; - -const getSessions = ({agent, isFree}) => { - const result = {}; - - // eslint-disable-next-line guard-for-in - for (const normalizedOptions in agent.sessions) { - const sessions = agent.sessions[normalizedOptions]; - - const filtered = sessions.filter(session => { - const result = session[Agent.kCurrentStreamsCount] < session.remoteSettings.maxConcurrentStreams; - - return isFree ? result : !result; - }); - - if (filtered.length !== 0) { - result[normalizedOptions] = filtered; - } - } - - return result; -}; - -const gracefullyClose = session => { - session[kGracefullyClosing] = true; - - if (session[kCurrentStreamsCount] === 0) { - session.close(); - } -}; - -class Agent extends EventEmitter { - constructor({timeout = 60000, maxSessions = Infinity, maxFreeSessions = 10, maxCachedTlsSessions = 100} = {}) { - super(); - - // A session is considered busy when its current streams count - // is equal to or greater than the `maxConcurrentStreams` value. - - // A session is considered free when its current streams count - // is less than the `maxConcurrentStreams` value. - - // SESSIONS[NORMALIZED_OPTIONS] = []; - this.sessions = {}; - - // The queue for creating new sessions. It looks like this: - // QUEUE[NORMALIZED_OPTIONS][NORMALIZED_ORIGIN] = ENTRY_FUNCTION - // - // The entry function has `listeners`, `completed` and `destroyed` properties. - // `listeners` is an array of objects containing `resolve` and `reject` functions. - // `completed` is a boolean. It's set to true after ENTRY_FUNCTION is executed. - // `destroyed` is a boolean. If it's set to true, the session will be destroyed if hasn't connected yet. - this.queue = {}; - - // Each session will use this timeout value. - this.timeout = timeout; - - // Max sessions in total - this.maxSessions = maxSessions; - - // Max free sessions in total - // TODO: decreasing `maxFreeSessions` should close some sessions - this.maxFreeSessions = maxFreeSessions; - - this._freeSessionsCount = 0; - this._sessionsCount = 0; - - // We don't support push streams by default. - this.settings = { - enablePush: false - }; - - // Reusing TLS sessions increases performance. - this.tlsSessionCache = new QuickLRU({maxSize: maxCachedTlsSessions}); - } - - static normalizeOrigin(url, servername) { - if (typeof url === 'string') { - url = new URL(url); - } - - if (servername && url.hostname !== servername) { - url.hostname = servername; - } - - return url.origin; - } - - normalizeOptions(options) { - let normalized = ''; - - if (options) { - for (const key of nameKeys) { - if (options[key]) { - normalized += `:${options[key]}`; - } - } - } - - return normalized; - } - - _tryToCreateNewSession(normalizedOptions, normalizedOrigin) { - if (!(normalizedOptions in this.queue) || !(normalizedOrigin in this.queue[normalizedOptions])) { - return; - } - - const item = this.queue[normalizedOptions][normalizedOrigin]; - - // The entry function can be run only once. - // BUG: The session may be never created when: - // - the first condition is false AND - // - this function is never called with the same arguments in the future. - if (this._sessionsCount < this.maxSessions && !item.completed) { - item.completed = true; - - item(); - } - } - - getSession(origin, options, listeners) { - return new Promise((resolve, reject) => { - if (Array.isArray(listeners)) { - listeners = [...listeners]; - - // Resolve the current promise ASAP, we're just moving the listeners. - // They will be executed at a different time. - resolve(); - } else { - listeners = [{resolve, reject}]; - } - - const normalizedOptions = this.normalizeOptions(options); - const normalizedOrigin = Agent.normalizeOrigin(origin, options && options.servername); - - if (normalizedOrigin === undefined) { - for (const {reject} of listeners) { - reject(new TypeError('The `origin` argument needs to be a string or an URL object')); - } - - return; - } - - if (normalizedOptions in this.sessions) { - const sessions = this.sessions[normalizedOptions]; - - let maxConcurrentStreams = -1; - let currentStreamsCount = -1; - let optimalSession; - - // We could just do this.sessions[normalizedOptions].find(...) but that isn't optimal. - // Additionally, we are looking for session which has biggest current pending streams count. - for (const session of sessions) { - const sessionMaxConcurrentStreams = session.remoteSettings.maxConcurrentStreams; - - if (sessionMaxConcurrentStreams < maxConcurrentStreams) { - break; - } - - if (session[kOriginSet].includes(normalizedOrigin)) { - const sessionCurrentStreamsCount = session[kCurrentStreamsCount]; - - if ( - sessionCurrentStreamsCount >= sessionMaxConcurrentStreams || - session[kGracefullyClosing] || - // Unfortunately the `close` event isn't called immediately, - // so `session.destroyed` is `true`, but `session.closed` is `false`. - session.destroyed - ) { - continue; - } - - // We only need set this once. - if (!optimalSession) { - maxConcurrentStreams = sessionMaxConcurrentStreams; - } - - // We're looking for the session which has biggest current pending stream count, - // in order to minimalize the amount of active sessions. - if (sessionCurrentStreamsCount > currentStreamsCount) { - optimalSession = session; - currentStreamsCount = sessionCurrentStreamsCount; - } - } - } - - if (optimalSession) { - /* istanbul ignore next: safety check */ - if (listeners.length !== 1) { - for (const {reject} of listeners) { - const error = new Error( - `Expected the length of listeners to be 1, got ${listeners.length}.\n` + - 'Please report this to https://github.com/szmarczak/http2-wrapper/' - ); - - reject(error); - } - - return; - } - - listeners[0].resolve(optimalSession); - return; - } - } - - if (normalizedOptions in this.queue) { - if (normalizedOrigin in this.queue[normalizedOptions]) { - // There's already an item in the queue, just attach ourselves to it. - this.queue[normalizedOptions][normalizedOrigin].listeners.push(...listeners); - - // This shouldn't be executed here. - // See the comment inside _tryToCreateNewSession. - this._tryToCreateNewSession(normalizedOptions, normalizedOrigin); - return; - } - } else { - this.queue[normalizedOptions] = {}; - } - - // The entry must be removed from the queue IMMEDIATELY when: - // 1. the session connects successfully, - // 2. an error occurs. - const removeFromQueue = () => { - // Our entry can be replaced. We cannot remove the new one. - if (normalizedOptions in this.queue && this.queue[normalizedOptions][normalizedOrigin] === entry) { - delete this.queue[normalizedOptions][normalizedOrigin]; - - if (Object.keys(this.queue[normalizedOptions]).length === 0) { - delete this.queue[normalizedOptions]; - } - } - }; - - // The main logic is here - const entry = () => { - const name = `${normalizedOrigin}:${normalizedOptions}`; - let receivedSettings = false; - - try { - const session = http2.connect(origin, { - createConnection: this.createConnection, - settings: this.settings, - session: this.tlsSessionCache.get(name), - ...options - }); - session[kCurrentStreamsCount] = 0; - session[kGracefullyClosing] = false; - - const isFree = () => session[kCurrentStreamsCount] < session.remoteSettings.maxConcurrentStreams; - let wasFree = true; - - session.socket.once('session', tlsSession => { - this.tlsSessionCache.set(name, tlsSession); - }); - - session.once('error', error => { - // Listeners are empty when the session successfully connected. - for (const {reject} of listeners) { - reject(error); - } - - // The connection got broken, purge the cache. - this.tlsSessionCache.delete(name); - }); - - session.setTimeout(this.timeout, () => { - // Terminates all streams owned by this session. - // TODO: Maybe the streams should have a "Session timed out" error? - session.destroy(); - }); - - session.once('close', () => { - if (receivedSettings) { - // 1. If it wasn't free then no need to decrease because - // it has been decreased already in session.request(). - // 2. `stream.once('close')` won't increment the count - // because the session is already closed. - if (wasFree) { - this._freeSessionsCount--; - } - - this._sessionsCount--; - - // This cannot be moved to the stream logic, - // because there may be a session that hadn't made a single request. - const where = this.sessions[normalizedOptions]; - where.splice(where.indexOf(session), 1); - - if (where.length === 0) { - delete this.sessions[normalizedOptions]; - } - } else { - // Broken connection - const error = new Error('Session closed without receiving a SETTINGS frame'); - error.code = 'HTTP2WRAPPER_NOSETTINGS'; - - for (const {reject} of listeners) { - reject(error); - } - - removeFromQueue(); - } - - // There may be another session awaiting. - this._tryToCreateNewSession(normalizedOptions, normalizedOrigin); - }); - - // Iterates over the queue and processes listeners. - const processListeners = () => { - if (!(normalizedOptions in this.queue) || !isFree()) { - return; - } - - for (const origin of session[kOriginSet]) { - if (origin in this.queue[normalizedOptions]) { - const {listeners} = this.queue[normalizedOptions][origin]; - - // Prevents session overloading. - while (listeners.length !== 0 && isFree()) { - // We assume `resolve(...)` calls `request(...)` *directly*, - // otherwise the session will get overloaded. - listeners.shift().resolve(session); - } - - const where = this.queue[normalizedOptions]; - if (where[origin].listeners.length === 0) { - delete where[origin]; - - if (Object.keys(where).length === 0) { - delete this.queue[normalizedOptions]; - break; - } - } - - // We're no longer free, no point in continuing. - if (!isFree()) { - break; - } - } - } - }; - - // The Origin Set cannot shrink. No need to check if it suddenly became covered by another one. - session.on('origin', () => { - session[kOriginSet] = session.originSet; - - if (!isFree()) { - // The session is full. - return; - } - - processListeners(); - - // Close covered sessions (if possible). - closeCoveredSessions(this.sessions[normalizedOptions], session); - }); - - session.once('remoteSettings', () => { - // Fix Node.js bug preventing the process from exiting - session.ref(); - session.unref(); - - this._sessionsCount++; - - // The Agent could have been destroyed already. - if (entry.destroyed) { - const error = new Error('Agent has been destroyed'); - - for (const listener of listeners) { - listener.reject(error); - } - - session.destroy(); - return; - } - - session[kOriginSet] = session.originSet; - - { - const where = this.sessions; - - if (normalizedOptions in where) { - const sessions = where[normalizedOptions]; - sessions.splice(getSortedIndex(sessions, session, compareSessions), 0, session); - } else { - where[normalizedOptions] = [session]; - } - } - - this._freeSessionsCount += 1; - receivedSettings = true; - - this.emit('session', session); - - processListeners(); - removeFromQueue(); - - // TODO: Close last recently used (or least used?) session - if (session[kCurrentStreamsCount] === 0 && this._freeSessionsCount > this.maxFreeSessions) { - session.close(); - } - - // Check if we haven't managed to execute all listeners. - if (listeners.length !== 0) { - // Request for a new session with predefined listeners. - this.getSession(normalizedOrigin, options, listeners); - listeners.length = 0; - } - - // `session.remoteSettings.maxConcurrentStreams` might get increased - session.on('remoteSettings', () => { - processListeners(); - - // In case the Origin Set changes - closeCoveredSessions(this.sessions[normalizedOptions], session); - }); - }); - - // Shim `session.request()` in order to catch all streams - session[kRequest] = session.request; - session.request = (headers, streamOptions) => { - if (session[kGracefullyClosing]) { - throw new Error('The session is gracefully closing. No new streams are allowed.'); - } - - const stream = session[kRequest](headers, streamOptions); - - // The process won't exit until the session is closed or all requests are gone. - session.ref(); - - ++session[kCurrentStreamsCount]; - - if (session[kCurrentStreamsCount] === session.remoteSettings.maxConcurrentStreams) { - this._freeSessionsCount--; - } - - stream.once('close', () => { - wasFree = isFree(); - - --session[kCurrentStreamsCount]; - - if (!session.destroyed && !session.closed) { - closeSessionIfCovered(this.sessions[normalizedOptions], session); - - if (isFree() && !session.closed) { - if (!wasFree) { - this._freeSessionsCount++; - - wasFree = true; - } - - const isEmpty = session[kCurrentStreamsCount] === 0; - - if (isEmpty) { - session.unref(); - } - - if ( - isEmpty && - ( - this._freeSessionsCount > this.maxFreeSessions || - session[kGracefullyClosing] - ) - ) { - session.close(); - } else { - closeCoveredSessions(this.sessions[normalizedOptions], session); - processListeners(); - } - } - } - }); - - return stream; - }; - } catch (error) { - for (const listener of listeners) { - listener.reject(error); - } - - removeFromQueue(); - } - }; - - entry.listeners = listeners; - entry.completed = false; - entry.destroyed = false; - - this.queue[normalizedOptions][normalizedOrigin] = entry; - this._tryToCreateNewSession(normalizedOptions, normalizedOrigin); - }); - } - - request(origin, options, headers, streamOptions) { - return new Promise((resolve, reject) => { - this.getSession(origin, options, [{ - reject, - resolve: session => { - try { - resolve(session.request(headers, streamOptions)); - } catch (error) { - reject(error); - } - } - }]); - }); - } - - createConnection(origin, options) { - return Agent.connect(origin, options); - } - - static connect(origin, options) { - options.ALPNProtocols = ['h2']; - - const port = origin.port || 443; - const host = origin.hostname || origin.host; - - if (typeof options.servername === 'undefined') { - options.servername = host; - } - - return tls.connect(port, host, options); - } - - closeFreeSessions() { - for (const sessions of Object.values(this.sessions)) { - for (const session of sessions) { - if (session[kCurrentStreamsCount] === 0) { - session.close(); - } - } - } - } - - destroy(reason) { - for (const sessions of Object.values(this.sessions)) { - for (const session of sessions) { - session.destroy(reason); - } - } - - for (const entriesOfAuthority of Object.values(this.queue)) { - for (const entry of Object.values(entriesOfAuthority)) { - entry.destroyed = true; - } - } - - // New requests should NOT attach to destroyed sessions - this.queue = {}; - } - - get freeSessions() { - return getSessions({agent: this, isFree: true}); - } - - get busySessions() { - return getSessions({agent: this, isFree: false}); - } -} - -Agent.kCurrentStreamsCount = kCurrentStreamsCount; -Agent.kGracefullyClosing = kGracefullyClosing; - -module.exports = { - Agent, - globalAgent: new Agent() -}; diff --git a/node_modules/http2-wrapper/source/auto.js b/node_modules/http2-wrapper/source/auto.js deleted file mode 100644 index af4e9ba..0000000 --- a/node_modules/http2-wrapper/source/auto.js +++ /dev/null @@ -1,149 +0,0 @@ -'use strict'; -const http = require('http'); -const https = require('https'); -const resolveALPN = require('resolve-alpn'); -const QuickLRU = require('quick-lru'); -const Http2ClientRequest = require('./client-request'); -const calculateServerName = require('./utils/calculate-server-name'); -const urlToOptions = require('./utils/url-to-options'); - -const cache = new QuickLRU({maxSize: 100}); -const queue = new Map(); - -const installSocket = (agent, socket, options) => { - socket._httpMessage = {shouldKeepAlive: true}; - - const onFree = () => { - agent.emit('free', socket, options); - }; - - socket.on('free', onFree); - - const onClose = () => { - agent.removeSocket(socket, options); - }; - - socket.on('close', onClose); - - const onRemove = () => { - agent.removeSocket(socket, options); - socket.off('close', onClose); - socket.off('free', onFree); - socket.off('agentRemove', onRemove); - }; - - socket.on('agentRemove', onRemove); - - agent.emit('free', socket, options); -}; - -const resolveProtocol = async options => { - const name = `${options.host}:${options.port}:${options.ALPNProtocols.sort()}`; - - if (!cache.has(name)) { - if (queue.has(name)) { - const result = await queue.get(name); - return result.alpnProtocol; - } - - const {path, agent} = options; - options.path = options.socketPath; - - const resultPromise = resolveALPN(options); - queue.set(name, resultPromise); - - try { - const {socket, alpnProtocol} = await resultPromise; - cache.set(name, alpnProtocol); - - options.path = path; - - if (alpnProtocol === 'h2') { - // https://github.com/nodejs/node/issues/33343 - socket.destroy(); - } else { - const {globalAgent} = https; - const defaultCreateConnection = https.Agent.prototype.createConnection; - - if (agent) { - if (agent.createConnection === defaultCreateConnection) { - installSocket(agent, socket, options); - } else { - socket.destroy(); - } - } else if (globalAgent.createConnection === defaultCreateConnection) { - installSocket(globalAgent, socket, options); - } else { - socket.destroy(); - } - } - - queue.delete(name); - - return alpnProtocol; - } catch (error) { - queue.delete(name); - - throw error; - } - } - - return cache.get(name); -}; - -module.exports = async (input, options, callback) => { - if (typeof input === 'string' || input instanceof URL) { - input = urlToOptions(new URL(input)); - } - - if (typeof options === 'function') { - callback = options; - options = undefined; - } - - options = { - ALPNProtocols: ['h2', 'http/1.1'], - ...input, - ...options, - resolveSocket: true - }; - - if (!Array.isArray(options.ALPNProtocols) || options.ALPNProtocols.length === 0) { - throw new Error('The `ALPNProtocols` option must be an Array with at least one entry'); - } - - options.protocol = options.protocol || 'https:'; - const isHttps = options.protocol === 'https:'; - - options.host = options.hostname || options.host || 'localhost'; - options.session = options.tlsSession; - options.servername = options.servername || calculateServerName(options); - options.port = options.port || (isHttps ? 443 : 80); - options._defaultAgent = isHttps ? https.globalAgent : http.globalAgent; - - const agents = options.agent; - - if (agents) { - if (agents.addRequest) { - throw new Error('The `options.agent` object can contain only `http`, `https` or `http2` properties'); - } - - options.agent = agents[isHttps ? 'https' : 'http']; - } - - if (isHttps) { - const protocol = await resolveProtocol(options); - - if (protocol === 'h2') { - if (agents) { - options.agent = agents.http2; - } - - return new Http2ClientRequest(options, callback); - } - } - - return http.request(options, callback); -}; - -module.exports.protocolCache = cache; 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; diff --git a/node_modules/http2-wrapper/source/incoming-message.js b/node_modules/http2-wrapper/source/incoming-message.js deleted file mode 100644 index 7d5c0d3..0000000 --- a/node_modules/http2-wrapper/source/incoming-message.js +++ /dev/null @@ -1,58 +0,0 @@ -'use strict'; -const {Readable} = require('stream'); - -class IncomingMessage extends Readable { - constructor(socket, highWaterMark) { - super({ - highWaterMark, - autoDestroy: false - }); - - this.statusCode = null; - this.statusMessage = ''; - this.httpVersion = '2.0'; - this.httpVersionMajor = 2; - this.httpVersionMinor = 0; - this.headers = {}; - this.trailers = {}; - this.req = null; - - this.aborted = false; - this.complete = false; - this.upgrade = null; - - this.rawHeaders = []; - this.rawTrailers = []; - - this.socket = socket; - this.connection = socket; - - this._dumped = false; - } - - _destroy(error) { - this.req._request.destroy(error); - } - - setTimeout(ms, callback) { - this.req.setTimeout(ms, callback); - return this; - } - - _dump() { - if (!this._dumped) { - this._dumped = true; - - this.removeAllListeners('data'); - this.resume(); - } - } - - _read() { - if (this.req) { - this.req._request.resume(); - } - } -} - -module.exports = IncomingMessage; diff --git a/node_modules/http2-wrapper/source/index.js b/node_modules/http2-wrapper/source/index.js deleted file mode 100644 index fb83349..0000000 --- a/node_modules/http2-wrapper/source/index.js +++ /dev/null @@ -1,28 +0,0 @@ -'use strict'; -const http2 = require('http2'); -const agent = require('./agent'); -const ClientRequest = require('./client-request'); -const IncomingMessage = require('./incoming-message'); -const auto = require('./auto'); - -const request = (url, options, callback) => { - return new ClientRequest(url, options, callback); -}; - -const get = (url, options, callback) => { - // eslint-disable-next-line unicorn/prevent-abbreviations - const req = new ClientRequest(url, options, callback); - req.end(); - - return req; -}; - -module.exports = { - ...http2, - ClientRequest, - IncomingMessage, - ...agent, - request, - get, - auto -}; diff --git a/node_modules/http2-wrapper/source/utils/calculate-server-name.js b/node_modules/http2-wrapper/source/utils/calculate-server-name.js deleted file mode 100644 index b05c099..0000000 --- a/node_modules/http2-wrapper/source/utils/calculate-server-name.js +++ /dev/null @@ -1,27 +0,0 @@ -'use strict'; -const net = require('net'); -/* istanbul ignore file: https://github.com/nodejs/node/blob/v13.0.1/lib/_http_agent.js */ - -module.exports = options => { - let servername = options.host; - const hostHeader = options.headers && options.headers.host; - - if (hostHeader) { - if (hostHeader.startsWith('[')) { - const index = hostHeader.indexOf(']'); - if (index === -1) { - servername = hostHeader; - } else { - servername = hostHeader.slice(1, -1); - } - } else { - servername = hostHeader.split(':', 1)[0]; - } - } - - if (net.isIP(servername)) { - return ''; - } - - return servername; -}; diff --git a/node_modules/http2-wrapper/source/utils/errors.js b/node_modules/http2-wrapper/source/utils/errors.js deleted file mode 100644 index 5018283..0000000 --- a/node_modules/http2-wrapper/source/utils/errors.js +++ /dev/null @@ -1,45 +0,0 @@ -'use strict'; -/* istanbul ignore file: https://github.com/nodejs/node/blob/master/lib/internal/errors.js */ - -const makeError = (Base, key, getMessage) => { - module.exports[key] = class NodeError extends Base { - constructor(...args) { - super(typeof getMessage === 'string' ? getMessage : getMessage(args)); - this.name = `${super.name} [${key}]`; - this.code = key; - } - }; -}; - -makeError(TypeError, 'ERR_INVALID_ARG_TYPE', args => { - const type = args[0].includes('.') ? 'property' : 'argument'; - - let valid = args[1]; - const isManyTypes = Array.isArray(valid); - - if (isManyTypes) { - valid = `${valid.slice(0, -1).join(', ')} or ${valid.slice(-1)}`; - } - - return `The "${args[0]}" ${type} must be ${isManyTypes ? 'one of' : 'of'} type ${valid}. Received ${typeof args[2]}`; -}); - -makeError(TypeError, 'ERR_INVALID_PROTOCOL', args => { - return `Protocol "${args[0]}" not supported. Expected "${args[1]}"`; -}); - -makeError(Error, 'ERR_HTTP_HEADERS_SENT', args => { - return `Cannot ${args[0]} headers after they are sent to the client`; -}); - -makeError(TypeError, 'ERR_INVALID_HTTP_TOKEN', args => { - return `${args[0]} must be a valid HTTP token [${args[1]}]`; -}); - -makeError(TypeError, 'ERR_HTTP_INVALID_HEADER_VALUE', args => { - return `Invalid value "${args[0]} for header "${args[1]}"`; -}); - -makeError(TypeError, 'ERR_INVALID_CHAR', args => { - return `Invalid character in ${args[0]} [${args[1]}]`; -}); diff --git a/node_modules/http2-wrapper/source/utils/is-request-pseudo-header.js b/node_modules/http2-wrapper/source/utils/is-request-pseudo-header.js deleted file mode 100644 index bed31cd..0000000 --- a/node_modules/http2-wrapper/source/utils/is-request-pseudo-header.js +++ /dev/null @@ -1,13 +0,0 @@ -'use strict'; - -module.exports = header => { - switch (header) { - case ':method': - case ':scheme': - case ':authority': - case ':path': - return true; - default: - return false; - } -}; diff --git a/node_modules/http2-wrapper/source/utils/proxy-events.js b/node_modules/http2-wrapper/source/utils/proxy-events.js deleted file mode 100644 index 35e2ae0..0000000 --- a/node_modules/http2-wrapper/source/utils/proxy-events.js +++ /dev/null @@ -1,7 +0,0 @@ -'use strict'; - -module.exports = (from, to, events) => { - for (const event of events) { - from.on(event, (...args) => to.emit(event, ...args)); - } -}; diff --git a/node_modules/http2-wrapper/source/utils/url-to-options.js b/node_modules/http2-wrapper/source/utils/url-to-options.js deleted file mode 100644 index 36d1580..0000000 --- a/node_modules/http2-wrapper/source/utils/url-to-options.js +++ /dev/null @@ -1,25 +0,0 @@ -'use strict'; -/* istanbul ignore file: https://github.com/nodejs/node/blob/a91293d4d9ab403046ab5eb022332e4e3d249bd3/lib/internal/url.js#L1257 */ - -module.exports = url => { - const options = { - protocol: url.protocol, - hostname: typeof url.hostname === 'string' && url.hostname.startsWith('[') ? url.hostname.slice(1, -1) : url.hostname, - host: url.host, - hash: url.hash, - search: url.search, - pathname: url.pathname, - href: url.href, - path: `${url.pathname || ''}${url.search || ''}` - }; - - if (typeof url.port === 'string' && url.port.length !== 0) { - options.port = Number(url.port); - } - - if (url.username || url.password) { - options.auth = `${url.username || ''}:${url.password || ''}`; - } - - return options; -}; |