mirror of
https://github.com/Unleash/unleash.git
synced 2025-01-06 00:07:44 +01:00
b06613d1b0
### What The heaviest requests we serve are the register and metrics POSTs from our SDKs/clients. This PR adds ratelimiting to /api/client/register, /api/client/metrics, /api/frontend/register and /api/frontend/metrics with a default set to 6000 requests per minute (or 100 rps) for each of the endpoints. It will be overrideable by the environment variables documented. ### Points of discussion @kwasniew already suggested using featuretoggles with variants to control the rate per clientId. I struggled to see if we could dynamically update the middleware after initialisation, so this attempt will need a restart of the pod to update the request limit.
62 lines
1.9 KiB
TypeScript
62 lines
1.9 KiB
TypeScript
import joi from 'joi';
|
|
import { Response } from 'express';
|
|
import { Logger } from '../logger';
|
|
import { UnleashError } from '../error/unleash-error';
|
|
import { fromLegacyError } from '../error/from-legacy-error';
|
|
import createError from 'http-errors';
|
|
|
|
export const customJoi = joi.extend((j) => ({
|
|
type: 'isUrlFriendly',
|
|
base: j.string(),
|
|
messages: {
|
|
'isUrlFriendly.base': '{{#label}} must be URL friendly',
|
|
},
|
|
validate(value, helpers) {
|
|
// Base validation regardless of the rules applied
|
|
if (encodeURIComponent(value) !== value) {
|
|
// Generate an error, state and options need to be passed
|
|
return { value, errors: helpers.error('isUrlFriendly.base') };
|
|
}
|
|
return undefined;
|
|
},
|
|
}));
|
|
|
|
export const nameType = customJoi.isUrlFriendly().min(1).max(100).required();
|
|
|
|
export const handleErrors: (
|
|
res: Response,
|
|
logger: Logger,
|
|
error: Error,
|
|
) => void = (res, logger, error) => {
|
|
if (createError.isHttpError(error)) {
|
|
return res
|
|
.status(
|
|
// @ts-expect-error - The error object here is not guaranteed to contain status
|
|
error.status ?? 400,
|
|
)
|
|
.json({ message: error.message });
|
|
}
|
|
|
|
const finalError =
|
|
error instanceof UnleashError ? error : fromLegacyError(error);
|
|
|
|
const format = (thing: object) => JSON.stringify(thing, null, 2);
|
|
|
|
if (!(error instanceof UnleashError)) {
|
|
logger.debug(
|
|
`I encountered an error that wasn't an instance of the \`UnleashError\` type. The original error was: ${format(
|
|
error,
|
|
)}. It was mapped to ${format(finalError.toJSON())}`,
|
|
);
|
|
}
|
|
|
|
if (finalError.statusCode === 500) {
|
|
logger.error(
|
|
`Server failed executing request: ${format(error)}`,
|
|
error,
|
|
);
|
|
}
|
|
|
|
return res.status(finalError.statusCode).json(finalError).end();
|
|
};
|