UNPKG

@oada/client

Version:

A lightweight client tool to interact with an OADA-compliant server

104 lines 3.63 kB
/** * @license * Copyright 2021 Open Ag Data Alliance * * 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. */ /** * Stuff for having client handle "recoverable" errors * rather than passing everything up to the user * * @packageDocumentation */ import { setTimeout } from "isomorphic-timers-promises"; import debug from "debug"; import { Headers } from "#fetch"; import { fixError } from "./utils.js"; const warn = debug("@oada/client:errors:warn"); const trace = debug("@oada/client:errors:trace"); /** * Wait 5 minutes if 429 with no Retry-After header * * @todo add override for this in client config? */ const DEFAULT_RETRY_TIMEOUT = 5 * 60 * 1_000; /** * Handle rate limit errors * * Wait the length specified by Retry-After header, * or `DEFAULT_RETRY_TIMEOUT` if the header is not present. */ async function handleRatelimit(error, request, ...rest) { // @ts-expect-error stupid errors const headers = new Headers(error.headers); // Figure out how many ms to wait // Header is either number of seconds, or a date/time const retry = headers.get("Retry-After") || headers.get("RateLimit-Reset") || headers.get("X-RateLimit-Reset"); const timeout = retry ? Number(retry) * 1000 || Number(new Date(retry)) - Date.now() : DEFAULT_RETRY_TIMEOUT; warn(error, `Received error, retrying in ${timeout} ms`); await setTimeout(timeout); return handleErrors(request, ...rest); } /** * Handle connection reset * * Wait a while then try to connect again. */ async function handleReset(error, request, ...rest) { warn(error, "Connection reset, retrying in 10000 ms"); await setTimeout(10_000); return handleErrors(request, ...rest); } /** * Handle any errors that client can deal with, * otherwise reject with original error. */ export async function handleErrors(request, ...rest) { try { return await request(...rest); } catch (cError) { // FIXME: WTF why is error an array sometimes??? // @ts-expect-error stupid error handling // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const error = cError?.[0]?.error ?? cError?.[0] ?? cError?.error ?? cError; trace(error, "Attempting to handle error"); // @ts-expect-error stupid error handling switch (`${error.status ?? cError?.code}`) { case "429": { return handleRatelimit(error, request, ...rest); } // Some servers use 503 for rate limit... case "503": { const headers = new Headers(error.headers); if (headers.has("Retry-After")) { return handleRatelimit(error, request, ...rest); } // If no Retry-After, don't assume rate-limit? break; } case "ECONNRESET": { return handleReset(error, request, ...rest); } default: // Do nothing } // Pass error up throw await fixError(cError); } } //# sourceMappingURL=errors.js.map