Skip to main content
Glama
request-size-limit.test.ts5.59 kB
/** * Security test for request size limiting in HTTP stream server * * This test verifies that the streaming back-pressure implementation * correctly prevents memory exhaustion attacks from oversized requests. */ import { spawn, ChildProcess } from 'child_process'; import fetch from 'node-fetch'; describe('HTTP Stream Server Security - Request Size Limiting', () => { let serverProcess: ChildProcess; let serverPort: number; const SERVER_URL = () => `http://localhost:${serverPort}`; beforeAll(async () => { // Find an available port serverPort = 30000 + Math.floor(Math.random() * 1000); // Start the HTTP stream server with explicit environment serverProcess = spawn('node', ['-r', 'ts-node/register', 'src/mcp-httpstream-server.ts'], { env: { ...process.env, HTTP_STREAM_PORT: serverPort.toString(), // Correct environment variable name HOST: 'localhost', NODE_ENV: 'test', TS_NODE_PROJECT: './tsconfig.json', }, stdio: ['pipe', 'pipe', 'pipe'], cwd: process.cwd(), }); // Log server output for debugging serverProcess.stdout?.on('data', (data) => { console.log('Server stdout:', data.toString()); }); serverProcess.stderr?.on('data', (data) => { console.log('Server stderr:', data.toString()); }); // Wait for server to start by checking stderr for the startup message await new Promise<void>((resolve, reject) => { const timeout = setTimeout(() => { reject(new Error('Server startup timeout')); }, 15000); const onStderr = (data: Buffer) => { const message = data.toString(); if ( message.includes(`MCP HTTP stream server listening at http://localhost:${serverPort}`) ) { clearTimeout(timeout); serverProcess.stderr?.off('data', onStderr); resolve(); } }; serverProcess.stderr?.on('data', onStderr); serverProcess.on('error', (error) => { clearTimeout(timeout); reject(error); }); }); }, 20000); afterAll(async () => { if (serverProcess) { serverProcess.kill('SIGTERM'); // Wait for process to exit await new Promise<void>((resolve) => { serverProcess.on('exit', () => resolve()); setTimeout(() => { serverProcess.kill('SIGKILL'); resolve(); }, 5000); }); } }); it('should reject requests with oversized Content-Length header', async () => { // Create a large payload that exceeds the 10MB limit const oversizedPayload = { jsonrpc: '2.0', method: 'initialize', params: { protocolVersion: '2025-03-26', capabilities: {}, clientInfo: { name: 'security-test', version: '1.0.0' }, // Add a large data field to exceed the size limit largeData: 'x'.repeat(11 * 1024 * 1024), // 11MB of 'x' characters }, id: 1, }; const response = await fetch(SERVER_URL(), { method: 'POST', headers: { 'Content-Type': 'application/json', Accept: 'application/json, text/event-stream', }, body: JSON.stringify(oversizedPayload), }); console.log('Response status:', response.status); console.log('Response headers:', Object.fromEntries(response.headers.entries())); if (response.status !== 413) { const responseText = await response.text(); console.log('Unexpected response body:', responseText.substring(0, 500) + '...'); } expect(response.status).toBe(413); // Payload Too Large const responseData = await response.json(); expect(responseData).toMatchObject({ jsonrpc: '2.0', error: { code: -32000, message: 'Payload Too Large', }, }); }, 10000); it('should accept normal-sized requests', async () => { const response = await fetch(SERVER_URL(), { method: 'POST', headers: { 'Content-Type': 'application/json', Accept: 'application/json, text/event-stream', }, body: JSON.stringify({ jsonrpc: '2.0', method: 'initialize', params: { protocolVersion: '2025-03-26', capabilities: {}, clientInfo: { name: 'security-test', version: '1.0.0' }, }, id: 1, }), }); console.log('Normal request - Response status:', response.status); console.log( 'Normal request - Response headers:', Object.fromEntries(response.headers.entries()), ); if (response.status !== 200) { const responseText = await response.text(); console.log('Normal request - Unexpected response body:', responseText); } expect(response.status).toBe(200); expect(response.headers.get('mcp-session-id')).toBeTruthy(); }, 10000); it('should handle requests without Content-Length header gracefully', async () => { // Create a normal request without Content-Length header const body = JSON.stringify({ jsonrpc: '2.0', method: 'initialize', params: { protocolVersion: '2025-03-26', capabilities: {}, clientInfo: { name: 'security-test', version: '1.0.0' }, }, id: 1, }); const response = await fetch(SERVER_URL(), { method: 'POST', headers: { 'Content-Type': 'application/json', Accept: 'application/json, text/event-stream', }, body, }); expect(response.status).toBe(200); expect(response.headers.get('mcp-session-id')).toBeTruthy(); }, 10000); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Jakedismo/KuzuMem-MCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server