feat: refine operations UI and transport limits
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
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);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user