feat: complete Ancestor quote bot with production-ready Docker support
This commit is contained in:
115
node_modules/matrix-js-sdk/src/http-api/utils.ts
generated
vendored
115
node_modules/matrix-js-sdk/src/http-api/utils.ts
generated
vendored
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2022 - 2024 The Matrix.org Foundation C.I.C.
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -14,18 +14,11 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { parse as parseContentType, type ContentType } from "content-type";
|
||||
import { parse as parseContentType, ParsedMediaType } from "content-type";
|
||||
|
||||
import { logger } from "../logger.ts";
|
||||
import { sleep } from "../utils.ts";
|
||||
import {
|
||||
ConnectionError,
|
||||
HTTPError,
|
||||
MatrixError,
|
||||
MatrixSafetyError,
|
||||
MatrixSafetyErrorCode,
|
||||
safeGetRetryAfterMs,
|
||||
} from "./errors.ts";
|
||||
import { logger } from "../logger";
|
||||
import { sleep } from "../utils";
|
||||
import { ConnectionError, HTTPError, MatrixError } from "./errors";
|
||||
|
||||
// Ponyfill for https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal/timeout
|
||||
export function timeoutSignal(ms: number): AbortSignal {
|
||||
@@ -39,7 +32,7 @@ export function timeoutSignal(ms: number): AbortSignal {
|
||||
|
||||
export function anySignal(signals: AbortSignal[]): {
|
||||
signal: AbortSignal;
|
||||
cleanup(this: void): void;
|
||||
cleanup(): void;
|
||||
} {
|
||||
const controller = new AbortController();
|
||||
|
||||
@@ -79,48 +72,24 @@ export function anySignal(signals: AbortSignal[]): {
|
||||
* @returns
|
||||
*/
|
||||
export function parseErrorResponse(response: XMLHttpRequest | Response, body?: string): Error {
|
||||
const httpHeaders = isXhr(response)
|
||||
? new Headers(
|
||||
response
|
||||
.getAllResponseHeaders()
|
||||
.trim()
|
||||
.split(/[\r\n]+/)
|
||||
.map((header): [string, string] => {
|
||||
const colonIdx = header.indexOf(":");
|
||||
return [header.substring(0, colonIdx), header.substring(colonIdx + 1)];
|
||||
}),
|
||||
)
|
||||
: response.headers;
|
||||
|
||||
let contentType: ContentType | null;
|
||||
let contentType: ParsedMediaType | null;
|
||||
try {
|
||||
contentType = getResponseContentType(httpHeaders);
|
||||
contentType = getResponseContentType(response);
|
||||
} catch (e) {
|
||||
return <Error>e;
|
||||
}
|
||||
|
||||
if (contentType?.type === "application/json" && body) {
|
||||
const errorBody = JSON.parse(body);
|
||||
if (errorBody.errcode && MatrixSafetyErrorCode.matches(errorBody.errcode)) {
|
||||
return new MatrixSafetyError(
|
||||
errorBody,
|
||||
response.status,
|
||||
isXhr(response) ? response.responseURL : response.url,
|
||||
undefined,
|
||||
httpHeaders,
|
||||
);
|
||||
}
|
||||
return new MatrixError(
|
||||
errorBody,
|
||||
JSON.parse(body),
|
||||
response.status,
|
||||
isXhr(response) ? response.responseURL : response.url,
|
||||
undefined,
|
||||
httpHeaders,
|
||||
);
|
||||
}
|
||||
if (contentType?.type === "text/plain") {
|
||||
return new HTTPError(`Server returned ${response.status} error: ${body}`, response.status, httpHeaders);
|
||||
return new HTTPError(`Server returned ${response.status} error: ${body}`, response.status);
|
||||
}
|
||||
return new HTTPError(`Server returned ${response.status} error`, response.status, httpHeaders);
|
||||
return new HTTPError(`Server returned ${response.status} error`, response.status);
|
||||
}
|
||||
|
||||
function isXhr(response: XMLHttpRequest | Response): response is XMLHttpRequest {
|
||||
@@ -128,7 +97,7 @@ function isXhr(response: XMLHttpRequest | Response): response is XMLHttpRequest
|
||||
}
|
||||
|
||||
/**
|
||||
* extract the Content-Type header from response headers, and
|
||||
* extract the Content-Type header from the response object, and
|
||||
* parse it to a `{type, parameters}` object.
|
||||
*
|
||||
* returns null if no content-type header could be found.
|
||||
@@ -136,10 +105,21 @@ function isXhr(response: XMLHttpRequest | Response): response is XMLHttpRequest
|
||||
* @param response - response object
|
||||
* @returns parsed content-type header, or null if not found
|
||||
*/
|
||||
function getResponseContentType(headers: Headers): ContentType | null {
|
||||
const contentType = headers.get("Content-Type");
|
||||
if (contentType === null) return null;
|
||||
return parseContentType(contentType);
|
||||
function getResponseContentType(response: XMLHttpRequest | Response): ParsedMediaType | null {
|
||||
let contentType: string | null;
|
||||
if (isXhr(response)) {
|
||||
contentType = response.getResponseHeader("Content-Type");
|
||||
} else {
|
||||
contentType = response.headers.get("Content-Type");
|
||||
}
|
||||
|
||||
if (!contentType) return null;
|
||||
|
||||
try {
|
||||
return parseContentType(contentType);
|
||||
} catch (e) {
|
||||
throw new Error(`Error parsing Content-Type '${contentType}': ${e}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -171,42 +151,3 @@ export async function retryNetworkOperation<T>(maxAttempts: number, callback: ()
|
||||
}
|
||||
throw lastConnectionError;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the backoff time for a request retry attempt.
|
||||
* This produces wait times of 2, 4, 8, and 16 seconds (30s total) after which we give up. If the
|
||||
* failure was due to a rate limited request, the time specified in the error is returned.
|
||||
*
|
||||
* Returns -1 if the error is not retryable, or if we reach the maximum number of attempts.
|
||||
*
|
||||
* @param err - The error thrown by the http call
|
||||
* @param attempts - The number of attempts made so far, including the one that just failed.
|
||||
* @param retryConnectionError - Whether to retry on {@link ConnectionError} (CORS, connection is down, etc.)
|
||||
*/
|
||||
export function calculateRetryBackoff(err: any, attempts: number, retryConnectionError: boolean): number {
|
||||
if (attempts > 4) {
|
||||
return -1; // give up
|
||||
}
|
||||
|
||||
if (err instanceof ConnectionError && !retryConnectionError) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (err.httpStatus && Math.floor(err.httpStatus / 100) === 4 && err.httpStatus !== 429) {
|
||||
// client error; no amount of retrying will save you now (except for rate limiting which is handled below)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (err.name === "AbortError") {
|
||||
// this is a client timeout, that is already very high 60s/80s
|
||||
// we don't want to retry, as it could do it for very long
|
||||
return -1;
|
||||
}
|
||||
|
||||
// If we are trying to send an event (or similar) that is too large in any way, then retrying won't help
|
||||
if (err.name === "M_TOO_LARGE") {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return safeGetRetryAfterMs(err, 1000 * Math.pow(2, attempts));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user