mirror of
https://github.com/samuelthomas2774/nxapi.git
synced 2026-04-21 14:37:59 -05:00
Handle HTTP 502/503/504 errors as temporary
This commit is contained in:
parent
67ab81a612
commit
34d6a47322
|
|
@ -1,10 +1,13 @@
|
|||
import createDebug from 'debug';
|
||||
import { NextFunction, Request, RequestHandler, Response } from 'express';
|
||||
import { ErrorResponse } from '../../api/util.js';
|
||||
import { temporary_http_errors, temporary_system_errors } from '../../util/errors.js';
|
||||
|
||||
const debug = createDebug('cli:util:http-server');
|
||||
|
||||
export class HttpServer {
|
||||
retry_after = 60;
|
||||
|
||||
protected createApiRequestHandler(callback: (req: Request, res: Response) => Promise<{} | void>, auth = false) {
|
||||
return async (req: Request, res: Response) => {
|
||||
try {
|
||||
|
|
@ -42,13 +45,17 @@ export class HttpServer {
|
|||
return JSON.stringify(data, null, space);
|
||||
}
|
||||
|
||||
protected handleRequestError(req: Request, res: Response, err: unknown) {
|
||||
protected handleRequestError(req: Request, res: Response, err: unknown, retry_after = this.retry_after) {
|
||||
debug('Error in request %s %s', req.method, req.url, err);
|
||||
|
||||
if (err instanceof ErrorResponse) {
|
||||
const retry_after = err.response.headers.get('Retry-After');
|
||||
const response_retry_after = err.response.headers.get('Retry-After');
|
||||
|
||||
if (retry_after && /^\d+$/.test(retry_after)) {
|
||||
if (response_retry_after && /^\d+$/.test(response_retry_after)) {
|
||||
res.setHeader('Retry-After', response_retry_after);
|
||||
}
|
||||
|
||||
if (temporary_http_errors.includes(err.response.status) && !response_retry_after) {
|
||||
res.setHeader('Retry-After', retry_after);
|
||||
}
|
||||
}
|
||||
|
|
@ -56,8 +63,8 @@ export class HttpServer {
|
|||
if (err && typeof err === 'object' && 'type' in err && 'code' in err && (err as any).type === 'system') {
|
||||
const code: string = (err as any).code;
|
||||
|
||||
if (code === 'ETIMEDOUT' || code === 'ENOTFOUND' || code === 'EAI_AGAIN') {
|
||||
res.setHeader('Retry-After', '60');
|
||||
if (code in temporary_system_errors) {
|
||||
res.setHeader('Retry-After', retry_after);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,8 +7,9 @@ import { ErrorResponse } from '../api/util.js';
|
|||
import { SavedToken } from './auth/coral.js';
|
||||
import { SplatNet2RecordsMonitor } from './splatnet2/monitor.js';
|
||||
import Loop, { LoopResult } from '../util/loop.js';
|
||||
import { getTitleIdFromEcUrl, hrduration, TemporaryErrorSymbol } from '../util/misc.js';
|
||||
import { getTitleIdFromEcUrl, hrduration } from '../util/misc.js';
|
||||
import { CoralUser } from './users.js';
|
||||
import { handleError } from '../util/errors.js';
|
||||
|
||||
const debug = createDebug('nxapi:nso:notify');
|
||||
const debugFriends = createDebug('nxapi:nso:notify:friends');
|
||||
|
|
@ -183,35 +184,6 @@ export class ZncNotifications extends Loop {
|
|||
}
|
||||
}
|
||||
|
||||
export async function handleError(
|
||||
err: ErrorResponse<CoralErrorResponse> | NodeJS.ErrnoException,
|
||||
loop: Loop,
|
||||
): Promise<LoopResult> {
|
||||
if (TemporaryErrorSymbol in err && err[TemporaryErrorSymbol]) {
|
||||
debug('Temporary error, waiting %ds before retrying', loop.update_interval, err);
|
||||
|
||||
return LoopResult.OK;
|
||||
} else if ('code' in err && (err as any).type === 'system' && err.code === 'ETIMEDOUT') {
|
||||
debug('Request timed out, waiting %ds before retrying', loop.update_interval, err);
|
||||
|
||||
return LoopResult.OK;
|
||||
} else if ('code' in err && (err as any).type === 'system' && err.code === 'ENOTFOUND') {
|
||||
debug('Request error, waiting %ds before retrying', loop.update_interval, err);
|
||||
|
||||
return LoopResult.OK;
|
||||
} else if ('code' in err && (err as any).type === 'system' && err.code === 'EAI_AGAIN') {
|
||||
debug('Request error - name resolution failed, waiting %ds before retrying', loop.update_interval, err);
|
||||
|
||||
return LoopResult.OK;
|
||||
} else if ('code' in err && (err as any).type === 'system' && err.code === 'ECONNRESET') {
|
||||
debug('Request error - connection reset, waiting %ds before retrying', loop.update_interval, err);
|
||||
|
||||
return LoopResult.OK;
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
export class NotificationManager {
|
||||
onPresenceUpdated?(friend: CurrentUser | Friend, prev?: CurrentUser | Friend, type?: PresenceEvent, naid?: string, ir?: boolean): void;
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import EventSource from 'eventsource';
|
|||
import { DiscordRpcClient, findDiscordRpcClient } from '../discord/rpc.js';
|
||||
import { getDiscordPresence, getInactiveDiscordPresence } from '../discord/util.js';
|
||||
import { DiscordPresencePlayTime, DiscordPresenceContext, DiscordPresence, ExternalMonitorConstructor, ExternalMonitor, ErrorResult } from '../discord/types.js';
|
||||
import { EmbeddedSplatNet2Monitor, handleError, ZncNotifications } from './notify.js';
|
||||
import { EmbeddedSplatNet2Monitor, ZncNotifications } from './notify.js';
|
||||
import { getPresenceFromUrl } from '../api/znc-proxy.js';
|
||||
import { ActiveEvent, CurrentUser, Friend, Game, Presence, PresenceState, CoralErrorResponse } from '../api/coral-types.js';
|
||||
import { ErrorResponse, ResponseSymbol } from '../api/util.js';
|
||||
|
|
@ -12,6 +12,7 @@ import { getTitleIdFromEcUrl } from '../index.js';
|
|||
import { parseLinkHeader } from '../util/http.js';
|
||||
import { getUserAgent } from '../util/useragent.js';
|
||||
import { TemporaryErrorSymbol } from '../util/misc.js';
|
||||
import { handleError } from '../util/errors.js';
|
||||
|
||||
const debug = createDebug('nxapi:nso:presence');
|
||||
const debugEventStream = createDebug('nxapi:nso:presence:sse');
|
||||
|
|
|
|||
42
src/util/errors.ts
Normal file
42
src/util/errors.ts
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
import createDebug from 'debug';
|
||||
import Loop, { LoopResult } from './loop.js';
|
||||
import { CoralErrorResponse } from '../api/coral-types.js';
|
||||
import { ErrorResponse } from '../api/util.js';
|
||||
import { TemporaryErrorSymbol } from './misc.js';
|
||||
|
||||
const debug = createDebug('nxapi:util:errors');
|
||||
|
||||
export const temporary_system_errors = {
|
||||
'ETIMEDOUT': 'request timed out',
|
||||
'ENOTFOUND': null,
|
||||
'EAI_AGAIN': 'name resolution failed',
|
||||
'ECONNRESET': 'connection reset',
|
||||
};
|
||||
export const temporary_http_errors = [
|
||||
502, // Bad Gateway
|
||||
503, // Service Unavailable
|
||||
504, // Gateway Timeout
|
||||
];
|
||||
|
||||
export async function handleError(
|
||||
err: ErrorResponse<CoralErrorResponse> | NodeJS.ErrnoException,
|
||||
loop: Loop,
|
||||
): Promise<LoopResult> {
|
||||
if (TemporaryErrorSymbol in err && err[TemporaryErrorSymbol]) {
|
||||
debug('Temporary error, waiting %ds before retrying', loop.update_interval, err);
|
||||
|
||||
return LoopResult.OK;
|
||||
} else if ('code' in err && (err as any).type === 'system' && err.code && err.code in temporary_system_errors) {
|
||||
const desc = temporary_system_errors[err.code as keyof typeof temporary_system_errors];
|
||||
debug('Request error%s, waiting %ds before retrying', desc ? ' - ' + desc : '', loop.update_interval, err);
|
||||
|
||||
return LoopResult.OK;
|
||||
} else if (err instanceof ErrorResponse && temporary_http_errors.includes(err.response.status)) {
|
||||
debug('Request error - HTTP %s (%s), waiting %ds before retrying',
|
||||
err.response.status, err.response.statusText, loop.update_interval, err);
|
||||
|
||||
return LoopResult.OK;
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user