Expand description
An owned byte stream over a russh channel that does not close the channel when it is dropped.
§The bug this module exists to make impossible
russh’s Channel::into_stream wraps the
channel in ChannelCloseOnDrop, whose Drop does
tokio::spawn(async move { sender.send(ChannelMsg::Close).await })(russh-0.61.2/src/channels/io/mod.rs). The server hands that stream to the
transport, so the Close is queued from a task nobody awaits the moment
the transfer finishes, racing the exit-status the server sends next.
OpenSSH frees a channel on Close and discards anything that arrives after
it, so the race decided whether a client saw an exit status at all.
Measured 2026-08-03, before this module existed: a raw
ssh … git-upload-pack 'team/project.git' returned a complete 244-byte
advertisement and exit status 255, and git push printed
* [new branch] HEAD -> main followed by error: failed to push some refs
for a push that had fully succeeded server-side. gunnar-cli carried a
250 ms linger timer to invert the race; it is deleted.
§The fix, by construction (one implementation)
Channel::split hands back a
ChannelReadHalf and a [ChannelWriteHalf], neither of which closes the
channel on drop. The write half stays with the server, which sends
exit-status and then close through the same sender the data went
through, so the ordering is a property of one FIFO queue rather than of a
sleep. The read half is moved in here and turned into an owned
AsyncRead, because russh’s own make_reader borrows the half and the
transport needs a 'static stream (it runs the blocking git protocol on
spawn_blocking, which will not accept a borrow).
There is no timing dependency left anywhere on this path.
Structs§
- Cancel
- The word a transfer’s git work hears when its client is gone.
- Channel
Reader - The data band of an SSH channel, as an owned
AsyncRead. - Session
Bound - A channel writer that ends with an error when the session does.
Constants§
- CROWDED_
SESSION_ END_ GRACE - The grace when the transfer’s fan has others queued behind it: a worker a hung-up client holds is a worker a waiting client does not get.
- SESSION_
END_ GRACE - How long a transfer whose session has ended is given to end by itself.
Functions§
- grace_
for - The grace a transfer whose session ended is given:
calm, orcrowdedwhenqueuedoperations wait for its fan. - serve_
until_ session_ ends - A transfer, bound to its session: what
servereturns, unless the session ends first and the transfer does not end by itself withingrace()(asked when the session ends;grace_for— the server givesSESSION_END_GRACE, orCROWDED_SESSION_END_GRACEwhen others wait for the fan), in which case it ends withended(). - session_
ended_ error - The error a transfer gets when the client’s session ended under it.
- session_
over - Resolve once
oversays the session has ended (or its sender is gone, which is the same thing said byDrop).
Type Aliases§
- Session
Over - The session is over, as a value every transfer on it can watch.