From cc4dca75edea1da7ed490b209f8e1ee472f4f830 Mon Sep 17 00:00:00 2001 From: "Victor C." <34163765+vicajilau@users.noreply.github.com> Date: Thu, 3 Sep 2026 06:29:05 +0100 Subject: [PATCH] chore: release 4.0.1 --- CHANGELOG.md | 2 +- pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9163a4c..53fc615 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## [4.0.1] - Unreleased +## [4.0.1] - 2026-09-03 - Fixed a channel stalling permanently when data arrived before the application subscribed to it. `StreamController.isPaused` is true until something listens, which suppressed every `SSH_MSG_CHANNEL_WINDOW_ADJUST`, and Dart delivers the first subscription as `onListen` rather than `onResume` — the only hook wired — so a peer that legally filled the advertised window in that gap was left at zero credit with no further data able to arrive and trigger a grant. Reproduced at the client's own sizes with 64 packets of 32 KiB into a 2 MiB window, before and after the first listener and through `.map()`, in every case zero adjustments. The remote forwarding example in the README reaches it: it awaits `Socket.connect()` for each connection before subscribing, and the forwarded channel is created with no listener attached [#244]. - Changed the receive window to be granted back once half of it has been consumed rather than after every data packet. Any consumed byte made the outstanding grant positive, so each `SSH_MSG_CHANNEL_DATA` was answered with a window adjust of its own, and the smaller the packets the closer the uplink packet count got to the downlink one: counted on `SSHChannelController`, 256 inbound packets of 32 KiB produced 256 adjustments, and 4096 of 64 B produced 4096. They now produce 8 and 0. This is one of the two rules OpenSSH applies in `channels.c`, which refills at half the window or once `local_window_max - local_window > local_maxpacket * 3`, whichever comes first. Only the first is implemented here, so at a 2 MiB window with 32 KiB packets this defers further than OpenSSH would, thirty-two packets against its three or four, which is the point of the change. The threshold never defers past one maximum packet of remaining credit, so a window smaller than twice the packet size cannot leave a conforming peer holding a chunk it may neither send nor is obliged to split. Pausing the stream still suppresses the grant, which is the documented backpressure mechanism [#244]. diff --git a/pubspec.yaml b/pubspec.yaml index 471fe4c..e70057f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: dartssh2 -version: 4.0.0 +version: 4.0.1 description: SSH and SFTP client written in pure Dart, aiming to be feature-rich as well as easy to use. homepage: https://github.com/vicajilau/dartssh2 repository: https://github.com/vicajilau/dartssh2