Repository navigation
sdk/python: end the local connection when a proxied guest port closes - #158
Merged
Merged
Conversation
proxy_port pumped the guest side in one thread and the local side in another, and the pump closed the local socket when the guest reached EOF. On Linux a close cannot wake a thread already blocked in recv on that socket, and the socket outlives the close until that read ends, so the FIN never reaches the client: a connection to a guest port whose upstream refuses hangs instead of ending. Measured against a guest bridge with nothing behind it — 13 ms in the guest, over 30 s through the relay, which turned a caller's five-second retry loop into a three-minute stall. Half-close it instead, the shape the Go SDK already uses, and let the read loop end on its own. The regression test reproduces on Linux only; macOS delivers the FIN either way.
The WHY was two lines where one carries it, the test helper's docstring restated its own body, and the helper class sat between the two tests instead of above them.
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.
proxy_portruns two threads per connection: one pumps the guest side into the local socket, the other pumps the local socket into the guest. When the guest side reached EOF the first thread closed the local socket.On Linux that close cannot wake the second thread, which is already blocked in
recvon that same socket, and the socket outlives the close until that read ends — so the FIN never reaches the client. A connection to a guest port whose upstream refuses hangs instead of ending.Measured against a guest bridge with nothing behind it:
A caller that retries on a refused connection pays the full socket timeout instead: OSWorld's browser setup retries every five seconds for fifteen attempts, and the first attempt alone cost 180 seconds on every task that launches a browser.
Half-closing is the fix, and it is the shape
sdk/go'sproxyConnalready uses —closeWrite(local)onceio.Copy(local, guest)returns. The local read loop then ends on its own when the client closes, and the socket is closed in thefinallyonce both directions are done. The read loop also stops raisingOSErrorinto a thread that has no handler for it.Verification
The regression test drives
proxy_portwith a guest conn that reports EOF while the local client is idle, which is the state the race needs:macOS delivers the FIN either way, so the test only reproduces on Linux — which is where CI runs it.
Gates from
sdk/python:ruff format --checkclean,ruff checkclean,pytest156 passed.