fix(api): reject oversized request bodies with 413
This commit is contained in:
+49
-12
@@ -17,6 +17,11 @@ import { EmbeddingsHandler } from './handlers/embeddings.ts';
|
||||
import { AuthMiddleware } from './middleware/auth.ts';
|
||||
import { SanityMiddleware } from './middleware/sanity.ts';
|
||||
|
||||
interface IParsedRequestBody {
|
||||
kind: 'ok' | 'invalid' | 'too_large';
|
||||
body?: unknown;
|
||||
}
|
||||
|
||||
/**
|
||||
* API Router - routes requests to handlers
|
||||
*/
|
||||
@@ -119,11 +124,16 @@ export class ApiRouter {
|
||||
}
|
||||
|
||||
// Parse body
|
||||
const body = await this.parseRequestBody(req);
|
||||
if (!body) {
|
||||
const parsedBody = await this.parseRequestBody(req);
|
||||
if (parsedBody.kind === 'too_large') {
|
||||
this.sendError(res, 413, 'Request body too large', 'invalid_request_error');
|
||||
return;
|
||||
}
|
||||
if (parsedBody.kind !== 'ok') {
|
||||
this.sendError(res, 400, 'Invalid JSON body', 'invalid_request_error');
|
||||
return;
|
||||
}
|
||||
const body = parsedBody.body;
|
||||
|
||||
// Validate request
|
||||
const validation = this.sanityMiddleware.validateChatRequest(body);
|
||||
@@ -155,11 +165,16 @@ export class ApiRouter {
|
||||
}
|
||||
|
||||
// Parse body
|
||||
const body = await this.parseRequestBody(req);
|
||||
if (!body) {
|
||||
const parsedBody = await this.parseRequestBody(req);
|
||||
if (parsedBody.kind === 'too_large') {
|
||||
this.sendError(res, 413, 'Request body too large', 'invalid_request_error');
|
||||
return;
|
||||
}
|
||||
if (parsedBody.kind !== 'ok') {
|
||||
this.sendError(res, 400, 'Invalid JSON body', 'invalid_request_error');
|
||||
return;
|
||||
}
|
||||
const body = parsedBody.body;
|
||||
|
||||
// Convert to chat format and handle
|
||||
const chatBody = this.convertCompletionToChat(body as Record<string, unknown>);
|
||||
@@ -229,11 +244,16 @@ export class ApiRouter {
|
||||
}
|
||||
|
||||
// Parse body
|
||||
const body = await this.parseRequestBody(req);
|
||||
if (!body) {
|
||||
const parsedBody = await this.parseRequestBody(req);
|
||||
if (parsedBody.kind === 'too_large') {
|
||||
this.sendError(res, 413, 'Request body too large', 'invalid_request_error');
|
||||
return;
|
||||
}
|
||||
if (parsedBody.kind !== 'ok') {
|
||||
this.sendError(res, 400, 'Invalid JSON body', 'invalid_request_error');
|
||||
return;
|
||||
}
|
||||
const body = parsedBody.body;
|
||||
|
||||
const validation = this.sanityMiddleware.validateEmbeddingsRequest(body);
|
||||
if (!validation.valid) {
|
||||
@@ -250,28 +270,45 @@ export class ApiRouter {
|
||||
/**
|
||||
* Parse request body
|
||||
*/
|
||||
private async parseRequestBody(req: http.IncomingMessage): Promise<unknown | null> {
|
||||
private async parseRequestBody(req: http.IncomingMessage): Promise<IParsedRequestBody> {
|
||||
return new Promise((resolve) => {
|
||||
let body = '';
|
||||
let resolved = false;
|
||||
|
||||
const finish = (result: IParsedRequestBody): void => {
|
||||
if (resolved) {
|
||||
return;
|
||||
}
|
||||
resolved = true;
|
||||
resolve(result);
|
||||
};
|
||||
|
||||
req.on('data', (chunk) => {
|
||||
if (resolved) {
|
||||
return;
|
||||
}
|
||||
|
||||
body += chunk.toString();
|
||||
// Limit body size
|
||||
|
||||
if (body.length > 10 * 1024 * 1024) {
|
||||
resolve(null);
|
||||
req.pause();
|
||||
req.destroy();
|
||||
finish({ kind: 'too_large' });
|
||||
}
|
||||
});
|
||||
|
||||
req.on('end', () => {
|
||||
try {
|
||||
resolve(JSON.parse(body));
|
||||
finish({ kind: 'ok', body: JSON.parse(body) });
|
||||
} catch {
|
||||
resolve(null);
|
||||
finish({ kind: 'invalid' });
|
||||
}
|
||||
});
|
||||
|
||||
req.on('error', () => {
|
||||
resolve(null);
|
||||
if (!resolved) {
|
||||
finish({ kind: 'invalid' });
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user