BaseClient
Serves as base class for client implementations, provides common client functionality.
using Renci.SshNet.Common;
using Renci.SshNet.Messages.Transport;
using System;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
namespace Renci.SshNet
{
[System.Runtime.CompilerServices.NullableContext(1)]
[System.Runtime.CompilerServices.Nullable(0)]
public abstract class BaseClient : IBaseClient, IDisposable
{
private readonly bool _ownsConnectionInfo;
private readonly IServiceFactory _serviceFactory;
private readonly object _keepAliveLock = new object();
private TimeSpan _keepAliveInterval;
[System.Runtime.CompilerServices.Nullable(2)]
private Timer _keepAliveTimer;
private ConnectionInfo _connectionInfo;
private bool _isDisposed;
[System.Runtime.CompilerServices.Nullable(2)]
[field: System.Runtime.CompilerServices.Nullable(2)]
internal ISession Session {
[System.Runtime.CompilerServices.NullableContext(2)]
get;
[System.Runtime.CompilerServices.NullableContext(2)]
private set;
}
internal IServiceFactory ServiceFactory => _serviceFactory;
public ConnectionInfo ConnectionInfo {
get {
CheckDisposed();
return _connectionInfo;
}
private set {
_connectionInfo = value;
}
}
public virtual bool IsConnected {
get {
CheckDisposed();
return IsSessionConnected();
}
}
public TimeSpan KeepAliveInterval {
get {
CheckDisposed();
return _keepAliveInterval;
}
set {
CheckDisposed();
value.EnsureValidTimeout("KeepAliveInterval");
if (!(value == _keepAliveInterval)) {
if (value == Timeout.InfiniteTimeSpan)
StopKeepAliveTimer();
else if (_keepAliveTimer != null) {
_keepAliveTimer.Change(value, value);
} else if (IsSessionConnected()) {
_keepAliveTimer = CreateKeepAliveTimer(value, value);
}
_keepAliveInterval = value;
}
}
}
[System.Runtime.CompilerServices.Nullable(new byte[] {
2,
1
})]
[method: System.Runtime.CompilerServices.Nullable(new byte[] {
2,
1
})]
[field: System.Runtime.CompilerServices.Nullable(new byte[] {
2,
1
})]
public event EventHandler<ExceptionEventArgs> ErrorOccurred;
[System.Runtime.CompilerServices.Nullable(new byte[] {
2,
1
})]
[method: System.Runtime.CompilerServices.Nullable(new byte[] {
2,
1
})]
[field: System.Runtime.CompilerServices.Nullable(new byte[] {
2,
1
})]
public event EventHandler<HostKeyEventArgs> HostKeyReceived;
[System.Runtime.CompilerServices.Nullable(new byte[] {
2,
1
})]
[method: System.Runtime.CompilerServices.Nullable(new byte[] {
2,
1
})]
[field: System.Runtime.CompilerServices.Nullable(new byte[] {
2,
1
})]
public event EventHandler<SshIdentificationEventArgs> ServerIdentificationReceived;
protected BaseClient(ConnectionInfo connectionInfo, bool ownsConnectionInfo)
: this(connectionInfo, ownsConnectionInfo, new ServiceFactory())
{
}
private protected BaseClient(ConnectionInfo connectionInfo, bool ownsConnectionInfo, IServiceFactory serviceFactory)
{
ThrowHelper.ThrowIfNull(connectionInfo, "connectionInfo");
ThrowHelper.ThrowIfNull(serviceFactory, "serviceFactory");
_connectionInfo = connectionInfo;
_ownsConnectionInfo = ownsConnectionInfo;
_serviceFactory = serviceFactory;
_keepAliveInterval = Timeout.InfiniteTimeSpan;
}
public void Connect()
{
CheckDisposed();
if (IsConnected)
throw new InvalidOperationException("The client is already connected.");
OnConnecting();
ISession session = Session;
if (session == null || !session.IsConnected) {
if (session != null)
DisposeSession(session);
Session = CreateAndConnectSession();
}
try {
OnConnected();
} catch {
DisposeSession();
throw;
}
StartKeepAliveTimer();
}
[AsyncStateMachine(typeof(<ConnectAsync>d__33))]
public Task ConnectAsync(CancellationToken cancellationToken)
{
<ConnectAsync>d__33 stateMachine = default(<ConnectAsync>d__33);
stateMachine.<>t__builder = AsyncTaskMethodBuilder.Create();
stateMachine.<>4__this = this;
stateMachine.cancellationToken = cancellationToken;
stateMachine.<>1__state = -1;
stateMachine.<>t__builder.Start(ref stateMachine);
return stateMachine.<>t__builder.Task;
}
public void Disconnect()
{
CheckDisposed();
OnDisconnecting();
StopKeepAliveTimer();
DisposeSession();
OnDisconnected();
}
[Obsolete("Use KeepAliveInterval to send a keep-alive message at regular intervals.")]
public void SendKeepAlive()
{
CheckDisposed();
SendKeepAliveMessage();
}
protected virtual void OnConnecting()
{
}
protected virtual void OnConnected()
{
}
protected virtual void OnDisconnecting()
{
Session?.OnDisconnecting();
}
protected virtual void OnDisconnected()
{
}
private void Session_ErrorOccured([System.Runtime.CompilerServices.Nullable(2)] object sender, ExceptionEventArgs e)
{
this.ErrorOccurred?.Invoke(this, e);
}
private void Session_HostKeyReceived([System.Runtime.CompilerServices.Nullable(2)] object sender, HostKeyEventArgs e)
{
this.HostKeyReceived?.Invoke(this, e);
}
private void Session_ServerIdentificationReceived([System.Runtime.CompilerServices.Nullable(2)] object sender, SshIdentificationEventArgs e)
{
this.ServerIdentificationReceived?.Invoke(this, e);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!_isDisposed && disposing) {
Disconnect();
if (_ownsConnectionInfo)
(_connectionInfo as IDisposable)?.Dispose();
_isDisposed = true;
}
}
protected void CheckDisposed()
{
ThrowHelper.ThrowObjectDisposedIf(_isDisposed, this);
}
private void StopKeepAliveTimer()
{
if (_keepAliveTimer != null) {
_keepAliveTimer.Dispose();
_keepAliveTimer = null;
}
}
private void SendKeepAliveMessage()
{
ISession session = Session;
if (session != null && Monitor.TryEnter(_keepAliveLock))
try {
session.TrySendMessage(new IgnoreMessage());
} finally {
Monitor.Exit(_keepAliveLock);
}
}
private void StartKeepAliveTimer()
{
if (!(_keepAliveInterval == Timeout.InfiniteTimeSpan) && _keepAliveTimer == null)
_keepAliveTimer = CreateKeepAliveTimer(_keepAliveInterval, _keepAliveInterval);
}
private Timer CreateKeepAliveTimer(TimeSpan dueTime, TimeSpan period)
{
return new Timer(delegate {
SendKeepAliveMessage();
}, Session, dueTime, period);
}
private ISession CreateAndConnectSession()
{
ISession session = _serviceFactory.CreateSession(ConnectionInfo, _serviceFactory.CreateSocketFactory());
session.ServerIdentificationReceived += Session_ServerIdentificationReceived;
session.HostKeyReceived += Session_HostKeyReceived;
session.ErrorOccured += Session_ErrorOccured;
try {
session.Connect();
return session;
} catch {
DisposeSession(session);
throw;
}
}
[AsyncStateMachine(typeof(<CreateAndConnectSessionAsync>d__51))]
private Task<ISession> CreateAndConnectSessionAsync(CancellationToken cancellationToken)
{
<CreateAndConnectSessionAsync>d__51 stateMachine = default(<CreateAndConnectSessionAsync>d__51);
stateMachine.<>t__builder = AsyncTaskMethodBuilder<ISession>.Create();
stateMachine.<>4__this = this;
stateMachine.cancellationToken = cancellationToken;
stateMachine.<>1__state = -1;
stateMachine.<>t__builder.Start(ref stateMachine);
return stateMachine.<>t__builder.Task;
}
private void DisposeSession(ISession session)
{
session.ErrorOccured -= Session_ErrorOccured;
session.HostKeyReceived -= Session_HostKeyReceived;
session.ServerIdentificationReceived -= Session_ServerIdentificationReceived;
session.Dispose();
}
private void DisposeSession()
{
ISession session = Session;
if (session != null) {
Session = null;
DisposeSession(session);
}
}
private bool IsSessionConnected()
{
return Session?.IsConnected ?? false;
}
}
}