import extend from 'extend' ; import { Agent } from 'http' ; import { Agent as HTTPSAgent } from 'https' ; import nodeFetch from 'node-fetch' ; import qs from 'querystring' ; import isStream from 'is-stream' ; import { URL } from 'url' ; import { FetchResponse , GaxiosError , GaxiosOptions , GaxiosPromise , GaxiosResponse , Headers , } from './common' ; import { getRetryConfig } from './retry' ; const fetch = hasFetch ( ) ? window . fetch : nodeFetch ; 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 ?. headers || { } ) ) { if ( header === key . toLowerCase ( ) ) { return options . headers ! [ key ] ; } } return undefined ; } let HttpsProxyAgent ; function loadProxy ( ) { const proxy = process . env . HTTPS_PROXY || process . env . https_proxy || process . env . HTTP_PROXY || process . env . http_proxy ; if ( proxy ) { HttpsProxyAgent = require ( 'https-proxy-agent' ) ; } return proxy ; } loadProxy ( ) ; function skipProxy ( url ) { const noProxyEnv = process . env . NO_PROXY ?? process . env . no_proxy ; if ( ! noProxyEnv ) { return false ; } const noProxyUrls = noProxyEnv . split ( ',' ) ; const parsedURL = new URL ( url ) ; return ! ! noProxyUrls . find ( url => { if ( url . startsWith ( '*.' ) || url . startsWith ( '.' ) ) { url = url . replace ( /^\*\./ , '.' ) ; return parsedURL . hostname . endsWith ( url ) ; } else { return url === parsedURL . origin || url === parsedURL . hostname ; } } ) ; } function getProxy ( url ) { if ( skipProxy ( url ) ) { return undefined ; } else { return loadProxy ( ) ; } } export class Gaxios { protected agentCache = new Map < string , Agent | ( ( parsedUrl : URL ) => Agent ) > ( ) ; /**
     * Default HTTP options that will be used for every HTTP request.
     */ defaults : GaxiosOptions ; param defaults constructor ( defaults ? ) { this . defaults = defaults || { } ; } param opts async request < T = any > ( opts = { } ) { opts = this . validateOpts ( opts ) ; return this . _request ( opts ) ; } private async _defaultAdapter < T > ( opts ) { const fetchImpl = opts . fetchImplementation || fetch ; const res = ( await fetchImpl ( opts . url ! , opts ) ) as FetchResponse ; const data = await this . getResponseData ( opts , res ) ; return this . translateResponse < T > ( opts , res , data ) ; } param opts protected async _request < T = any > ( opts = { } ) { try { let translatedResponse ; if ( opts . adapter ) { translatedResponse = await opts . adapter < T > ( opts , this . _defaultAdapter . bind ( this ) ) ; } else { translatedResponse = await this . _defaultAdapter ( opts ) ; } if ( ! opts . validateStatus ! ( translatedResponse . status ) ) { throw new GaxiosError < T > ( `Request failed with status code ${ translatedResponse . status }` , opts , translatedResponse ) ; } return translatedResponse ; } catch ( e ) { const err = e as GaxiosError ; err . config = opts ; const { shouldRetry , config } = await getRetryConfig ( e ) ; if ( shouldRetry && config ) { err . config . retryConfig ! . currentRetryAttempt = config . retryConfig ! . currentRetryAttempt ; return this . _request < T > ( err . config ) ; } throw err ; } } private 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 { } return data as { } ; } case 'arraybuffer' : return res . arrayBuffer ( ) ; case 'blob' : return res . blob ( ) ; default : return res . text ( ) ; } } param opts private validateOpts ( options ) { const opts = extend ( true , { } , this . defaults , options ) ; if ( ! opts . url ) { throw new Error ( 'URL is required.' ) ; } const baseUrl = opts . baseUrl || opts . baseURL ; if ( baseUrl ) { opts . url = baseUrl + 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 . 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 . data ) { if ( isStream . readable ( opts . data ) ) { opts . body = opts . data ; } else if ( hasBuffer ( ) && Buffer . isBuffer ( opts . data ) ) { opts . body = opts . data ; if ( ! hasHeader ( opts , 'Content-Type' ) ) { opts . headers [ 'Content-Type' ] = 'application/json' ; } } else if ( typeof opts . data === 'object' ) { if ( getHeader ( opts , 'content-type' ) === 'application/x-www-form-urlencoded' ) { opts . body = opts . paramsSerializer ( opts . data ) ; } else { if ( ! hasHeader ( opts , 'Content-Type' ) ) { opts . headers [ 'Content-Type' ] = 'application/json' ; } opts . body = JSON . stringify ( opts . data ) ; } } else { opts . body = opts . data ; } } opts . validateStatus = opts . validateStatus || this . validateStatus ; opts . responseType = opts . responseType || 'json' ; if ( ! opts . headers [ 'Accept' ] && opts . responseType === 'json' ) { opts . headers [ 'Accept' ] = 'application/json' ; } opts . method = opts . method || 'GET' ; const proxy = getProxy ( opts . url ) ; if ( proxy ) { if ( this . agentCache . has ( proxy ) ) { opts . agent = this . agentCache . get ( proxy ) ; } else { if ( opts . cert && opts . key ) { const parsedURL = new URL ( proxy ) ; opts . agent = new HttpsProxyAgent ( { port : parsedURL . port , host : parsedURL . host , protocol : parsedURL . protocol , cert : opts . cert , key : opts . key , } ) ; } else { opts . agent = new HttpsProxyAgent ( proxy ) ; } this . agentCache . set ( proxy , opts . agent ! ) ; } } else if ( opts . cert && opts . key ) { if ( this . agentCache . has ( opts . key ) ) { opts . agent = this . agentCache . get ( opts . key ) ; } else { opts . agent = new HTTPSAgent ( { cert : opts . cert , key : opts . key , } ) ; this . agentCache . set ( opts . key , opts . agent ! ) ; } } return opts ; } param status private validateStatus ( status ) { return status >= 200 && status < 300 ; } param params private paramsSerializer ( params ) { return qs . stringify ( params ) ; } private translateResponse < T > ( opts , res , data ? ) { const headers = { } as Headers ; res . headers . forEach ( ( value , key ) => { headers [ key ] = value ; } ) ; return { config : opts , data : data as T , headers , status : res . status , statusText : res . statusText , request : { responseURL : res . url , } , } ; } }