Repository navigation
Conversation
There was a problem hiding this comment.
Would just swapping those two statements also work? 🤔
There was a problem hiding this comment.
Yes, it does. Thanks, that's much simpler! Sleeping first and then waiting for can_timeout handles every case the new tests cover: a disarm during the sleep (nested or not), re-arming without a busy loop, and activity moving the deadline. That works because disarm() refreshes last_activity when it re-arms, so the deadline check right after wait() can't fire on a stale timestamp.
I've switched the fix to the swap in 657fba1 and kept the tests. On main, the two disarm tests fail, and with the swap all three pass. With a standalone DumpMaster and tcp_timeout=1, a 2-second async client_connected hook gets Closing connection due to inactivity at 1.00s on main. With the swap, nothing is closed and the request completes. I also merged main into the branch, which picks up the Debian 12 builder that fixes the failing Linux build jobs.
…-followup # Conflicts: # CHANGELOG.md

Description
TimeoutWatchdog.watch()waits for the watchdog to be enabled and then sleeps until the inactivity deadline. If a blocking addon hook disarms the watchdog during that sleep, the existing code can still invoke the timeout callback after waking because it only checks the activity deadline. On a live connection, that closes the client while the hook is still running.I reproduced this with a standalone asyncio script that starts a local HTTP server and a regular
DumpMasterproxy withtcp_timeout=1. An asyncrequesthook sleeps for two seconds; the client sends a GET through the proxy immediately after the watchdog enters its sleep.Closing connection due to inactivityat 1.00 seconds and the client received EOF (b'').HTTP/1.1 200 OKwith the expected body.The watchdog now sleeps first and then waits for the event before checking the activity deadline (suggested in review). If it was disarmed during the sleep, it blocks until it's re-armed. Re-arming refreshes the activity timestamp, so the next check can't fire on a stale deadline and the loop doesn't spin. The final nested
disarm()exit continues to register activity and re-enable the watchdog, and the configured timeout remains unchanged.The regression tests cover:
On unfixed
main, the two disarm-depth cases failed and the activity-deadline case passed. With this change, all three cases pass.Validation:
uv run pytest test/mitmproxy/proxy: 467 passed, 4 skipped.uv run tox -e lint: passed.Checklist