Skip to content

Commit 009d37f

Browse files
committed
feat: add Worker and Worker Pool (unstable)
1 parent caf0cc1 commit 009d37f

File tree

4 files changed

+153
-0
lines changed

4 files changed

+153
-0
lines changed

README.md

+5
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,11 @@ const client = new SmtpClient({
156156
});
157157
```
158158

159+
## Pool, Worker
160+
> This is unstable API may change! This requires deno to run in unstable mode.
161+
162+
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.
163+
159164
## TLS issues
160165
When getting TLS errors make shure:
161166
1. you use the correct port (mostly 25, 587, 465)

mod.ts

+1
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ export type {
55
} from "./config.ts";
66
export { SmtpClient } from "./smtp.ts";
77
export { quotedPrintableEncode } from "./encoding.ts";
8+
export { SMTPWorker, SMTPWorkerPool } from './pool.ts'

pool.ts

+107
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import { ConnectConfigWithAuthentication, SendConfig } from "./config.ts";
2+
3+
export class SMTPWorker {
4+
#timeout: number;
5+
6+
constructor(
7+
config: ConnectConfigWithAuthentication,
8+
{ timeout = 60000, autoconnect = true } = {},
9+
) {
10+
this.#config = config;
11+
this.#timeout = timeout;
12+
if(autoconnect) {
13+
this.#startup();
14+
}
15+
}
16+
#w!: Worker;
17+
#idleTO: number | null = null;
18+
#idleMode2 = false;
19+
#noCon = true;
20+
#config: ConnectConfigWithAuthentication;
21+
22+
#startup() {
23+
this.#w = new Worker(new URL("./worker.ts", import.meta.url), {
24+
type: "module",
25+
deno: {
26+
permissions: {
27+
net: "inherit",
28+
// ts files
29+
read: true,
30+
},
31+
namespace: true,
32+
},
33+
});
34+
35+
this.#w.addEventListener("message", (ev: MessageEvent<boolean>) => {
36+
if (ev.data) {
37+
this.#stopIdle();
38+
} else {
39+
if (this.#idleMode2) {
40+
this.#cleanup();
41+
} else {
42+
this.#startIdle();
43+
}
44+
}
45+
});
46+
47+
this.#w.postMessage({
48+
__setup: this.#config,
49+
});
50+
51+
this.#noCon = false;
52+
}
53+
54+
#startIdle() {
55+
console.log("started idle");
56+
if (this.#idleTO) return;
57+
58+
this.#idleTO = setTimeout(() => {
59+
console.log("idle mod 2");
60+
this.#idleMode2 = true;
61+
this.#w.postMessage({ __check_idle: true });
62+
}, this.#timeout);
63+
}
64+
65+
#stopIdle() {
66+
if (this.#idleTO) clearTimeout(this.#idleTO);
67+
68+
this.#idleMode2 = false;
69+
this.#idleTO = null;
70+
}
71+
72+
#cleanup() {
73+
console.log("killed");
74+
this.#w.terminate();
75+
this.#stopIdle();
76+
}
77+
78+
public send(mail: SendConfig) {
79+
this.#stopIdle();
80+
if (this.#noCon) {
81+
this.#startup();
82+
}
83+
this.#w.postMessage(mail);
84+
}
85+
}
86+
87+
export class SMTPWorkerPool {
88+
pool: SMTPWorker[] = []
89+
90+
constructor(
91+
config: ConnectConfigWithAuthentication,
92+
{ timeout = 60000, size = 2 } = {}
93+
) {
94+
for (let i = 0; i < size; i++) {
95+
this.pool.push(new SMTPWorker(config, {timeout, autoconnect: i === 0}))
96+
}
97+
}
98+
99+
#lastUsed = -1
100+
101+
send(mail: SendConfig) {
102+
this.#lastUsed = (this.#lastUsed + 1) % this.pool.length
103+
104+
this.pool[this.#lastUsed].send(mail)
105+
}
106+
}
107+

worker.ts

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/// <reference no-default-lib="true" />
2+
/// <reference lib="deno.worker" />
3+
/// <reference lib="deno.unstable" />
4+
5+
import { SmtpClient } from "./smtp.ts";
6+
import { SendConfig } from "./config.ts";
7+
8+
const client = new SmtpClient({ console_debug: true });
9+
10+
let cb: () => void;
11+
const readyPromise = new Promise<void>((res) => {
12+
cb = res;
13+
});
14+
15+
let hasIdlePromise = false;
16+
17+
async function send(config: SendConfig) {
18+
client.send(config);
19+
20+
if (!hasIdlePromise) {
21+
hasIdlePromise = true;
22+
await client.idle;
23+
postMessage(false);
24+
hasIdlePromise = false;
25+
}
26+
}
27+
28+
addEventListener("message", async (ev: MessageEvent) => {
29+
if (ev.data.__setup) {
30+
await client.connectTLS(ev.data.__setup);
31+
cb();
32+
return;
33+
}
34+
if (ev.data.__check_idle) {
35+
postMessage(client.isSending);
36+
return;
37+
}
38+
await readyPromise;
39+
send(ev.data);
40+
});

0 commit comments

Comments
 (0)