Repository navigation
fix(smtp): add connect and socket timeouts and write full payloads - #2
Merged
Merged
Conversation
neonbyte1
force-pushed
the
fix/smtp-timeouts
branch
from
October 7, 2026 00:29
f7f3ad1 to
1af835d
Compare
Owner
|
Thanks for the thorough investigation in #1 and the clean fix here. The changes look solid:
LGTM. Merging this now. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #1.
Changes
connectionTimeoutMs(default30000): TCP connect and each TLS handshake (secure: trueor afterSTARTTLS).socketTimeoutMs(default60000): each wait for a server reply and each socket write. It is an inactivity limit, so a large upload that keeps making progress is not cut off.1..2147483647(thesetTimeoutrange).send()rejects with... timed out after N ms. A pooled client that timed out reconnects on next use instead of reusing a dead socket.SmtpCommandloops over partialwrite()results, each write bounded bysocketTimeoutMs. This fixes DATA truncation above one write (64 KiB over TLS).RCPT TOand the AUTH LOGIN → PLAIN fallback rethrowSmtpTimeoutErrorinstead of treating it as a rejection.Deno.connect(abortable viasignal) followed byDeno.startTls+handshake(), so connect and handshake are bounded separately.SmtpAuthreusesSmtpCommandwire I/O (removes its duplicate reader/writer);SmtpClientkeeps transport state only inconnectionState.src/smtp/Timeout.ts(withTimeout,SmtpTimeoutError, defaults), exported through the@smtpbarrel.[Unreleased].Tests
tests/helpers/SmtpServer.tsis a scripted loopback SMTP server that records whether the client hung up. New tests, all of which fail or hang on 0.3.0:socketTimeoutMsConnection closedafter the server gave upconnectionTimeoutMsunexpected end of fileafter the server gave upAll recipients were rejected by SMTP server0,1,2147483647,2147483648)Deno's default op/resource sanitizers stay on, so the tests also catch leaked sockets and timers.
Verification
deno task check(fmt, lint, check) and the full suite: 112/112 on Deno 2.5.4 (the CI version, type-checked) and 2.9.7; new transport tests stable over repeated runs.smtp.ethereal.email:587(STARTTLS + AUTH): integration suite passes; a 1.9 MiB message sent twice over one pooled connection.connectionTimeoutMs: 1500: rejected after 1507 ms.Known limitation
When a server stalls during the TLS handshake,
send()rejects on time, but Deno keeps that TCP socket open until the peer answers or disconnects (details in #1). It is documented in USAGE. Timeouts after the handshake close the socket immediately.Not included
src/index.ts:157still failsdeno checkon Deno 2.9+ (setTimeouttyping). That predates this PR and is left out to keep it focused.