fix(server): accept empty requests with unsupported media types

Inject an empty JSON object for requests that have an unsupported content type but zero content length. Fastify defaults to rejecting these with a 415 error.

Closes #348
This commit is contained in:
neon
2026-06-17 05:07:44 +00:00
parent 0cddaf050c
commit 7b48d49351
3 changed files with 179 additions and 16 deletions
@@ -1,4 +1,11 @@
import { UnsupportedMediaTypeException } from '@nestjs/common';
import type { FastifyInstance } from 'fastify';
import type { IncomingHttpHeaders } from 'http';
import { Readable } from 'stream';
const DEFAULT_TRUST_PROXY = 'loopback,linklocal,uniquelocal';
const EMPTY_JSON_BODY = '{}';
const BODY_METHODS = new Set(['DELETE', 'PATCH', 'POST', 'PUT']);
export function parseTrustProxy(value: string | undefined): string | boolean | number {
const raw = value?.trim();
@@ -52,3 +59,33 @@ export function buildCspDirectives(options: CspOptions = {}) {
upgradeInsecureRequests: null,
};
}
export function shouldInjectEmptyJsonBody(method: string, headers: IncomingHttpHeaders): boolean {
const contentType = getHeaderValue(headers['content-type'])?.toLowerCase();
if (!BODY_METHODS.has(method.toUpperCase()) || !contentType?.startsWith('application/json')) {
return false;
}
const contentLength = getHeaderValue(headers['content-length'])?.trim();
return contentLength === undefined || contentLength === '0';
}
export function buildEmptyJsonBodyStream(headers: IncomingHttpHeaders): Readable {
headers['content-length'] = String(Buffer.byteLength(EMPTY_JSON_BODY));
return Readable.from([EMPTY_JSON_BODY]);
}
export function registerEmptyBodyContentTypeParser(fastify: FastifyInstance): void {
fastify.addContentTypeParser<string>('*', { parseAs: 'string' }, (request, body, done) => {
if (BODY_METHODS.has(request.method.toUpperCase()) && body.length === 0) {
done(null, {});
return;
}
done(new UnsupportedMediaTypeException('Unsupported Media Type'));
});
}
function getHeaderValue(value: string | string[] | undefined): string | undefined {
return Array.isArray(value) ? value[0] : value;
}