diff options
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/mail.py')
| -rw-r--r-- | packages/meshbay-hub/src/meshbay_hub/mail.py | 18 |
1 files changed, 18 insertions, 0 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/mail.py b/packages/meshbay-hub/src/meshbay_hub/mail.py index 4f776bf..8373204 100644 --- a/packages/meshbay-hub/src/meshbay_hub/mail.py +++ b/packages/meshbay-hub/src/meshbay_hub/mail.py @@ -5,6 +5,7 @@ Postfix listens on loopback only (inet_interfaces = loopback-only), so no authentication is needed. See docs/MAIL-SERVER.md for the full setup. """ +import asyncio import logging import smtplib from email.message import EmailMessage @@ -22,6 +23,7 @@ def configure(hub_id: str) -> None: def _send(msg: EmailMessage) -> bool: + """Blocking. Every caller in an async handler must use `send_off_loop`.""" try: with smtplib.SMTP("localhost", 25, timeout=10) as s: s.send_message(msg) @@ -31,6 +33,22 @@ def _send(msg: EmailMessage) -> bool: return False +async def send_off_loop(fn, *args, **kwargs) -> None: + """Run one of the `send_*` functions below in a worker thread. + + `smtplib` is synchronous and this one waits up to ten seconds. Called + directly from an async handler — which is what all four call sites did — + that ten seconds is not one request's, it is **the whole hub's**: no other + request is served, no node socket is read, no WebRTC offer is relayed, + for as long as the MTA takes to answer. An unreachable mail server made + the instance stop responding to everyone, and one of the three paths that + reaches it (`PATCH /v1/users/me`) had no rate limit at all. + + So the cost of a slow MTA is one request now, not the instance. + """ + await asyncio.to_thread(fn, *args, **kwargs) + + def send_verification_code(to: str, code: str, recovery_key: str | None = None) -> None: """ Registration verification e-mail. When `recovery_key` is given it is |