diff --git a/package-lock.json b/package-lock.json index 8759a701..b4606f00 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,6 @@ "express": "^5.0.1", "express-rate-limit": "^7.5.0", "pkce-challenge": "^5.0.0", - "raw-body": "^3.0.0", "zod": "^3.23.8", "zod-to-json-schema": "^3.24.1" }, diff --git a/package.json b/package.json index 8be8f100..30205b1c 100644 --- a/package.json +++ b/package.json @@ -71,7 +71,6 @@ "express": "^5.0.1", "express-rate-limit": "^7.5.0", "pkce-challenge": "^5.0.0", - "raw-body": "^3.0.0", "zod": "^3.23.8", "zod-to-json-schema": "^3.24.1" }, @@ -100,4 +99,4 @@ "resolutions": { "strip-ansi": "6.0.1" } -} \ No newline at end of file +} diff --git a/src/server/sse.ts b/src/server/sse.ts index e0725686..aa4e9cfd 100644 --- a/src/server/sse.ts +++ b/src/server/sse.ts @@ -2,12 +2,11 @@ import { randomUUID } from "node:crypto"; import { IncomingMessage, ServerResponse } from "node:http"; import { Transport } from "../shared/transport.js"; import { JSONRPCMessage, JSONRPCMessageSchema, MessageExtraInfo, RequestInfo } from "../types.js"; -import getRawBody from "raw-body"; import contentType from "content-type"; import { AuthInfo } from "./auth/types.js"; import { URL } from 'url'; -const MAXIMUM_MESSAGE_SIZE = "4mb"; +const MAXIMUM_MESSAGE_SIZE = 4 * 1024 * 1024; // 4MB /** * Configuration options for SSEServerTransport. @@ -161,7 +160,7 @@ export class SSEServerTransport implements Transport { body = parsedBody ?? await getRawBody(req, { limit: MAXIMUM_MESSAGE_SIZE, - encoding: ct.parameters.charset ?? "utf-8", + encoding: (ct.parameters.charset as BufferEncoding) ?? "utf-8", }); } catch (error) { res.writeHead(400).end(String(error)); @@ -219,3 +218,27 @@ export class SSEServerTransport implements Transport { return this._sessionId; } } + +export function getRawBody(req: IncomingMessage, { limit, encoding }: { limit: number, encoding: BufferEncoding }) { + return new Promise((resolve, reject) => { + let received = 0; + + const chunks: Buffer[] = []; + req.on("data", (chunk: Buffer) => { + received += chunk.length; + if (received > limit) + return reject(new Error(`Message size exceeds limit of ${limit} bytes`)); + chunks.push(chunk); + }); + req.on('end', () => { + try { + resolve(Buffer.concat(chunks).toString(encoding)); + } catch (error) { + reject(error); + } + }); + req.on('error', (error) => { + reject(error); + }); + }); +} \ No newline at end of file diff --git a/src/server/streamableHttp.ts b/src/server/streamableHttp.ts index 3bf84e43..aef475df 100644 --- a/src/server/streamableHttp.ts +++ b/src/server/streamableHttp.ts @@ -1,12 +1,12 @@ import { IncomingMessage, ServerResponse } from "node:http"; import { Transport } from "../shared/transport.js"; import { MessageExtraInfo, RequestInfo, isInitializeRequest, isJSONRPCError, isJSONRPCRequest, isJSONRPCResponse, JSONRPCMessage, JSONRPCMessageSchema, RequestId, SUPPORTED_PROTOCOL_VERSIONS, DEFAULT_NEGOTIATED_PROTOCOL_VERSION } from "../types.js"; -import getRawBody from "raw-body"; import contentType from "content-type"; import { randomUUID } from "node:crypto"; import { AuthInfo } from "./auth/types.js"; +import { getRawBody } from "./sse.js"; -const MAXIMUM_MESSAGE_SIZE = "4mb"; +const MAXIMUM_MESSAGE_SIZE = 4 * 1024 * 1024; // 4MB export type StreamId = string; export type EventId = string; @@ -412,9 +412,9 @@ export class StreamableHTTPServerTransport implements Transport { const parsedCt = contentType.parse(ct); const body = await getRawBody(req, { limit: MAXIMUM_MESSAGE_SIZE, - encoding: parsedCt.parameters.charset ?? "utf-8", + encoding: (parsedCt.parameters.charset as BufferEncoding) ?? "utf-8", }); - rawMessage = JSON.parse(body.toString()); + rawMessage = JSON.parse(body); } let messages: JSONRPCMessage[];