Skip to content

Commit f856875

Browse files
authored
Merge pull request #1007 from AikidoSec/add-retry-after-header-hono
Set Retry-After header in middleware
2 parents 1b55ee9 + aeeaae6 commit f856875

20 files changed

Lines changed: 256 additions & 70 deletions

docs/generic-middleware.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ function onRequest(...) {
3232
message += ` (Your IP: ${result.ip})`;
3333
}
3434

35-
// Block the request and send a http 429 status code
35+
// Return a 429 response with a Retry-After header, e.g.:
36+
// res.setHeader("Retry-After", result.retryAfterSeconds.toString());
3637
return ...;
3738
}
3839

library/middleware/express.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,11 @@ export function addExpressMiddleware(app: Express | Router) {
1919
message += ` (Your IP: ${escapeHTML(result.ip)})`;
2020
}
2121

22-
res.status(429).type("text").send(message);
22+
res
23+
.status(429)
24+
.type("text")
25+
.header("Retry-After", result.retryAfterSeconds.toString())
26+
.send(message);
2327
return;
2428
}
2529

library/middleware/fastify.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import type { FastifyInstance } from "fastify";
55

66
export type FastifyReply = {
77
status(code: number): FastifyReply;
8+
header(key: string, value: string): FastifyReply;
89
send(payload: string): FastifyReply;
910
};
1011

@@ -41,7 +42,10 @@ export const fastifyHook: FastifyHookHandler = (_, reply, done) => {
4142
message += ` (Your IP: ${escapeHTML(result.ip)})`;
4243
}
4344

44-
return reply.status(429).send(message);
45+
return reply
46+
.status(429)
47+
.header("Retry-After", result.retryAfterSeconds.toString())
48+
.send(message);
4549
}
4650

4751
if (result.type === "blocked") {

library/middleware/hapi.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,11 @@ export function addHapiMiddleware(app: Server) {
1818
message += ` (Your IP: ${escapeHTML(result.ip)})`;
1919
}
2020

21-
return h.response(message).code(429).takeover();
21+
return h
22+
.response(message)
23+
.code(429)
24+
.header("Retry-After", result.retryAfterSeconds.toString())
25+
.takeover();
2226
}
2327

2428
if (result.type === "blocked") {

library/middleware/hono.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ export function addHonoMiddleware<
2323
message += ` (Your IP: ${escapeHTML(result.ip)})`;
2424
}
2525

26-
return c.text(message, 429);
26+
return c.text(message, 429, {
27+
"Retry-After": result.retryAfterSeconds.toString(),
28+
});
2729
}
2830

2931
if (result.type === "blocked") {

library/middleware/koa.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export function addKoaMiddleware(app: Application): void {
2222
ctx.type = "text/plain";
2323
ctx.body = message;
2424
ctx.status = 429;
25+
ctx.set("Retry-After", result.retryAfterSeconds.toString());
2526
return;
2627
}
2728

library/middleware/restify.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export function addRestifyMiddleware(server: any) {
1919

2020
res.status(429);
2121
res.setHeader("Content-Type", "text/plain");
22+
res.setHeader("Retry-After", result.retryAfterSeconds.toString());
2223
res.send(message);
2324

2425
return next(false);

library/middleware/shouldBlockRequest.ts

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,20 @@ import { getInstance } from "../agent/AgentSingleton";
44
import { getContext, updateContext } from "../agent/Context";
55
import { shouldRateLimitRequest } from "../ratelimiting/shouldRateLimitRequest";
66

7-
type Result = {
8-
block: boolean;
9-
type?: "ratelimited" | "blocked";
10-
trigger?: "ip" | "user" | "group";
11-
ip?: string;
12-
};
7+
type Result =
8+
| { block: false }
9+
| {
10+
block: true;
11+
type: "ratelimited";
12+
trigger: "ip" | "user" | "group";
13+
ip?: string;
14+
retryAfterSeconds: number;
15+
}
16+
| {
17+
block: true;
18+
type: "blocked";
19+
trigger: "user";
20+
};
1321

1422
export function shouldBlockRequest(): Result {
1523
const context = getContext();
@@ -49,6 +57,7 @@ export function shouldBlockRequest(): Result {
4957
type: "ratelimited",
5058
trigger: rateLimitResult.trigger,
5159
ip: context.remoteAddress,
60+
retryAfterSeconds: rateLimitResult.retryAfterSeconds,
5261
};
5362
}
5463

0 commit comments

Comments
 (0)