refactor(core): Parse Webhook request bodies on-demand (#6394)

Also,
1. Consistent CORS support ~on all three webhook types~ waiting webhooks never supported CORS. I'll fix that in another PR
2. [Fixes binary-data handling when request body is text, json, or xml](https://linear.app/n8n/issue/NODE-505/webhook-binary-data-handling-fails-for-textplain-files).
3. Reduced number of middleware that each request has to go through.
4. Removed the need to maintain webhook endpoints in the auth-exception list.
5. Skip all middlewares (apart from `compression`) on Webhook routes. 
6. move `multipart/form-data` support out of individual nodes
7. upgrade `formidable`
8. fix the filenames on binary-data in webhooks nodes
9. add unit tests and integration tests for webhook request handling, and increase test coverage
This commit is contained in:
कारतोफ्फेलस्क्रिप्ट™
2023-08-01 17:32:30 +02:00
committed by GitHub
parent 369a2e9796
commit 31d8f478ee
29 changed files with 905 additions and 604 deletions

View File

@@ -0,0 +1,61 @@
import { parse as parseContentDisposition } from 'content-disposition';
import { parse as parseContentType } from 'content-type';
import getRawBody from 'raw-body';
import { type RequestHandler } from 'express';
import { jsonParse } from 'n8n-workflow';
import config from '@/config';
const payloadSizeMax = config.getEnv('endpoints.payloadSizeMax');
export const rawBody: RequestHandler = async (req, res, next) => {
if ('content-type' in req.headers) {
const { type: contentType, parameters } = (() => {
try {
return parseContentType(req);
} catch {
return { type: undefined, parameters: undefined };
}
})();
req.contentType = contentType;
req.encoding = (parameters?.charset ?? 'utf-8').toLowerCase() as BufferEncoding;
const contentDispositionHeader = req.headers['content-disposition'];
if (contentDispositionHeader?.length) {
const {
type,
parameters: { filename },
} = parseContentDisposition(contentDispositionHeader);
req.contentDisposition = { type, filename };
}
}
req.readRawBody = async () => {
if (!req.rawBody) {
req.rawBody = await getRawBody(req, {
length: req.headers['content-length'],
limit: `${String(payloadSizeMax)}mb`,
});
req._body = true;
}
};
next();
};
export const jsonParser: RequestHandler = async (req, res, next) => {
await req.readRawBody();
if (Buffer.isBuffer(req.rawBody)) {
if (req.contentType === 'application/json') {
try {
req.body = jsonParse<unknown>(req.rawBody.toString(req.encoding));
} catch (error) {
res.status(400).send({ error: 'Failed to parse request body' });
return;
}
} else {
req.body = {};
}
}
next();
};

View File

@@ -1,2 +1,3 @@
export * from './auth';
export * from './bodyParser';
export * from './cors';