From 5a3b149752e4a9a4085947f077c04d61a6949c64 Mon Sep 17 00:00:00 2001 From: Jonathan Baldie Date: Mon, 31 Aug 2026 12:28:05 +0100 Subject: [PATCH] fix: make /health endpoint trailing-slash tolerant The health route and its auth bypass both matched the exact literal path /health, so /health/ fell through to the auth check (401) and would 404 even with a valid token. Both patterns now accept an optional trailing slash. Fixes #67 Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01Q25veeccRvgA5zjp6Fhijq --- src/handler.ts | 2 +- src/middleware.ts | 2 +- tests/handler_test.ts | 7 +++++++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/handler.ts b/src/handler.ts index 2f4ea10..a16991f 100644 --- a/src/handler.ts +++ b/src/handler.ts @@ -107,7 +107,7 @@ function lengthHandler(mgr: QueueManager): RouteHandler { } function registerRoutes(router: Router, mgr: QueueManager): void { - router.get("/health", () => { + router.get("/health{/}?", () => { return new Response(JSON.stringify({ status: "ok" }), { status: 200, headers: { "Content-Type": "application/json" }, diff --git a/src/middleware.ts b/src/middleware.ts index 4707118..3fa56a6 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -3,7 +3,7 @@ import { RateLimiter } from "./rate_limiter.ts"; export type HttpHandler = (request: Request, info?: Deno.ServeHandlerInfo) => Promise | Response; export type Middleware = (next: HttpHandler) => HttpHandler; -const HEALTH_PATTERN = new URLPattern({ pathname: "/health" }); +const HEALTH_PATTERN = new URLPattern({ pathname: "/health{/}?" }); export function withAuth(apiToken: string): Middleware { return (next: HttpHandler) => { diff --git a/tests/handler_test.ts b/tests/handler_test.ts index 043edf7..43b8edc 100644 --- a/tests/handler_test.ts +++ b/tests/handler_test.ts @@ -262,6 +262,13 @@ Deno.test("response body: health returns {status:ok} JSON", async () => { assertEquals(await res.json(), { status: "ok" }); }); +Deno.test("response body: health trailing slash returns {status:ok} JSON, no auth required", async () => { + const handler = makeHandler(); + const res = await handler(new Request("http://localhost/health/")); + assertEquals(res.status, 200); + assertEquals(await res.json(), { status: "ok" }); +}); + Deno.test("response body: health POST returns 'Method not allowed'", async () => { const handler = makeHandler(); const res = await handler(new Request("http://localhost/health", { method: "POST" }));