mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-23 15:42:12 +03:00
fix(admission): reserve Responses and Messages bodies before clone (#10814)
Merged — validated together with a batch of related RaviTharuma PRs in one combined worktree (typecheck:core clean, complexity/file-size/changelog gates green, focused tests passing). Thanks for the contribution!
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { handleChat } from "@/sse/handlers/chat";
|
||||
import { initTranslators } from "@omniroute/open-sse/translator/index.ts";
|
||||
import { withChatAdmission } from "@/shared/middleware/withChatAdmission";
|
||||
|
||||
let initialized = false;
|
||||
|
||||
@@ -41,7 +42,9 @@ export async function OPTIONS() {
|
||||
* already-registered bidirectional translators. The AgentBridge MITM proxy
|
||||
* (`server.cjs`) forwards the IDE's intercepted cloudcode request here.
|
||||
*/
|
||||
export async function POST(request: Request): Promise<Response> {
|
||||
async function postHandler(request: Request): Promise<Response> {
|
||||
await ensureInitialized();
|
||||
return await handleChat(request);
|
||||
}
|
||||
|
||||
export const POST = withChatAdmission(postHandler);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { handleChat } from "@/sse/handlers/chat";
|
||||
import { initTranslators } from "@omniroute/open-sse/translator/index.ts";
|
||||
import { transformToOllama } from "@omniroute/open-sse/utils/ollamaTransform.ts";
|
||||
import { withChatAdmission } from "@/shared/middleware/withChatAdmission";
|
||||
|
||||
let initialized = false;
|
||||
|
||||
@@ -21,7 +22,7 @@ export async function OPTIONS() {
|
||||
});
|
||||
}
|
||||
|
||||
export async function POST(request) {
|
||||
async function postHandler(request) {
|
||||
await ensureInitialized();
|
||||
|
||||
const clonedReq = request.clone();
|
||||
@@ -34,3 +35,5 @@ export async function POST(request) {
|
||||
const response = await handleChat(request);
|
||||
return transformToOllama(response, modelName);
|
||||
}
|
||||
|
||||
export const POST = withChatAdmission(postHandler);
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
readCompressionRequestHeader,
|
||||
withCompressionHeaderEcho,
|
||||
} from "@/shared/utils/compressionHeaderEcho";
|
||||
import { withChatAdmission } from "@/shared/middleware/withChatAdmission";
|
||||
|
||||
let initPromise = null;
|
||||
const injectionGuard = createInjectionGuard();
|
||||
@@ -41,7 +42,7 @@ export async function OPTIONS() {
|
||||
*
|
||||
* @see https://platform.openai.com/docs/api-reference/completions
|
||||
*/
|
||||
export async function POST(request: Request) {
|
||||
async function postHandler(request: Request) {
|
||||
await ensureInitialized();
|
||||
|
||||
// #6422 — capture the compression request header once so we can echo it back
|
||||
@@ -122,3 +123,5 @@ export async function POST(request: Request) {
|
||||
compressionRequestHeader
|
||||
);
|
||||
}
|
||||
|
||||
export const POST = withChatAdmission(postHandler);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { handleChat } from "@/sse/handlers/chat";
|
||||
import { initTranslators } from "@omniroute/open-sse/translator/index.ts";
|
||||
import { withInjectionGuard } from "@/middleware/promptInjectionGuard";
|
||||
import { withChatAdmission } from "@/shared/middleware/withChatAdmission";
|
||||
import { requireJsonContentType } from "@/shared/middleware/requireJsonContentType";
|
||||
import {
|
||||
withEarlyStreamKeepalive,
|
||||
@@ -78,4 +79,4 @@ async function postHandler(request: any, context: any, preParsedBody: any = null
|
||||
return await handleChat(request, null, body);
|
||||
}
|
||||
|
||||
export const POST = withInjectionGuard(postHandler);
|
||||
export const POST = withChatAdmission(withInjectionGuard(postHandler));
|
||||
|
||||
@@ -4,6 +4,7 @@ import { initTranslators } from "@omniroute/open-sse/translator/index.ts";
|
||||
import { errorResponse } from "@omniroute/open-sse/utils/error.ts";
|
||||
import { HTTP_STATUS } from "@omniroute/open-sse/config/constants.ts";
|
||||
import { getRegistryEntry } from "@omniroute/open-sse/config/providerRegistry.ts";
|
||||
import { withChatAdmission } from "@/shared/middleware/withChatAdmission";
|
||||
|
||||
let initialized = false;
|
||||
|
||||
@@ -31,7 +32,7 @@ export async function OPTIONS() {
|
||||
* Routes to the specified provider, validating model/provider match.
|
||||
* Full body format validation is delegated to handleChat.
|
||||
*/
|
||||
export async function POST(request, { params }) {
|
||||
async function postHandler(request, { params }) {
|
||||
const { provider: rawProvider } = await params;
|
||||
|
||||
const providerEntry = getRegistryEntry(rawProvider);
|
||||
@@ -103,3 +104,5 @@ export async function POST(request, { params }) {
|
||||
|
||||
return await handleChat(newRequest, () => buildClientRawRequest(request, rawBody));
|
||||
}
|
||||
|
||||
export const POST = withChatAdmission(postHandler);
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
import { CORS_HEADERS, handleCorsOptions } from "@/shared/utils/cors";
|
||||
import { handleChat } from "@/sse/handlers/chat";
|
||||
import { withChatAdmission } from "@/shared/middleware/withChatAdmission";
|
||||
import { createInjectionGuard } from "@/middleware/promptInjectionGuard";
|
||||
import { getRelayTokenByHash, checkRateLimit, recordRelayUsage } from "@/lib/db/relayProxies";
|
||||
import {
|
||||
@@ -199,7 +200,7 @@ export async function OPTIONS() {
|
||||
return handleCorsOptions();
|
||||
}
|
||||
|
||||
export async function POST(request: Request) {
|
||||
async function postHandler(request: Request) {
|
||||
const startTime = Date.now();
|
||||
const clientIp = getClientIp(request);
|
||||
const userAgent = sanitizeForensicHeader(request.headers.get("user-agent"));
|
||||
@@ -433,3 +434,5 @@ export async function POST(request: Request) {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export const POST = withChatAdmission(postHandler);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { handleChat } from "@/sse/handlers/chat";
|
||||
import { initTranslators } from "@omniroute/open-sse/translator/index.ts";
|
||||
import { withChatAdmission } from "@/shared/middleware/withChatAdmission";
|
||||
|
||||
let initialized = false;
|
||||
|
||||
@@ -25,7 +26,9 @@ export async function OPTIONS() {
|
||||
* Reuses the shared chat handler so native Codex passthrough can keep
|
||||
* arbitrary Responses suffixes all the way to the upstream provider.
|
||||
*/
|
||||
export async function POST(request) {
|
||||
async function postHandler(request) {
|
||||
await ensureInitialized();
|
||||
return await handleChat(request);
|
||||
}
|
||||
|
||||
export const POST = withChatAdmission(postHandler);
|
||||
|
||||
48
src/shared/middleware/withChatAdmission.ts
Normal file
48
src/shared/middleware/withChatAdmission.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Compose process-wide chat admission in front of a route handler.
|
||||
*
|
||||
* Uses the shipped `admitChatRequest` budget/fairness controller — it does not
|
||||
* introduce a second admission path. Call this *outside* `withInjectionGuard`
|
||||
* so a large `/v1/responses` or `/v1/messages` body is reserved (or 503-shed)
|
||||
* before `request.clone()` / `.json()`.
|
||||
*/
|
||||
import {
|
||||
admitChatRequest,
|
||||
CHAT_ADMISSION_QUEUE_MAX_MS,
|
||||
releaseChatAdmissionAfterHandler,
|
||||
resolveSessionId,
|
||||
type ChatAdmissionController,
|
||||
} from "./chatBodyAdmission";
|
||||
|
||||
type RouteHandler = (request: Request, ...args: any[]) => Promise<Response> | Response;
|
||||
|
||||
export function withChatAdmission(
|
||||
handler: RouteHandler,
|
||||
options: {
|
||||
controller?: ChatAdmissionController;
|
||||
queueMs?: number;
|
||||
largeBodyBytes?: number;
|
||||
hardMaxBytes?: number;
|
||||
} = {}
|
||||
): RouteHandler {
|
||||
return async function admittedHandler(request: Request, ...args: any[]) {
|
||||
const sessionId = resolveSessionId(request);
|
||||
const admission = await admitChatRequest(request, {
|
||||
sessionId,
|
||||
queueMs: options.queueMs ?? CHAT_ADMISSION_QUEUE_MAX_MS,
|
||||
controller: options.controller,
|
||||
largeBodyBytes: options.largeBodyBytes,
|
||||
hardMaxBytes: options.hardMaxBytes,
|
||||
});
|
||||
if (admission.admit === false) return admission.response;
|
||||
try {
|
||||
return await releaseChatAdmissionAfterHandler(
|
||||
Promise.resolve(handler(admission.request, ...args)),
|
||||
admission.lease
|
||||
);
|
||||
} catch (error) {
|
||||
admission.lease?.release();
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user