480342232a
On macOS (256 soft fd limit), routing the weixin/email pollers through a local HTTP proxy leaked one TCP socket per failed poll/connect cycle until the gateway hit `[Errno 24] Too many open files` and crashed (launchd respawn loop). Live capture showed 216 of 256 fds pinned on connections to the proxy, ~214 of them abandoned. Code-side gaps fixed: - email adapter, `connect()`: no try/finally around the IMAP test connection — a failure in login/ID/select/search abandoned the connected socket with no owner. Every reconnect-watcher retry builds a fresh adapter, so each retry against an unreachable/proxied host leaked another fd. Teardown now runs in `finally`. - email adapter, IMAP teardown: `imaplib.IMAP4.logout()` only swallows `OSError` internally; on a broken connection `LOGOUT` raises `IMAP4.abort` before the internal `shutdown()`, leaving the socket open. New `_close_imap()` helper chases a failed `logout()` with an unconditional `shutdown()`; used in `connect()` and `_fetch_new_messages()`. - weixin adapter: repeated poll failures through a proxy strand sockets in the aiohttp connector where the tight keepalive reaper never sees them. The poll loop now recycles its ClientSession (swap-then-close, safe for concurrent `_process_message` tasks) after each MAX_CONSECUTIVE_FAILURES streak, tearing down the connector and every socket it holds. Targeted tests: tests/gateway/test_poller_fd_lifecycle.py (9 tests). Reported by @EthanHunter1229 with measured fd captures.