67 lines
3.7 KiB
TypeScript
67 lines
3.7 KiB
TypeScript
import { configureHttpBodyParsers, DEFAULT_JSON_BODY_LIMIT, IMPORT_JSON_BODY_LIMIT } from './http-body-limits';
|
|
|
|
const express = require('express') as () => {
|
|
use(...args: unknown[]): void;
|
|
post(path: string, handler: (request: { body?: unknown; rawBody?: Buffer }, response: { json(body: unknown): void }) => void): void;
|
|
listen(port: number, host: string, callback: () => void): { close(callback: (error?: Error) => void): void; address(): { port: number } | string | null };
|
|
};
|
|
const expressModule = require('express') as { json(options: { limit: string }): (...args: unknown[]) => unknown; urlencoded(options: { limit: string; extended: boolean }): (...args: unknown[]) => unknown };
|
|
const http = require('node:http') as typeof import('node:http');
|
|
|
|
describe('configureHttpBodyParsers', () => {
|
|
it('keeps ordinary JSON bounded while granting only import routes a larger limit', () => {
|
|
const use = jest.fn();
|
|
const useBodyParser = jest.fn();
|
|
|
|
configureHttpBodyParsers({ use, useBodyParser } as never);
|
|
|
|
expect(DEFAULT_JSON_BODY_LIMIT).toBe('2mb');
|
|
expect(IMPORT_JSON_BODY_LIMIT).toBe('25mb');
|
|
expect(use).toHaveBeenCalledTimes(1);
|
|
expect(use).toHaveBeenCalledWith('/api/client/send/imports', expect.any(Function));
|
|
expect(useBodyParser).toHaveBeenNthCalledWith(1, 'json', { limit: '2mb' });
|
|
expect(useBodyParser).toHaveBeenNthCalledWith(2, 'urlencoded', { limit: '2mb', extended: true });
|
|
});
|
|
|
|
it('accepts a 3 MiB import JSON body but rejects the same ordinary JSON body', async () => {
|
|
const serverApp = express();
|
|
configureHttpBodyParsers({
|
|
use: serverApp.use.bind(serverApp),
|
|
useBodyParser(type: 'json' | 'urlencoded', options: { limit: string; extended?: boolean }) {
|
|
serverApp.use(type === 'json'
|
|
? expressModule.json({ limit: options.limit })
|
|
: expressModule.urlencoded({ limit: options.limit, extended: options.extended ?? true }));
|
|
},
|
|
} as never);
|
|
serverApp.post('/api/client/send/imports/preview', (request, response) => response.json({ size: request.rawBody?.length ?? 0 }));
|
|
serverApp.post('/api/ordinary', (_request, response) => response.json({ accepted: true }));
|
|
|
|
const server = await new Promise<ReturnType<typeof serverApp.listen>>((resolve) => {
|
|
const listening = serverApp.listen(0, '127.0.0.1', () => resolve(listening));
|
|
});
|
|
try {
|
|
const address = server.address();
|
|
if (!address || typeof address === 'string') throw new Error('test server did not expose a TCP port');
|
|
const body = JSON.stringify({ content: 'x'.repeat(3 * 1024 * 1024) });
|
|
const importResponse = await postJSON(address.port, '/api/client/send/imports/preview', body);
|
|
expect(importResponse.status).toBe(200);
|
|
expect(JSON.parse(importResponse.body)).toEqual({ size: Buffer.byteLength(body) });
|
|
await expect(postJSON(address.port, '/api/ordinary', body)).resolves.toMatchObject({ status: 413 });
|
|
} finally {
|
|
await new Promise<void>((resolve, reject) => server.close((error) => error ? reject(error) : resolve()));
|
|
}
|
|
});
|
|
});
|
|
|
|
function postJSON(port: number, path: string, body: string) {
|
|
return new Promise<{ status: number; body: string }>((resolve, reject) => {
|
|
const request = http.request({ hostname: '127.0.0.1', port, path, method: 'POST', headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body) } }, (response) => {
|
|
const chunks: Buffer[] = [];
|
|
response.on('data', (chunk: Buffer) => chunks.push(chunk));
|
|
response.once('end', () => resolve({ status: response.statusCode ?? 0, body: Buffer.concat(chunks).toString('utf8') }));
|
|
});
|
|
request.once('error', reject);
|
|
request.end(body);
|
|
});
|
|
}
|