UNPKG

gaxios

Version:

A simple common HTTP client specifically for Google APIs and services.

480 lines 20.6 kB
"use strict"; // Copyright 2018 Google LLC // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); }; var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) { if (kind === "m") throw new TypeError("Private method is not writable"); if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter"); if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it"); return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; var _Gaxios_instances, _a, _Gaxios_urlMayUseProxy, _Gaxios_applyRequestInterceptors, _Gaxios_applyResponseInterceptors, _Gaxios_prepareRequest, _Gaxios_proxyAgent, _Gaxios_getProxyAgent; Object.defineProperty(exports, "__esModule", { value: true }); exports.Gaxios = void 0; const extend_1 = __importDefault(require("extend")); const https_1 = require("https"); const node_fetch_1 = __importDefault(require("node-fetch")); const querystring_1 = __importDefault(require("querystring")); const is_stream_1 = __importDefault(require("is-stream")); const url_1 = require("url"); const common_1 = require("./common"); const retry_1 = require("./retry"); const stream_1 = require("stream"); const uuid_1 = require("uuid"); const interceptor_1 = require("./interceptor"); /* eslint-disable @typescript-eslint/no-explicit-any */ const fetch = hasFetch() ? window.fetch : node_fetch_1.default; function hasWindow() { return typeof window !== 'undefined' && !!window; } function hasFetch() { return hasWindow() && !!window.fetch; } function hasBuffer() { return typeof Buffer !== 'undefined'; } function hasHeader(options, header) { return !!getHeader(options, header); } function getHeader(options, header) { header = header.toLowerCase(); for (const key of Object.keys((options === null || options === void 0 ? void 0 : options.headers) || {})) { if (header === key.toLowerCase()) { return options.headers[key]; } } return undefined; } class Gaxios { /** * The Gaxios class is responsible for making HTTP requests. * @param defaults The default set of options to be used for this instance. */ constructor(defaults) { _Gaxios_instances.add(this); this.agentCache = new Map(); this.defaults = defaults || {}; this.interceptors = { request: new interceptor_1.GaxiosInterceptorManager(), response: new interceptor_1.GaxiosInterceptorManager(), }; } /** * Perform an HTTP request with the given options. * @param opts Set of HTTP options that will be used for this HTTP request. */ async request(opts = {}) { opts = await __classPrivateFieldGet(this, _Gaxios_instances, "m", _Gaxios_prepareRequest).call(this, opts); opts = await __classPrivateFieldGet(this, _Gaxios_instances, "m", _Gaxios_applyRequestInterceptors).call(this, opts); return __classPrivateFieldGet(this, _Gaxios_instances, "m", _Gaxios_applyResponseInterceptors).call(this, this._request(opts)); } async _defaultAdapter(opts) { const fetchImpl = opts.fetchImplementation || fetch; const res = (await fetchImpl(opts.url, opts)); const data = await this.getResponseData(opts, res); return this.translateResponse(opts, res, data); } /** * Internal, retryable version of the `request` method. * @param opts Set of HTTP options that will be used for this HTTP request. */ async _request(opts = {}) { var _b; try { let translatedResponse; if (opts.adapter) { translatedResponse = await opts.adapter(opts, this._defaultAdapter.bind(this)); } else { translatedResponse = await this._defaultAdapter(opts); } if (!opts.validateStatus(translatedResponse.status)) { if (opts.responseType === 'stream') { let response = ''; await new Promise(resolve => { (translatedResponse === null || translatedResponse === void 0 ? void 0 : translatedResponse.data).on('data', chunk => { response += chunk; }); (translatedResponse === null || translatedResponse === void 0 ? void 0 : translatedResponse.data).on('end', resolve); }); translatedResponse.data = response; } throw new common_1.GaxiosError(`Request failed with status code ${translatedResponse.status}`, opts, translatedResponse); } return translatedResponse; } catch (e) { const err = e instanceof common_1.GaxiosError ? e : new common_1.GaxiosError(e.message, opts, undefined, e); const { shouldRetry, config } = await (0, retry_1.getRetryConfig)(err); if (shouldRetry && config) { err.config.retryConfig.currentRetryAttempt = config.retryConfig.currentRetryAttempt; // The error's config could be redacted - therefore we only want to // copy the retry state over to the existing config opts.retryConfig = (_b = err.config) === null || _b === void 0 ? void 0 : _b.retryConfig; return this._request(opts); } throw err; } } async getResponseData(opts, res) { switch (opts.responseType) { case 'stream': return res.body; case 'json': { let data = await res.text(); try { data = JSON.parse(data); } catch (_b) { // continue } return data; } case 'arraybuffer': return res.arrayBuffer(); case 'blob': return res.blob(); case 'text': return res.text(); default: return this.getResponseDataFromContentType(res); } } /** * By default, throw for any non-2xx status code * @param status status code from the HTTP response */ validateStatus(status) { return status >= 200 && status < 300; } /** * Encode a set of key/value pars into a querystring format (?foo=bar&baz=boo) * @param params key value pars to encode */ paramsSerializer(params) { return querystring_1.default.stringify(params); } translateResponse(opts, res, data) { // headers need to be converted from a map to an obj const headers = {}; res.headers.forEach((value, key) => { headers[key] = value; }); return { config: opts, data: data, headers, status: res.status, statusText: res.statusText, // XMLHttpRequestLike request: { responseURL: res.url, }, }; } /** * Attempts to parse a response by looking at the Content-Type header. * @param {FetchResponse} response the HTTP response. * @returns {Promise<any>} a promise that resolves to the response data. */ async getResponseDataFromContentType(response) { let contentType = response.headers.get('Content-Type'); if (contentType === null) { // Maintain existing functionality by calling text() return response.text(); } contentType = contentType.toLowerCase(); if (contentType.includes('application/json')) { let data = await response.text(); try { data = JSON.parse(data); } catch (_b) { // continue } return data; } else if (contentType.match(/^text\//)) { return response.text(); } else { // If the content type is something not easily handled, just return the raw data (blob) return response.blob(); } } /** * Creates an async generator that yields the pieces of a multipart/related request body. * This implementation follows the spec: https://www.ietf.org/rfc/rfc2387.txt. However, recursive * multipart/related requests are not currently supported. * * @param {GaxioMultipartOptions[]} multipartOptions the pieces to turn into a multipart/related body. * @param {string} boundary the boundary string to be placed between each part. */ async *getMultipartRequest(multipartOptions, boundary) { const finale = `--${boundary}--`; for (const currentPart of multipartOptions) { const partContentType = currentPart.headers['Content-Type'] || 'application/octet-stream'; const preamble = `--${boundary}\r\nContent-Type: ${partContentType}\r\n\r\n`; yield preamble; if (typeof currentPart.content === 'string') { yield currentPart.content; } else { yield* currentPart.content; } yield '\r\n'; } yield finale; } } exports.Gaxios = Gaxios; _a = Gaxios, _Gaxios_instances = new WeakSet(), _Gaxios_urlMayUseProxy = function _Gaxios_urlMayUseProxy(url, noProxy = []) { var _b, _c; const candidate = new url_1.URL(url); const noProxyList = [...noProxy]; const noProxyEnvList = ((_c = ((_b = process.env.NO_PROXY) !== null && _b !== void 0 ? _b : process.env.no_proxy)) === null || _c === void 0 ? void 0 : _c.split(',')) || []; for (const rule of noProxyEnvList) { noProxyList.push(rule.trim()); } for (const rule of noProxyList) { // Match regex if (rule instanceof RegExp) { if (rule.test(candidate.toString())) { return false; } } // Match URL else if (rule instanceof url_1.URL) { if (rule.origin === candidate.origin) { return false; } } // Match string regex else if (rule.startsWith('*.') || rule.startsWith('.')) { const cleanedRule = rule.replace(/^\*\./, '.'); if (candidate.hostname.endsWith(cleanedRule)) { return false; } } // Basic string match else if (rule === candidate.origin || rule === candidate.hostname || rule === candidate.href) { return false; } } return true; }, _Gaxios_applyRequestInterceptors = /** * Applies the request interceptors. The request interceptors are applied after the * call to prepareRequest is completed. * * @param {GaxiosOptions} options The current set of options. * * @returns {Promise<GaxiosOptions>} Promise that resolves to the set of options or response after interceptors are applied. */ async function _Gaxios_applyRequestInterceptors(options) { let promiseChain = Promise.resolve(options); for (const interceptor of this.interceptors.request.values()) { if (interceptor) { promiseChain = promiseChain.then(interceptor.resolved, interceptor.rejected); } } return promiseChain; }, _Gaxios_applyResponseInterceptors = /** * Applies the response interceptors. The response interceptors are applied after the * call to request is made. * * @param {GaxiosOptions} options The current set of options. * * @returns {Promise<GaxiosOptions>} Promise that resolves to the set of options or response after interceptors are applied. */ async function _Gaxios_applyResponseInterceptors(response) { let promiseChain = Promise.resolve(response); for (const interceptor of this.interceptors.response.values()) { if (interceptor) { promiseChain = promiseChain.then(interceptor.resolved, interceptor.rejected); } } return promiseChain; }, _Gaxios_prepareRequest = /** * Validates the options, merges them with defaults, and prepare request. * * @param options The original options passed from the client. * @returns Prepared options, ready to make a request */ async function _Gaxios_prepareRequest(options) { var _b, _c, _d, _e; const opts = (0, extend_1.default)(true, {}, this.defaults, options); if (!opts.url) { throw new Error('URL is required.'); } // baseUrl has been deprecated, remove in 2.0 const baseUrl = opts.baseUrl || opts.baseURL; if (baseUrl) { opts.url = baseUrl.toString() + opts.url; } opts.paramsSerializer = opts.paramsSerializer || this.paramsSerializer; if (opts.params && Object.keys(opts.params).length > 0) { let additionalQueryParams = opts.paramsSerializer(opts.params); if (additionalQueryParams.startsWith('?')) { additionalQueryParams = additionalQueryParams.slice(1); } const prefix = opts.url.toString().includes('?') ? '&' : '?'; opts.url = opts.url + prefix + additionalQueryParams; } if (typeof options.maxContentLength === 'number') { opts.size = options.maxContentLength; } if (typeof options.maxRedirects === 'number') { opts.follow = options.maxRedirects; } opts.headers = opts.headers || {}; if (opts.multipart === undefined && opts.data) { const isFormData = typeof FormData === 'undefined' ? false : (opts === null || opts === void 0 ? void 0 : opts.data) instanceof FormData; if (is_stream_1.default.readable(opts.data)) { opts.body = opts.data; } else if (hasBuffer() && Buffer.isBuffer(opts.data)) { // Do not attempt to JSON.stringify() a Buffer: opts.body = opts.data; if (!hasHeader(opts, 'Content-Type')) { opts.headers['Content-Type'] = 'application/json'; } } else if (typeof opts.data === 'object') { // If www-form-urlencoded content type has been set, but data is // provided as an object, serialize the content using querystring: if (!isFormData) { if (getHeader(opts, 'content-type') === 'application/x-www-form-urlencoded') { opts.body = opts.paramsSerializer(opts.data); } else { // } else if (!(opts.data instanceof FormData)) { if (!hasHeader(opts, 'Content-Type')) { opts.headers['Content-Type'] = 'application/json'; } opts.body = JSON.stringify(opts.data); } } } else { opts.body = opts.data; } } else if (opts.multipart && opts.multipart.length > 0) { // note: once the minimum version reaches Node 16, // this can be replaced with randomUUID() function from crypto // and the dependency on UUID removed const boundary = (0, uuid_1.v4)(); opts.headers['Content-Type'] = `multipart/related; boundary=${boundary}`; const bodyStream = new stream_1.PassThrough(); opts.body = bodyStream; (0, stream_1.pipeline)(this.getMultipartRequest(opts.multipart, boundary), bodyStream, () => { }); } opts.validateStatus = opts.validateStatus || this.validateStatus; opts.responseType = opts.responseType || 'unknown'; if (!opts.headers['Accept'] && opts.responseType === 'json') { opts.headers['Accept'] = 'application/json'; } opts.method = opts.method || 'GET'; const proxy = opts.proxy || ((_b = process === null || process === void 0 ? void 0 : process.env) === null || _b === void 0 ? void 0 : _b.HTTPS_PROXY) || ((_c = process === null || process === void 0 ? void 0 : process.env) === null || _c === void 0 ? void 0 : _c.https_proxy) || ((_d = process === null || process === void 0 ? void 0 : process.env) === null || _d === void 0 ? void 0 : _d.HTTP_PROXY) || ((_e = process === null || process === void 0 ? void 0 : process.env) === null || _e === void 0 ? void 0 : _e.http_proxy); const urlMayUseProxy = __classPrivateFieldGet(this, _Gaxios_instances, "m", _Gaxios_urlMayUseProxy).call(this, opts.url, opts.noProxy); if (opts.agent) { // don't do any of the following options - use the user-provided agent. } else if (proxy && urlMayUseProxy) { const HttpsProxyAgent = await __classPrivateFieldGet(_a, _a, "m", _Gaxios_getProxyAgent).call(_a); if (this.agentCache.has(proxy)) { opts.agent = this.agentCache.get(proxy); } else { opts.agent = new HttpsProxyAgent(proxy, { cert: opts.cert, key: opts.key, }); this.agentCache.set(proxy, opts.agent); } } else if (opts.cert && opts.key) { // Configure client for mTLS if (this.agentCache.has(opts.key)) { opts.agent = this.agentCache.get(opts.key); } else { opts.agent = new https_1.Agent({ cert: opts.cert, key: opts.key, }); this.agentCache.set(opts.key, opts.agent); } } if (typeof opts.errorRedactor !== 'function' && opts.errorRedactor !== false) { opts.errorRedactor = common_1.defaultErrorRedactor; } return opts; }, _Gaxios_getProxyAgent = async function _Gaxios_getProxyAgent() { __classPrivateFieldSet(this, _a, __classPrivateFieldGet(this, _a, "f", _Gaxios_proxyAgent) || (await Promise.resolve().then(() => __importStar(require('https-proxy-agent')))).HttpsProxyAgent, "f", _Gaxios_proxyAgent); return __classPrivateFieldGet(this, _a, "f", _Gaxios_proxyAgent); }; /** * A cache for the lazily-loaded proxy agent. * * Should use {@link Gaxios[#getProxyAgent]} to retrieve. */ // using `import` to dynamically import the types here _Gaxios_proxyAgent = { value: void 0 }; //# sourceMappingURL=gaxios.js.map