SSH: open a shell off the caller's thread, and stop waiting when the caller gives up

This commit is contained in:
jaap-jan committed 2026-10-02 14:02:18 +02:00
1 parent 365954c541
commit f997e87c5f
2 files changed
+99 -9

No files matched your search

@@ -28,6 +28,44 @@ public sealed class KeyAuthenticationTests(SshServerFixture fixture)
{
private static CancellationToken Token => TestContext.Current.CancellationToken;
/// <remarks>
/// Opening a shell is three round trips to the server, and SSH.NET makes them synchronously. They used to
/// be made on the caller's thread — the UI thread — behind a <c>Task.FromResult</c>, so the task this
/// returned was already finished and the window had stopped painting while it was being made.
/// </remarks>
[Fact]
public async Task OpeningAShell_LeavesTheCallingThreadFree()
{
await using var connection = await ConnectTrustedAsync(
new SshPrivateKeyCredential(Pkcs1(fixture.ClientKey), Passphrase: null));
var opening = connection.OpenShellAsync(TerminalSize.Default, Token);
opening.IsCompleted.ShouldBeFalse("three round trips to the server cannot have been made on this thread");
await using var shell = await opening;
}
/// <remarks>
/// The cancellation token used to be read once, before any of those round trips. A caller that gives up
/// now stops waiting at once, and the connection is left able to open the next shell.
/// </remarks>
[Fact]
public async Task AShellGivenUpOn_StopsTheWait_AndTheConnectionOpensAnother()
{
await using var connection = await ConnectTrustedAsync(
new SshPrivateKeyCredential(Pkcs1(fixture.ClientKey), Passphrase: null));
using var giveUp = CancellationTokenSource.CreateLinkedTokenSource(Token);
var opening = connection.OpenShellAsync(TerminalSize.Default, giveUp.Token);
await giveUp.CancelAsync();
await Should.ThrowAsync<OperationCanceledException>(opening);
await using var shell = await connection.OpenShellAsync(TerminalSize.Default, Token);
}
[Fact]
public async Task AKeyHeldAsBytes_AuthenticatesAndOpensAShell()
{