summaryrefslogtreecommitdiff
path: root/node_modules/http2-wrapper/source
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/http2-wrapper/source')
-rw-r--r--node_modules/http2-wrapper/source/agent.js670
-rw-r--r--node_modules/http2-wrapper/source/auto.js149
-rw-r--r--node_modules/http2-wrapper/source/client-request.js445
-rw-r--r--node_modules/http2-wrapper/source/incoming-message.js58
-rw-r--r--node_modules/http2-wrapper/source/index.js28
-rw-r--r--node_modules/http2-wrapper/source/utils/calculate-server-name.js27
-rw-r--r--node_modules/http2-wrapper/source/utils/errors.js45
-rw-r--r--node_modules/http2-wrapper/source/utils/is-request-pseudo-header.js13
-rw-r--r--node_modules/http2-wrapper/source/utils/proxy-events.js7
-rw-r--r--node_modules/http2-wrapper/source/utils/url-to-options.js25
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;
-};