From b2d4bc3db862f7fb87803e42eecfbdf7edd2e9d6 Mon Sep 17 00:00:00 2001 From: VM Date: Sun, 26 May 2024 22:48:17 -0700 Subject: [PATCH] fix: close IMAP connection gracefully after use --- nodes/Imap/Imap.node.ts | 129 ++++++++++++++++++++++------------------ 1 file changed, 72 insertions(+), 57 deletions(-) diff --git a/nodes/Imap/Imap.node.ts b/nodes/Imap/Imap.node.ts index d2d3bd3..15f7502 100644 --- a/nodes/Imap/Imap.node.ts +++ b/nodes/Imap/Imap.node.ts @@ -125,72 +125,87 @@ export class Imap implements INodeType { }); } - // get node parameters - const FIRST_ITEM_INDEX = 0; // resource and operation are the same for all items - const resource = this.getNodeParameter('resource', FIRST_ITEM_INDEX) as string; - const operation = this.getNodeParameter('operation', FIRST_ITEM_INDEX) as string; - - var resultBranches: INodeExecutionData[][] = []; - var resultItems: INodeExecutionData[] = []; - resultBranches.push(resultItems); - - // run corresponding operation - const handler = allResourceDefinitions.find((resourceDef) => resourceDef.resource.value === resource)?.operationDefs.find((operationDef) => operationDef.operation.value === operation); - if (handler) { - // running operation in a loop for each input item - const items = this.getInputData(); - - for (let itemIndex = 0; itemIndex < items.length; itemIndex++) { - try { - // some errors are not thrown but logged by ImapFlow internally, so we try to catch them - ImapFlowErrorCatcher.getInstance().startErrorCatching(); - - const result = await handler.executeImapAction(this, itemIndex, client); - if (result) { - resultItems.push(...result); - } else { - this.logger.warn(`Operation "${operation}" for resource "${resource}" returned no data`); - } - } catch (error) { - const internalImapErrors = ImapFlowErrorCatcher.getInstance().stopAndGetErrors(); - const internalImapErrorsMessage = internalImapErrors.join(", \n"); + // try/catch to close connection in any case + try { - if (internalImapErrors.length > 0) { - this.logger.error(`IMAP server reported errors: ${internalImapErrorsMessage}`); - } - if (error instanceof NodeApiError) { - // don't include internal IMAP errors, because the error message is already composed by the handler - throw error; - } + // get node parameters + const FIRST_ITEM_INDEX = 0; // resource and operation are the same for all items + const resource = this.getNodeParameter('resource', FIRST_ITEM_INDEX) as string; + const operation = this.getNodeParameter('operation', FIRST_ITEM_INDEX) as string; - // seems to be unknown error, check IMAP internal errors and include them in the error message + var resultBranches: INodeExecutionData[][] = []; + var resultItems: INodeExecutionData[] = []; + resultBranches.push(resultItems); - var errorMessage = error.responseText || error.message || undefined; - if (!errorMessage) { - if (internalImapErrorsMessage) { - errorMessage = internalImapErrorsMessage; + // run corresponding operation + const handler = allResourceDefinitions.find((resourceDef) => resourceDef.resource.value === resource)?.operationDefs.find((operationDef) => operationDef.operation.value === operation); + if (handler) { + // running operation in a loop for each input item + const items = this.getInputData(); + + for (let itemIndex = 0; itemIndex < items.length; itemIndex++) { + try { + // some errors are not thrown but logged by ImapFlow internally, so we try to catch them + ImapFlowErrorCatcher.getInstance().startErrorCatching(); + + const result = await handler.executeImapAction(this, itemIndex, client); + if (result) { + resultItems.push(...result); } else { - errorMessage = 'Unknown error'; + this.logger.warn(`Operation "${operation}" for resource "${resource}" returned no data`); } + } catch (error) { + const internalImapErrors = ImapFlowErrorCatcher.getInstance().stopAndGetErrors(); + const internalImapErrorsMessage = internalImapErrors.join(", \n"); + + if (internalImapErrors.length > 0) { + this.logger.error(`IMAP server reported errors: ${internalImapErrorsMessage}`); + } + + if (error instanceof NodeApiError) { + // don't include internal IMAP errors, because the error message is already composed by the handler + throw error; + } + + // seems to be unknown error, check IMAP internal errors and include them in the error message + + var errorMessage = error.responseText || error.message || undefined; + if (!errorMessage) { + if (internalImapErrorsMessage) { + errorMessage = internalImapErrorsMessage; + } else { + errorMessage = 'Unknown error'; + } + } + this.logger.error(`Operation "${operation}" for resource "${resource}" failed: ${errorMessage}`); + this.logger.error(JSON.stringify(error)); + var errorDetails : any = { + message: errorMessage, + }; + if (internalImapErrorsMessage) { + errorDetails.description = "The following errors were reported by the IMAP server: \n" + internalImapErrorsMessage; + } + throw new NodeApiError(this.getNode(), {}, errorDetails); } - this.logger.error(`Operation "${operation}" for resource "${resource}" failed: ${errorMessage}`); - this.logger.error(JSON.stringify(error)); - var errorDetails : any = { - message: errorMessage, - }; - if (internalImapErrorsMessage) { - errorDetails.description = "The following errors were reported by the IMAP server: \n" + internalImapErrorsMessage; - } - throw new NodeApiError(this.getNode(), {}, errorDetails); } + + } else { + this.logger.error(`Unknown operation "${operation}" for resource "${resource}"`); + throw new NodeApiError(this.getNode(), {}, { + message: `Unknown operation "${operation}" for resource "${resource}"`, + }); } - } else { - this.logger.error(`Unknown operation "${operation}" for resource "${resource}"`); - throw new NodeApiError(this.getNode(), {}, { - message: `Unknown operation "${operation}" for resource "${resource}"`, - }); + // close connection + client.logout(); + this.logger?.info('IMAP connection closed'); + + } catch (error) { + // close connection and rethrow error + client.logout(); + this.logger?.error(`IMAP connection closed. Error: ${error.message}`); + throw error; } return resultBranches; @@ -208,7 +223,7 @@ export class Imap implements INodeType { try { const client = createImapClient(credentials); await client.connect(); - client.close(); + client.logout(); } catch (error) { return { status: 'Error',