Stop an attachment holding an IMAP connection open forever

FixEmailService
Shipped
September 3, 2026 at 12:03 AM UTC
Author
Kamo
Commit
f20c467

getAttachment returned Jakarta Mail's lazy IMAPInputStream straight out of withStore, which puts the Store back in the pool the instant the lambda returns. Two things followed from that. The response body was pulled over a connection another request was free to borrow and SELECT elsewhere, and the folder had to be left open to keep even that possible — one whole IMAP connection per attachment ever opened, against Dovecot's budget of ten per user and IP, with a comment saying the caller would close it and no caller that ever did. Every other method on this service closes its folder in a finally; these did not. The part is now drained while the folder is open, so the folder closes on the way out and what the controller hands to Spring owes nothing to the connection. Parts up to 4 MB stay in the heap and larger ones spool to a temp file that is unlinked as soon as it is opened, because a mailbox accepts attachments far bigger than the pod's heap and a download must not be able to OOM the service. Also decodes the sender's filename. It arrives as an RFC 2047 encoded-word whenever it is not plain ASCII, and Jakarta Mail only decodes one if a JVM-wide system property says so, which defaults to off — so an accent, an emoji, or the narrow no-break space macOS puts before "PM" was enough for the attachment list to show =?UTF-8?B?U2NyZWVuc2hvdCAy...?= and a download to save a file named that. Decoded here rather than by the system property, so an unknown charset falls back to the literal instead of throwing. Drops getInlinePart and its helpers unread: they carried the same leak and had no caller, the controllers resolving a cid through the attachment list instead.

All changes

Like what you see shipping?

Every one of these updates lands in your workspace automatically. Start free and watch it grow week after week.

Start Free ForeverView Pricing