mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-26 09:52:11 +03:00
fix(api): add explicit HEAD handler for /v1/models to prevent ~6s hang (#6400) Integrated into release/v3.8.47. (thanks @chirag127)
This commit is contained in:
@@ -9,6 +9,18 @@ export async function OPTIONS() {
|
||||
return handleCorsOptions();
|
||||
}
|
||||
|
||||
/**
|
||||
* HEAD /v1/models/{model} — availability probe (RFC 9110 §9.3.2).
|
||||
* Prevents Next.js auto-derived HEAD from streaming the full GET body,
|
||||
* which caused ~6s hangs for SDK/gateway health probes (#6400).
|
||||
*/
|
||||
export async function HEAD() {
|
||||
return new Response(null, {
|
||||
status: 200,
|
||||
headers: { "content-type": "application/json" },
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /v1/models/{model} — OpenAI-compatible single-model retrieval (#4674).
|
||||
*
|
||||
|
||||
@@ -6,12 +6,27 @@ import { getUnifiedModelsResponse } from "./catalog";
|
||||
export async function OPTIONS() {
|
||||
return new Response(null, {
|
||||
headers: {
|
||||
"Access-Control-Allow-Methods": "GET, OPTIONS",
|
||||
"Access-Control-Allow-Methods": "GET, HEAD, OPTIONS",
|
||||
"Access-Control-Allow-Headers": "*",
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* HEAD /v1/models - availability probe (RFC 9110 §9.3.2: HEAD returns headers only)
|
||||
*
|
||||
* Explicit handler prevents Next.js auto-derived HEAD from streaming the full
|
||||
* GET body (200+ providers enumerated on-demand), which caused ~6s hang for
|
||||
* clients like the OpenAI SDK that use HEAD as a health/preflight probe.
|
||||
* See: https://github.com/diegosouzapw/OmniRoute/issues/6400
|
||||
*/
|
||||
export async function HEAD() {
|
||||
return new Response(null, {
|
||||
status: 200,
|
||||
headers: { "content-type": "application/json" },
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /v1/models - OpenAI compatible models list
|
||||
*/
|
||||
|
||||
48
tests/unit/head-request-v1-models-6400.test.ts
Normal file
48
tests/unit/head-request-v1-models-6400.test.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import { describe, it } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
import * as modelsRoute from "../../src/app/api/v1/models/route";
|
||||
|
||||
/**
|
||||
* Regression test for issue #6400.
|
||||
*
|
||||
* Before the fix, `src/app/api/v1/models/route.ts` exported only `OPTIONS` and
|
||||
* `GET`. Next.js 16 App Router auto-derives `HEAD` from `GET` when no explicit
|
||||
* handler exists, and the derived `HEAD` streams the full `GET` body (which
|
||||
* the client discards). Because `getUnifiedModelsResponse()` enumerates 200+
|
||||
* providers on-demand, the body stream stayed open ~6s — clients issuing HEAD
|
||||
* as an availability probe (OpenAI SDK, openai-python, httpx, gateway
|
||||
* health-checkers) stalled until their timeout fired.
|
||||
*
|
||||
* RFC 9110 §9.3.2: HEAD MUST close after the headers.
|
||||
*
|
||||
* Fix: explicit `HEAD` handler that returns `{ status: 200, body: null }`, and
|
||||
* `OPTIONS` advertises `HEAD` in `Access-Control-Allow-Methods`.
|
||||
*/
|
||||
describe("issue #6400 — HEAD /v1/models returns immediately", () => {
|
||||
it("exports an explicit HEAD handler", () => {
|
||||
assert.equal(
|
||||
typeof (modelsRoute as { HEAD?: unknown }).HEAD,
|
||||
"function",
|
||||
"HEAD export missing — Next.js will auto-derive from GET and stream the body"
|
||||
);
|
||||
});
|
||||
|
||||
it("HEAD returns 200 with a null body (no streaming)", async () => {
|
||||
const head = (modelsRoute as unknown as {
|
||||
HEAD: () => Promise<Response>;
|
||||
}).HEAD;
|
||||
const response = await head();
|
||||
assert.equal(response.status, 200);
|
||||
assert.equal(response.body, null, "HEAD body must be null per RFC 9110 §9.3.2");
|
||||
});
|
||||
|
||||
it("OPTIONS advertises HEAD in Access-Control-Allow-Methods", async () => {
|
||||
const response = await modelsRoute.OPTIONS();
|
||||
const methods = response.headers.get("Access-Control-Allow-Methods") ?? "";
|
||||
assert.ok(
|
||||
/\bHEAD\b/.test(methods),
|
||||
`expected HEAD in Access-Control-Allow-Methods, got: ${methods}`
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user