Skip to content

Half closing of connections when CloseWrite() is available#536

Open
liojacqs wants to merge 1 commit into
jpillora:masterfrom
usualexperts:fix-close-order
Open

Half closing of connections when CloseWrite() is available#536
liojacqs wants to merge 1 commit into
jpillora:masterfrom
usualexperts:fix-close-order

Conversation

@liojacqs

Copy link
Copy Markdown

Fix for issue #535 Non graceful closing of remote connection

Use of CloseWrite() instead of Close() in Pipe(), only when available (typically TCP and SSH Channel).
This triggers EOF on the other side instead of totally closing the TCP connection / SSH Channel, leaving the possibility to receive remaining incoming data.

Also removed sync.Once, as when io.Copy finishes we only WriteClose() or Close() the destination of the copy, which triggers io.Copy to end on the other side in cascade.

Also added dst.Close() in both pipeRemote() and handleTCP() as we still need to Close() even with CloseWrite() called in both directions.

Clean termination when FIN received on one side but there's data remaining to send from the other side.

Fixes an issue discovered in a reverse scenario (client -> chiselServer -> chiselClient -> server), where the client would close connection after sending data, but before the server has finished sending data.
Without Chisel, same situation, the server is delaying the close because it still wants to send data, and it closes after having sent it.
With Chisel, chiselServer was closing the connection immediately first, preventing the server to send its remaining data.
@liojacqs

liojacqs commented Dec 6, 2024

Copy link
Copy Markdown
Author

Hello,
Any feedback / suggestion / remark on this ?

@liojacqs

liojacqs commented Dec 9, 2025

Copy link
Copy Markdown
Author

Hello,
Still no feedback / suggestion / remark on this ?

jpillora added a commit that referenced this pull request Jul 16, 2026
- cio.Pipe now propagates half-closes: clean EOF in one direction
  CloseWrites the destination while the other direction keeps
  flowing; copy errors still tear down both ends, and Pipe closes
  both conns before returning (no caller changes needed)
- rwcConn delegates CloseWrite to the underlying stream so the
  SOCKS path half-closes through ssh channels (from PR #548)
- exit side dials the target before accepting the ssh channel and
  rejects it on failure (ssh.ConnectionFailed), so inbound clients
  see a prompt close instead of a live conn to a dead target
- dial is context-bound (tunnel teardown cancels it) with a 30s
  default timeout, tunable via CHISEL_DIAL_TIMEOUT
- adapted from PRs #536/#548/#538; dropped #538's accept-loop
  serialization and error-text-into-stream reset behavior
- e2e: half-close echo (fails on old Pipe, verified), channel
  rejection for dead targets, prompt local-conn close; unit test
  for rwcConn CloseWrite delegation

Closes md task 19

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant