From 8ae2d801d8e2c881fcff5f7cf4fcb699c10b2be2 Mon Sep 17 00:00:00 2001 From: agobrech <45268029+agobrech@users.noreply.github.com> Date: Wed, 17 May 2023 10:06:24 +0200 Subject: [PATCH] feat(Respond to Webhook Node): Move from Binary Buffer to Binary streaming (#5613) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * replace binary buffer with binary streaming * Add binary assertion and remove duplicate code * handle streams correctly * fix binary response in `own` mode * fix stream response missing headers --------- Co-authored-by: कारतोफ्फेलस्क्रिप्ट™ Co-authored-by: कारतोफ्फेलस्क्रिप्ट™ Co-authored-by: Marcus --- packages/cli/src/WebhookHelpers.ts | 19 ++++++++++++++----- .../RespondToWebhook/RespondToWebhook.node.ts | 16 +++++++++------- packages/workflow/src/Interfaces.ts | 2 +- 3 files changed, 24 insertions(+), 13 deletions(-) diff --git a/packages/cli/src/WebhookHelpers.ts b/packages/cli/src/WebhookHelpers.ts index e399f10beb..4ac4dbd1fa 100644 --- a/packages/cli/src/WebhookHelpers.ts +++ b/packages/cli/src/WebhookHelpers.ts @@ -15,10 +15,13 @@ /* eslint-disable prefer-destructuring */ import type express from 'express'; import get from 'lodash.get'; +import stream from 'stream'; +import { promisify } from 'util'; import { BinaryDataManager, NodeExecuteFunctions, eventEmitter } from 'n8n-core'; import type { + IBinaryData, IBinaryKeyData, IDataObject, IDeferredPromise, @@ -59,6 +62,8 @@ import type { WorkflowEntity } from '@db/entities/WorkflowEntity'; import { getWorkflowOwner } from '@/UserManagement/UserManagementHelper'; import { Container } from 'typedi'; +const pipeline = promisify(stream.pipeline); + export const WEBHOOK_METHODS = ['DELETE', 'GET', 'HEAD', 'PATCH', 'POST', 'PUT']; /** @@ -418,13 +423,17 @@ export async function executeWebhook( return; } - if (Buffer.isBuffer(response.body)) { + const binaryData = (response.body as IDataObject)?.binaryData as IBinaryData; + if (binaryData?.id) { + res.header(response.headers); + const stream = NodeExecuteFunctions.getBinaryStream(binaryData.id); + void pipeline(stream, res).then(() => + responseCallback(null, { noWebhookResponse: true }), + ); + } else if (Buffer.isBuffer(response.body)) { res.header(response.headers); res.end(response.body); - - responseCallback(null, { - noWebhookResponse: true, - }); + responseCallback(null, { noWebhookResponse: true }); } else { // TODO: This probably needs some more changes depending on the options on the // Webhook Response node diff --git a/packages/nodes-base/nodes/RespondToWebhook/RespondToWebhook.node.ts b/packages/nodes-base/nodes/RespondToWebhook/RespondToWebhook.node.ts index 3296d404c8..699dd40a52 100644 --- a/packages/nodes-base/nodes/RespondToWebhook/RespondToWebhook.node.ts +++ b/packages/nodes-base/nodes/RespondToWebhook/RespondToWebhook.node.ts @@ -1,3 +1,4 @@ +import type { Readable } from 'stream'; import type { IDataObject, IExecuteFunctions, @@ -7,7 +8,7 @@ import type { INodeType, INodeTypeDescription, } from 'n8n-workflow'; -import { jsonParse, NodeOperationError } from 'n8n-workflow'; +import { jsonParse, BINARY_ENCODING, NodeOperationError } from 'n8n-workflow'; export class RespondToWebhook implements INodeType { description: INodeTypeDescription = { @@ -201,7 +202,7 @@ export class RespondToWebhook implements INodeType { } } - let responseBody: IN8nHttpResponse; + let responseBody: IN8nHttpResponse | Readable; if (respondWith === 'json') { const responseBodyParameter = this.getNodeParameter('responseBody', 0) as string; if (responseBodyParameter) { @@ -235,15 +236,16 @@ export class RespondToWebhook implements INodeType { } const binaryData = this.helpers.assertBinaryData(0, responseBinaryPropertyName); - const binaryDataBuffer = await this.helpers.getBinaryDataBuffer( - 0, - responseBinaryPropertyName, - ); + if (binaryData.id) { + responseBody = { binaryData }; + } else { + responseBody = Buffer.from(binaryData.data, BINARY_ENCODING); + headers['content-length'] = (responseBody as Buffer).length; + } if (!headers['content-type']) { headers['content-type'] = binaryData.mimeType; } - responseBody = binaryDataBuffer; } else if (respondWith !== 'noData') { throw new NodeOperationError( this.getNode(), diff --git a/packages/workflow/src/Interfaces.ts b/packages/workflow/src/Interfaces.ts index b343a9b267..60a3555546 100644 --- a/packages/workflow/src/Interfaces.ts +++ b/packages/workflow/src/Interfaces.ts @@ -527,7 +527,7 @@ export interface IHttpRequestOptions { export type IN8nHttpResponse = IDataObject | Buffer | GenericValue | GenericValue[] | null; export interface IN8nHttpFullResponse { - body: IN8nHttpResponse; + body: IN8nHttpResponse | Readable; headers: IDataObject; statusCode: number; statusMessage?: string;