(
optionsInput: PromiseOrValue<FinalRequestOptions>,
retriesRemaining: number | null,
retryOfRequestLogID: string | undefined,
)
| 750 | } |
| 751 | |
| 752 | private async makeRequest( |
| 753 | optionsInput: PromiseOrValue<FinalRequestOptions>, |
| 754 | retriesRemaining: number | null, |
| 755 | retryOfRequestLogID: string | undefined, |
| 756 | ): Promise<APIResponseProps> { |
| 757 | const options = await optionsInput; |
| 758 | const maxRetries = options.maxRetries ?? this.maxRetries; |
| 759 | if (retriesRemaining == null) { |
| 760 | retriesRemaining = maxRetries; |
| 761 | } |
| 762 | |
| 763 | await this.prepareOptions(options); |
| 764 | |
| 765 | const { req, url, timeout } = await this.buildRequest(options, { |
| 766 | retryCount: maxRetries - retriesRemaining, |
| 767 | }); |
| 768 | |
| 769 | await this.prepareRequest(req, { url, options }); |
| 770 | await this._provider?.prepareRequest?.(req, { url, options }); |
| 771 | |
| 772 | /** Not an API request ID, just for correlating local log entries. */ |
| 773 | const requestLogID = 'log_' + ((Math.random() * (1 << 24)) | 0).toString(16).padStart(6, '0'); |
| 774 | const retryLogStr = retryOfRequestLogID === undefined ? '' : `, retryOf: ${retryOfRequestLogID}`; |
| 775 | const startTime = Date.now(); |
| 776 | |
| 777 | loggerFor(this).debug( |
| 778 | `[${requestLogID}] sending request`, |
| 779 | formatRequestDetails({ |
| 780 | retryOfRequestLogID, |
| 781 | method: options.method, |
| 782 | url, |
| 783 | options, |
| 784 | headers: req.headers, |
| 785 | }), |
| 786 | ); |
| 787 | |
| 788 | if (options.signal?.aborted) { |
| 789 | throw new Errors.APIUserAbortError(); |
| 790 | } |
| 791 | |
| 792 | const security = options.__security ?? { bearerAuth: true }; |
| 793 | const controller = new AbortController(); |
| 794 | const response = await this.fetchWithAuth(url, req, timeout, controller, security).catch(castToError); |
| 795 | const headersTime = Date.now(); |
| 796 | |
| 797 | if (response instanceof globalThis.Error) { |
| 798 | const retryMessage = `retrying, ${retriesRemaining} attempts remaining`; |
| 799 | if (options.signal?.aborted) { |
| 800 | throw new Errors.APIUserAbortError(); |
| 801 | } |
| 802 | // detect native connection timeout errors |
| 803 | // deno throws "TypeError: error sending request for url (https://example/): client error (Connect): tcp connect error: Operation timed out (os error 60): Operation timed out (os error 60)" |
| 804 | // undici throws "TypeError: fetch failed" with cause "ConnectTimeoutError: Connect Timeout Error (attempted address: example:443, timeout: 1ms)" |
| 805 | // others do not provide enough information to distinguish timeouts from other connection errors |
| 806 | const isTimeout = |
| 807 | isAbortError(response) || |
| 808 | /timed? ?out/i.test(String(response) + ('cause' in response ? String(response.cause) : '')); |
| 809 | if (retriesRemaining) { |
no test coverage detected