From 009d37f496cb3963f169b7454644cf04bee01d90 Mon Sep 17 00:00:00 2001 From: mathe42 <2pi_r2@gmx.de> Date: Thu, 21 Apr 2022 19:16:01 +0200 Subject: [PATCH] feat: add Worker and Worker Pool (unstable) --- README.md | 5 +++ mod.ts | 1 + pool.ts | 107 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ worker.ts | 40 ++++++++++++++++++++ 4 files changed, 153 insertions(+) create mode 100644 pool.ts create mode 100644 worker.ts diff --git a/README.md b/README.md index 529800c..61d168a 100644 --- a/README.md +++ b/README.md @@ -156,6 +156,11 @@ const client = new SmtpClient({ }); ``` +## Pool, Worker +> This is unstable API may change! This requires deno to run in unstable mode. + +Adds 2 new classes `SMTPWorker` and `SMTPWorkerPool` (for constructor options see code for now). This creates a SMTP client (or multiple) that get automaticly killed if the connection is not used for around 60s. + ## TLS issues When getting TLS errors make shure: 1. you use the correct port (mostly 25, 587, 465) diff --git a/mod.ts b/mod.ts index 34671a9..4e6d8c9 100644 --- a/mod.ts +++ b/mod.ts @@ -5,3 +5,4 @@ export type { } from "./config.ts"; export { SmtpClient } from "./smtp.ts"; export { quotedPrintableEncode } from "./encoding.ts"; +export { SMTPWorker, SMTPWorkerPool } from './pool.ts' \ No newline at end of file diff --git a/pool.ts b/pool.ts new file mode 100644 index 0000000..d281167 --- /dev/null +++ b/pool.ts @@ -0,0 +1,107 @@ +import { ConnectConfigWithAuthentication, SendConfig } from "./config.ts"; + +export class SMTPWorker { + #timeout: number; + + constructor( + config: ConnectConfigWithAuthentication, + { timeout = 60000, autoconnect = true } = {}, + ) { + this.#config = config; + this.#timeout = timeout; + if(autoconnect) { + this.#startup(); + } + } + #w!: Worker; + #idleTO: number | null = null; + #idleMode2 = false; + #noCon = true; + #config: ConnectConfigWithAuthentication; + + #startup() { + this.#w = new Worker(new URL("./worker.ts", import.meta.url), { + type: "module", + deno: { + permissions: { + net: "inherit", + // ts files + read: true, + }, + namespace: true, + }, + }); + + this.#w.addEventListener("message", (ev: MessageEvent) => { + if (ev.data) { + this.#stopIdle(); + } else { + if (this.#idleMode2) { + this.#cleanup(); + } else { + this.#startIdle(); + } + } + }); + + this.#w.postMessage({ + __setup: this.#config, + }); + + this.#noCon = false; + } + + #startIdle() { + console.log("started idle"); + if (this.#idleTO) return; + + this.#idleTO = setTimeout(() => { + console.log("idle mod 2"); + this.#idleMode2 = true; + this.#w.postMessage({ __check_idle: true }); + }, this.#timeout); + } + + #stopIdle() { + if (this.#idleTO) clearTimeout(this.#idleTO); + + this.#idleMode2 = false; + this.#idleTO = null; + } + + #cleanup() { + console.log("killed"); + this.#w.terminate(); + this.#stopIdle(); + } + + public send(mail: SendConfig) { + this.#stopIdle(); + if (this.#noCon) { + this.#startup(); + } + this.#w.postMessage(mail); + } +} + +export class SMTPWorkerPool { + pool: SMTPWorker[] = [] + + constructor( + config: ConnectConfigWithAuthentication, + { timeout = 60000, size = 2 } = {} + ) { + for (let i = 0; i < size; i++) { + this.pool.push(new SMTPWorker(config, {timeout, autoconnect: i === 0})) + } + } + + #lastUsed = -1 + + send(mail: SendConfig) { + this.#lastUsed = (this.#lastUsed + 1) % this.pool.length + + this.pool[this.#lastUsed].send(mail) + } +} + diff --git a/worker.ts b/worker.ts new file mode 100644 index 0000000..00abb78 --- /dev/null +++ b/worker.ts @@ -0,0 +1,40 @@ +/// +/// +/// + +import { SmtpClient } from "./smtp.ts"; +import { SendConfig } from "./config.ts"; + +const client = new SmtpClient({ console_debug: true }); + +let cb: () => void; +const readyPromise = new Promise((res) => { + cb = res; +}); + +let hasIdlePromise = false; + +async function send(config: SendConfig) { + client.send(config); + + if (!hasIdlePromise) { + hasIdlePromise = true; + await client.idle; + postMessage(false); + hasIdlePromise = false; + } +} + +addEventListener("message", async (ev: MessageEvent) => { + if (ev.data.__setup) { + await client.connectTLS(ev.data.__setup); + cb(); + return; + } + if (ev.data.__check_idle) { + postMessage(client.isSending); + return; + } + await readyPromise; + send(ev.data); +});