Skip to content

Commit

Permalink
SshClient: replace DisconnectedAsync API with Disconnected API. (#270)
Browse files Browse the repository at this point in the history
  • Loading branch information
tmds authored Dec 11, 2024
1 parent 2999ea0 commit 1599735
Show file tree
Hide file tree
Showing 4 changed files with 21 additions and 55 deletions.
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,7 @@ class SshClient : IDisposable

// Not usable with AutoReconnect.
// A connection must be established before calling this method.
// Throws if the connection was closed for another reason than explicit close by the user.
Task DisconnectedAsync(CancellationToken cancellationToken = default);
CancellationToken Disconnected { get; }

Task<RemoteProcess> ExecuteAsync(string command, CancellationToken cancellationToken);
Task<RemoteProcess> ExecuteAsync(string command, ExecuteOptions? options = null, CancellationToken cancellationToken = default);
Expand Down
11 changes: 7 additions & 4 deletions src/Tmds.Ssh/SshClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,13 +89,16 @@ public async Task ConnectAsync(CancellationToken cancellationToken = default)
await GetSessionAsync(cancellationToken, explicitConnect: true).ConfigureAwait(false);
}

public Task DisconnectedAsync(CancellationToken cancellationToken = default)
public CancellationToken Disconnected
{
SshSession session = GetConnectedSesion(cancellationToken);
return session.DisconnectedAsync(cancellationToken);
get
{
SshSession session = GetConnectedSesion();
return session.ConnectionClosed;
}
}

private SshSession GetConnectedSesion(CancellationToken cancellationToken, [CallerMemberNameAttribute]string? callerMethod = null)
private SshSession GetConnectedSesion([CallerMemberNameAttribute]string? callerMethod = null)
{
if (_autoReconnect)
{
Expand Down
27 changes: 0 additions & 27 deletions src/Tmds.Ssh/SshSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -98,33 +98,6 @@ public async Task ConnectAsync(TimeSpan connectTimeout, CancellationToken ct = d
await task;
}

public async Task DisconnectedAsync(CancellationToken cancellationToken)
{
Task? runningConnectionTask = null;
lock (_gate)
{
// Throw if a user has Disposed before calling this method.
ThrowIfDisposed();

runningConnectionTask = _runningConnectionTask;
}

if (runningConnectionTask is null)
{
Debug.Assert(false); // this method shouldn't ever get called if we never connected.
ThrowNeverConnected();
}
await runningConnectionTask.WaitAsync(cancellationToken);

// Don't throw if the connection was closed due to an explicit close by the user.
if (_abortReason == DisposedException)
{
return;
}

ThrowNewConnectionClosedException();
}

private async Task<SshConnection> EstablishConnectionAsync(CancellationToken ct)
{
Debug.Assert(_settings is not null);
Expand Down
35 changes: 13 additions & 22 deletions test/Tmds.Ssh.Tests/ConnectTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -461,62 +461,53 @@ public async Task SshConfig_Options()
}

[Fact]
public async Task DisconnectedAsync()
public async Task Disconnected()
{
using var client = await _sshServer.CreateClientAsync();

Task disconnectedTask = client.DisconnectedAsync();
CancellationToken disconnected = client.Disconnected;

client.Dispose();

await disconnectedTask;
Assert.True(disconnected.IsCancellationRequested);
}

[Fact]
public async Task DisconnectedAsync_WithError()
public async Task Disconnected_WithError()
{
using var client = await _sshServer.CreateClientAsync();

Task disconnectedTask = client.DisconnectedAsync();
CancellationToken disconnected = client.Disconnected;

client.ForceConnectionClose();

await Assert.ThrowsAsync<SshConnectionClosedException>(() => disconnectedTask);
Assert.True(disconnected.IsCancellationRequested);
Assert.True(client.Disconnected.IsCancellationRequested);
}

[Fact]
public async Task DisconnectedAsync_RequiresConnect()
public async Task Disconnected_RequiresConnect()
{
using var client = await _sshServer.CreateClientAsync(connect: false);

await Assert.ThrowsAsync<InvalidOperationException>(() => client.DisconnectedAsync());
Assert.Throws<InvalidOperationException>(() => client.Disconnected);
}

[Fact]
public async Task DisconnectedAsync_NotWhenAutoReconnect()
public async Task Disconnected_NotWhenAutoReconnect()
{
using var client = await _sshServer.CreateClientAsync(configure: settings => settings.AutoReconnect = true);

await Assert.ThrowsAsync<InvalidOperationException>(() => client.DisconnectedAsync());
Assert.Throws<InvalidOperationException>(() => client.Disconnected);
}

[Fact]
public async Task DisconnectedAsync_NotAfterDispose()
public async Task Disconnected_NotAfterDispose()
{
using var client = await _sshServer.CreateClientAsync();

client.Dispose();

await Assert.ThrowsAsync<ObjectDisposedException>(() => client.DisconnectedAsync());
}

[Fact]
public async Task DisconnectedAsync_CanCancel()
{
using var client = await _sshServer.CreateClientAsync();

var cts = new CancellationTokenSource(50);

await Assert.ThrowsAnyAsync<OperationCanceledException>(() => client.DisconnectedAsync(cts.Token));
Assert.Throws<ObjectDisposedException>(() => client.Disconnected);
}
}

0 comments on commit 1599735

Please sign in to comment.