diff --git a/Borepin/Borepin/Base/ConnectionModelBase.cs b/Borepin/Borepin/Base/ConnectionModelBase.cs
index a07360e..aed8945 100644
--- a/Borepin/Borepin/Base/ConnectionModelBase.cs
+++ b/Borepin/Borepin/Base/ConnectionModelBase.cs
@@ -1,5 +1,6 @@
using Borepin.Service;
using FabAccessAPI;
+using NLog;
using Prism.Navigation;
using Prism.Services;
using System;
@@ -30,12 +31,13 @@ namespace Borepin.Base
#endregion
#region Methods
- public async void OnConnectionStatusChanged(object sender, ConnectionStatusChange args)
+ public async void OnConnectionStatusChanged(object sender, ConnectionStatusChanged args)
{
switch(args)
{
- case ConnectionStatusChange.Connected:
+ case ConnectionStatusChanged.Connected:
IsConnected = true;
+ IsConnecting = false;
try
{
await LoadAPIData().ConfigureAwait(false);
@@ -44,35 +46,16 @@ namespace Borepin.Base
{
IsConnected = false;
await _API.Disconnect().ConfigureAwait(false);
- _API.UnbindAllEvents();
+ _API.UnbindEventHandler();
}
break;
- case ConnectionStatusChange.Reconnected:
- try
- {
- await ReloadAPIData().ConfigureAwait(false);
- }
- catch
- {
- IsConnected = false;
- await _API.Disconnect().ConfigureAwait(false);
- _API.UnbindAllEvents();
- }
- break;
- case ConnectionStatusChange.ConnectionLoss:
- try
- {
- await _API.Reconnect().ConfigureAwait(false);
- }
- catch
- {
- IsConnected = false;
- await _API.Disconnect().ConfigureAwait(false);
- _API.UnbindAllEvents();
- }
- break;
- case ConnectionStatusChange.Disconnected:
+ case ConnectionStatusChanged.ConnectionLoss:
IsConnected = false;
+ IsConnecting = true;
+ break;
+ case ConnectionStatusChanged.Disconnected:
+ IsConnected = false;
+ IsConnecting = false;
break;
}
}
@@ -81,22 +64,28 @@ namespace Borepin.Base
{
return Task.CompletedTask;
}
- public virtual Task ReloadAPIData()
- {
- return Task.CompletedTask;
- }
#endregion
#region Fields
///
/// PageModel is Connected
///
- private bool _IsConnected = true;
+ private bool _IsConnected = false;
public bool IsConnected
{
get => _IsConnected;
set => SetProperty(ref _IsConnected, value);
}
+
+ ///
+ /// PageModel is Connecting
+ ///
+ private bool _IsConnecting = false;
+ public bool IsConnecting
+ {
+ get => _IsConnecting;
+ set => SetProperty(ref _IsConnecting, value);
+ }
#endregion
#region INavigationAware
@@ -110,11 +99,12 @@ namespace Borepin.Base
{
await LoadAPIData().ConfigureAwait(false);
}
- catch(Exception ex)
+ catch(Exception exception)
{
IsConnected = false;
await _API.Disconnect().ConfigureAwait(false);
- _API.UnbindAllEvents();
+ _API.UnbindEventHandler();
+ Log.Error("LoadAPIData failed", exception);
}
}
}
diff --git a/Borepin/Borepin/Page/MachineListPage.xaml b/Borepin/Borepin/Page/MachineListPage.xaml
index 5b47d51..08fcd0a 100644
--- a/Borepin/Borepin/Page/MachineListPage.xaml
+++ b/Borepin/Borepin/Page/MachineListPage.xaml
@@ -17,9 +17,7 @@
-
-
-
+
-
-
-
diff --git a/Borepin/Borepin/PageModel/AddServerProcess/AuthPlainPageModel.cs b/Borepin/Borepin/PageModel/AddServerProcess/AuthPlainPageModel.cs
index 81612c4..8cc022b 100644
--- a/Borepin/Borepin/PageModel/AddServerProcess/AuthPlainPageModel.cs
+++ b/Borepin/Borepin/PageModel/AddServerProcess/AuthPlainPageModel.cs
@@ -8,6 +8,7 @@ using Borepin.Service;
using Borepin.Service.Storage;
using FabAccessAPI;
using FabAccessAPI.Exceptions;
+using FabAccessAPI.Exceptions.SASL;
using Prism.Commands;
using Prism.Navigation;
using Prism.Services;
@@ -90,7 +91,7 @@ namespace Borepin.PageModel.AddServerProcess
_ConnectionData = new ConnectionData()
{
Host = _ConnectionData.Host,
- Mechanism = Mechanism.PLAIN,
+ Mechanism = SASLMechanismEnum.PLAIN,
Username = Username,
Properties = new Dictionary(StringComparer.Ordinal)
{
@@ -104,7 +105,7 @@ namespace Borepin.PageModel.AddServerProcess
if (_API.IsConnected)
{
await _API.Disconnect().ConfigureAwait(true);
- _API.UnbindAllEvents();
+ _API.UnbindEventHandler();
}
}
@@ -112,7 +113,7 @@ namespace Borepin.PageModel.AddServerProcess
{
await _API.Connect(_ConnectionData).ConfigureAwait(false);
}
- catch (ConnectingFailedException)
+ catch (ConnectionException)
{
Device.BeginInvokeOnMainThread(async () =>
{
diff --git a/Borepin/Borepin/PageModel/AddServerProcess/ChooseAuthTypePageModel.cs b/Borepin/Borepin/PageModel/AddServerProcess/ChooseAuthTypePageModel.cs
index 2e3f618..c546002 100644
--- a/Borepin/Borepin/PageModel/AddServerProcess/ChooseAuthTypePageModel.cs
+++ b/Borepin/Borepin/PageModel/AddServerProcess/ChooseAuthTypePageModel.cs
@@ -51,7 +51,7 @@ namespace Borepin.PageModel.AddServerProcess
}
public async void AuthPlainCommandExecute()
{
- _ConnectionData.Mechanism = Mechanism.PLAIN;
+ _ConnectionData.Mechanism = SASLMechanismEnum.PLAIN;
INavigationResult result = await _NavigationService.NavigateAsync("AddServerProcess_AuthPlainPage").ConfigureAwait(false);
if(result.Exception != null)
diff --git a/Borepin/Borepin/PageModel/AddServerProcess/SelectServerPageModel.cs b/Borepin/Borepin/PageModel/AddServerProcess/SelectServerPageModel.cs
index 390bc6c..62b6e15 100644
--- a/Borepin/Borepin/PageModel/AddServerProcess/SelectServerPageModel.cs
+++ b/Borepin/Borepin/PageModel/AddServerProcess/SelectServerPageModel.cs
@@ -118,9 +118,9 @@ namespace Borepin.PageModel.AddServerProcess
try
{
API api = new API();
- await api.TestConnection(_ConnectionData).ConfigureAwait(false);
+ await api.TryToConnect(_ConnectionData).ConfigureAwait(false);
}
- catch(ConnectingFailedException)
+ catch(ConnectionException)
{
Device.BeginInvokeOnMainThread(async () =>
{
diff --git a/Borepin/Borepin/PageModel/ServerPageModel.cs b/Borepin/Borepin/PageModel/ServerPageModel.cs
index 7540bfa..2212e3c 100644
--- a/Borepin/Borepin/PageModel/ServerPageModel.cs
+++ b/Borepin/Borepin/PageModel/ServerPageModel.cs
@@ -4,6 +4,7 @@ using Borepin.Service;
using Borepin.Service.Storage;
using FabAccessAPI;
using FabAccessAPI.Exceptions;
+using FabAccessAPI.Exceptions.SASL;
using Prism.Commands;
using Prism.Navigation;
using Prism.Services;
@@ -51,7 +52,7 @@ namespace Borepin.PageModel
throw new InstanceIncorrectException();
}
- if(_API.ConnectionData != null && Connection_Item != null)
+ if(_API.IsConnected && Connection_Item != null)
{
InstanceIsActiveConnection = Connection_Item.Equals(_API.ConnectionData);
}
@@ -124,14 +125,14 @@ namespace Borepin.PageModel
if(_API.IsConnected)
{
await _API.Disconnect().ConfigureAwait(true);
- _API.UnbindAllEvents();
+ _API.UnbindEventHandler();
}
try
{
await _API.Connect(Connection_Item).ConfigureAwait(false);
}
- catch(ConnectingFailedException)
+ catch(ConnectionException)
{
Device.BeginInvokeOnMainThread(async () =>
{
@@ -184,7 +185,7 @@ namespace Borepin.PageModel
}
public async Task DisonnectCommandExecute()
{
- _API.UnbindAllEvents();
+ _API.UnbindEventHandler();
await _API.Disconnect().ConfigureAwait(false);
await LoadInstance(Connection_Item).ConfigureAwait(false);
@@ -215,7 +216,7 @@ namespace Borepin.PageModel
if(string.Equals(result.Parameters.GetValue("result"), "confirm", StringComparison.Ordinal))
{
await _API.Disconnect().ConfigureAwait(false);
- _API.UnbindAllEvents();
+ _API.UnbindEventHandler();
await _LoginStorageService.Remove(result.Parameters.GetValue("instance")).ConfigureAwait(false);
Device.BeginInvokeOnMainThread(async () =>
diff --git a/Borepin/Borepin/PageModel/StartPageModel.cs b/Borepin/Borepin/PageModel/StartPageModel.cs
index 4c7bfef..59bdec5 100644
--- a/Borepin/Borepin/PageModel/StartPageModel.cs
+++ b/Borepin/Borepin/PageModel/StartPageModel.cs
@@ -3,6 +3,7 @@ using Borepin.Service;
using Borepin.Service.Storage;
using FabAccessAPI;
using FabAccessAPI.Exceptions;
+using FabAccessAPI.Exceptions.SASL;
using Prism.AppModel;
using Prism.Navigation;
using Prism.Services;
@@ -69,7 +70,7 @@ namespace Borepin.PageModel
}
});
}
- catch (ConnectingFailedException)
+ catch (ConnectionException)
{
Device.BeginInvokeOnMainThread(async () =>
{
diff --git a/Borepin/Borepin/View/ConnectionStateView.xaml b/Borepin/Borepin/View/ConnectionStateView.xaml
index fae3a98..4e78667 100644
--- a/Borepin/Borepin/View/ConnectionStateView.xaml
+++ b/Borepin/Borepin/View/ConnectionStateView.xaml
@@ -16,9 +16,11 @@
-
-
-
+
+
+
+
+
diff --git a/FabAccessAPI/API.cs b/FabAccessAPI/API.cs
index a23b258..3bf0bbf 100644
--- a/FabAccessAPI/API.cs
+++ b/FabAccessAPI/API.cs
@@ -1,5 +1,6 @@
using Capnp.Rpc;
using FabAccessAPI.Exceptions;
+using FabAccessAPI.Exceptions.SASL;
using FabAccessAPI.Schema;
using NLog;
using S22.Sasl;
@@ -7,7 +8,6 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Security;
-using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Threading.Tasks;
@@ -21,49 +21,76 @@ namespace FabAccessAPI
#endregion
#region Private Members
+ ///
+ /// Internal client to connect to a server with TCP and RPC
+ ///
private TcpRpcClient _TcpRpcClient;
+
+ ///
+ /// Private ConnectionData
+ ///
+ private ConnectionData _ConnectionData;
+
+ ///
+ /// Private ServerData
+ ///
+ private ServerData _ServerData;
+
+ ///
+ /// Private Session
+ ///
+ private Session _Session;
+
+ ///
+ /// Private Bootstrap
+ ///
private IBootstrap _Bootstrap;
+
+ ///
+ /// Timer to check connection status
+ ///
+ private readonly Timer _ConnectionHeatbeat;
+
+ ///
+ /// Semaphore to connect only once
+ ///
private static SemaphoreSlim _ConnectSemaphore = new SemaphoreSlim(1, 1);
- private static SemaphoreSlim _ReconnectSemaphore = new SemaphoreSlim(1, 1);
#endregion
#region Constructors
+
public API()
{
-
- }
- #endregion
-
- #region Events
- public event EventHandler ConnectionStatusChanged;
-
- public void OnTcpRpcConnectionChanged(object sender, ConnectionStateChange args)
- {
- if (args.LastState == ConnectionState.Active && args.NewState == ConnectionState.Down)
- {
- Log.Trace("TcpRpcClient Event ConnectionLoss");
- ConnectionStatusChanged?.Invoke(this, ConnectionStatusChange.ConnectionLoss);
- _TcpRpcClient = null;
- }
- }
-
- public void UnbindAllEvents()
- {
- if(ConnectionStatusChanged != null)
- {
- foreach (Delegate d in ConnectionStatusChanged.GetInvocationList())
- {
- ConnectionStatusChanged -= (EventHandler)d;
- }
- }
+ _ConnectionHeatbeat = new Timer(Heartbeat, null, 1000, 1000);
}
#endregion
#region Members
- public ConnectionData ConnectionData { get; private set; }
+ ///
+ /// State of the conneciton, can the API-Service connect to a server
+ ///
+ public bool CanConnect
+ {
+ get
+ {
+ return _ConnectionData != null;
+ }
+ }
- public ConnectionInfo ConnectionInfo { get; private set; }
+ ///
+ /// State of the conneciton, is the API-Service connecting to a server
+ ///
+ public bool IsConnecting
+ {
+ get
+ {
+ return _TcpRpcClient != null && _ConnectionData != null;
+ }
+ }
+ ///
+ /// State of the conneciton, is the API-Service connected to a server
+ ///
public bool IsConnected
{
get
@@ -72,15 +99,124 @@ namespace FabAccessAPI
}
}
- public Session Session { get; private set; }
+ ///
+ /// Information about the connection
+ ///
+ /// When API-Service is not connected or trying to connected to a server
+ public ConnectionData ConnectionData
+ {
+ get
+ {
+ if(_ConnectionData == null || !IsConnecting)
+ {
+ throw new InvalidOperationException();
+ }
+ else
+ {
+ return _ConnectionData;
+ }
+ }
+ private set
+ {
+ _ConnectionData = value;
+ }
+ }
+
+ ///
+ /// Information about the server
+ /// Is only avalible if the API-Service is connected
+ ///
+ /// When API-Service is not connected
+ public ServerData ServerData
+ {
+ get
+ {
+ if (_ServerData == null || !IsConnected)
+ {
+ throw new InvalidOperationException();
+ }
+ else
+ {
+ return _ServerData;
+ }
+ }
+ private set
+ {
+ _ServerData = value;
+ }
+ }
+ #endregion
+
+ #region Events
+ ///
+ /// Event on changes in connection status
+ ///
+ public event EventHandler ConnectionStatusChanged;
+
+ ///
+ /// Unbind all handlers from EventHandler
+ ///
+ public void UnbindEventHandler()
+ {
+ if (ConnectionStatusChanged != null)
+ {
+ Log.Trace("Eventhandlers unbinded");
+ foreach (Delegate d in ConnectionStatusChanged.GetInvocationList())
+ {
+ ConnectionStatusChanged -= (EventHandler)d;
+ }
+ }
+ }
+
+ ///
+ /// Eventhandler for TcpRpcConnectionChanged
+ /// Track connection loss and publish i in ConnectionStatusChanged
+ ///
+ public void OnTcpRpcConnectionChanged(object sender, ConnectionStateChange args)
+ {
+ if (args.LastState == ConnectionState.Active && args.NewState == ConnectionState.Down)
+ {
+ Log.Trace("TcpRpcClient Event ConnectionLoss");
+ ConnectionStatusChanged?.Invoke(this, FabAccessAPI.ConnectionStatusChanged.ConnectionLoss);
+ }
+ }
+ #endregion
+
+ #region Session
+ ///
+ /// Get session after connection
+ ///
+ /// When API-Service is not connected
+ public Session Session
+ {
+ get
+ {
+ if (_Session == null || !IsConnected)
+ {
+ throw new InvalidOperationException();
+ }
+ else
+ {
+ return _Session;
+ }
+ }
+ private set
+ {
+ _Session = value;
+ }
+ }
+
#endregion
#region Methods
///
/// Connect to server with ConnectionData
+ /// If connection lost, the API-Server will try to reconnect
///
- ///
- ///
+ /// Data to establish a connection to a server
+ /// When API-Service can not connect to a server
+ /// When API-Service can connect to a server but can not authenticate
+ /// When API-Service is allready connected
public async Task Connect(ConnectionData connectionData, TcpRpcClient tcpRpcClient = null)
{
await _ConnectSemaphore.WaitAsync();
@@ -88,7 +224,8 @@ namespace FabAccessAPI
{
if (IsConnected)
{
- await Disconnect();
+ Log.Warn("API already connected");
+ throw new InvalidOperationException();
}
if (tcpRpcClient == null)
@@ -101,20 +238,19 @@ namespace FabAccessAPI
await _ConnectAsync(tcpRpcClient, connectionData).ConfigureAwait(false);
_Bootstrap = tcpRpcClient.GetMain();
- ConnectionInfo = await _GetConnectionInfo(_Bootstrap);
+ ServerData = await _GetServerData(_Bootstrap);
Session = await _Authenticate(connectionData).ConfigureAwait(false);
ConnectionData = connectionData;
_TcpRpcClient = tcpRpcClient;
tcpRpcClient.ConnectionStateChanged += OnTcpRpcConnectionChanged;
- ConnectionStatusChanged?.Invoke(this, ConnectionStatusChange.Connected);
+ ConnectionStatusChanged?.Invoke(this, FabAccessAPI.ConnectionStatusChanged.Connected);
Log.Info("API connected");
}
catch (System.Exception ex)
{
- await Disconnect().ConfigureAwait(false);
- Log.Warn(ex, "API connecting failed");
+ Log.Warn(ex, "API connect failed");
throw ex;
}
}
@@ -124,47 +260,36 @@ namespace FabAccessAPI
}
}
+ ///
+ /// Disconnect from a server
+ ///
+ /// When API-Service is not connected or trying to connect
public Task Disconnect()
{
if (IsConnected)
{
_TcpRpcClient.Dispose();
}
-
- _Bootstrap = null;
- Session = null;
- _TcpRpcClient = null;
- ConnectionData = null;
- ConnectionInfo = null;
- ConnectionStatusChanged?.Invoke(this, ConnectionStatusChange.Disconnected);
+ _Bootstrap = null;
+ _TcpRpcClient = null;
+
+ Session = null;
+ ConnectionData = null;
+ ServerData = null;
+
+ ConnectionStatusChanged?.Invoke(this, FabAccessAPI.ConnectionStatusChanged.Disconnected);
Log.Info("API disconnected");
-
return Task.CompletedTask;
}
- public async Task Reconnect()
- {
- await _ReconnectSemaphore.WaitAsync();
- try
- {
- if (ConnectionData != null && IsConnected == false)
- {
- await Connect(ConnectionData);
- }
-
- ConnectionStatusChanged?.Invoke(this, ConnectionStatusChange.Reconnected);
- Log.Info("API reconnected");
- }
- finally
- {
- _ReconnectSemaphore.Release();
- }
-
- }
-
- public async Task TestConnection(ConnectionData connectionData, TcpRpcClient tcpRpcClient = null)
+ ///
+ /// Try to connect to a server and get ServerData
+ /// The connection is not maintained
+ ///
+ /// When API-Service can not connect to a server
+ public async Task TryToConnect(ConnectionData connectionData, TcpRpcClient tcpRpcClient = null)
{
try
{
@@ -174,41 +299,57 @@ namespace FabAccessAPI
}
await _ConnectAsync(tcpRpcClient, connectionData).ConfigureAwait(false);
- IBootstrap testBootstrap = tcpRpcClient.GetMain();
+ IBootstrap bootstrap = tcpRpcClient.GetMain();
- ConnectionInfo connectionInfo = await _GetConnectionInfo(testBootstrap).ConfigureAwait(false);
+ ServerData serverData = await _GetServerData(bootstrap).ConfigureAwait(false);
tcpRpcClient.Dispose();
- return connectionInfo;
+ return serverData;
}
- catch
+ catch(System.Exception ex)
{
- throw new ConnectingFailedException();
+ throw new ConnectionException("Test Connection Failed", ex);
}
}
+
+ ///
+ /// Public Wrapper to run HeartbeatAsync
+ ///
+ public void Heartbeat(object state)
+ {
+ _ = HeartbeatAsync();
+ }
#endregion
#region Private Methods
+
+
+ private async Task HeartbeatAsync()
+ {
+ if(!IsConnected && CanConnect)
+ {
+ await Connect(ConnectionData).ConfigureAwait(false);
+ }
+ }
+
///
/// Validate Certificate
/// TODO: Do some validation
///
- private static bool _RemoteCertificateValidationCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
+ private bool _RemoteCertificateValidationCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
// TODO Cert Check
return true;
}
///
- /// Connect to server async with ConnectionData
+ /// Connect async to a server with ConnectionData
///
- /// TLS Error
- /// Based on RPC Exception
- ///
- private async Task _ConnectAsync(TcpRpcClient rpcClient, ConnectionData connectionData)
+ /// Based on RPC Exception
+ private async Task _ConnectAsync(TcpRpcClient tcprpcClient, ConnectionData connectionData)
{
- rpcClient.InjectMidlayer((tcpstream) =>
+ tcprpcClient.InjectMidlayer((tcpstream) =>
{
var sslStream = new SslStream(tcpstream, false, new RemoteCertificateValidationCallback(_RemoteCertificateValidationCallback));
try
@@ -216,61 +357,58 @@ namespace FabAccessAPI
sslStream.AuthenticateAsClient("bffhd");
return sslStream;
}
- catch (AuthenticationException)
+ catch (AuthenticationException exception)
{
sslStream.Close();
- throw;
+ Log.Warn(exception);
+ throw new ConnectionException("TLS failed", exception);
}
});
-
+
try
{
Task timeoutTask = Task.Delay(3000);
- rpcClient.Connect(connectionData.Host.Host, connectionData.Host.Port);
- await await Task.WhenAny(rpcClient.WhenConnected, timeoutTask);
-
- if(timeoutTask.IsCompleted)
+ tcprpcClient.Connect(connectionData.Host.Host, connectionData.Host.Port);
+ await await Task.WhenAny(tcprpcClient.WhenConnected, timeoutTask);
+
+ if (timeoutTask.IsCompleted)
{
- throw new ConnectingFailedException("Connection timeout");
+ Exceptions.TimeoutException timeoutException = new Exceptions.TimeoutException();
+ Log.Warn(timeoutException);
+ throw new ConnectionException("Connection timeout", timeoutException);
}
}
catch (RpcException exception) when (string.Equals(exception.Message, "TcpRpcClient is unable to connect", StringComparison.Ordinal))
{
- throw new ConnectingFailedException("RPC Connecting failed", exception);
+ Log.Warn(exception);
+ throw new ConnectionException("RPC Connecting failed", exception);
}
}
- ///
- /// Create ConnectionInfo from Bootstrap
- ///
- private async Task _GetConnectionInfo(IBootstrap bootstrap)
- {
- ConnectionInfo connectionInfo = new ConnectionInfo()
- {
- APIVersion = await bootstrap.GetAPIVersion().ConfigureAwait(false),
- Mechanisms = new List(await bootstrap.Mechanisms().ConfigureAwait(false)),
- ServerName = (await bootstrap.GetServerRelease().ConfigureAwait(false)).Item1,
- ServerRelease = (await bootstrap.GetServerRelease().ConfigureAwait(false)).Item2,
- };
-
- return connectionInfo;
- }
-
///
/// Authenticate connection with ConnectionData
///
- ///
- ///
- ///
+ /// Data to establish a connection to a server
+ ///
private async Task _Authenticate(ConnectionData connectionData)
{
- IAuthentication? authentication = await _Bootstrap.CreateSession(MechanismString.ToString(connectionData.Mechanism)).ConfigureAwait(false);
+ IAuthentication? authentication = await _Bootstrap.CreateSession(SASLMechanism.ToString(connectionData.Mechanism)).ConfigureAwait(false);
- return await _SASLAuthenticate(authentication, MechanismString.ToString(connectionData.Mechanism), connectionData.Properties).ConfigureAwait(false);
+ try
+ {
+ return await _SASLAuthenticate(authentication, SASLMechanism.ToString(connectionData.Mechanism), connectionData.Properties).ConfigureAwait(false);
+ }
+ catch (System.Exception exception)
+ {
+ Log.Warn(exception, "API authenticating failed");
+ AuthenticationException authenticationException = new AuthenticationException("Authentication failed", exception);
+
+ throw authenticationException;
+ }
}
///
- /// Authenticate Connection to get Session
+ /// Authenticate with SASL
///
///
///
@@ -293,11 +431,11 @@ namespace FabAccessAPI
Response? response = await authentication.Step(data);
while (!saslMechanism.IsCompleted)
{
- if(response.Failed != null)
+ if (response.Failed != null)
{
break;
}
- if(response.Challenge != null)
+ if (response.Challenge != null)
{
byte[]? additional = saslMechanism.GetResponse(response.Challenge.ToArray());
response = await authentication.Step(additional);
@@ -331,6 +469,22 @@ namespace FabAccessAPI
throw new AuthenticationFailedException();
}
}
+
+ ///
+ /// Get ServerData from server with tcprpcconnection
+ ///
+ private async Task _GetServerData(IBootstrap bootstrap)
+ {
+ ServerData serverData = new ServerData()
+ {
+ APIVersion = await bootstrap.GetAPIVersion().ConfigureAwait(false),
+ Mechanisms = new List(await bootstrap.Mechanisms().ConfigureAwait(false)),
+ ServerName = (await bootstrap.GetServerRelease().ConfigureAwait(false)).Item1,
+ ServerRelease = (await bootstrap.GetServerRelease().ConfigureAwait(false)).Item2,
+ };
+
+ return serverData;
+ }
#endregion
}
}
diff --git a/FabAccessAPI/ConnectionData.cs b/FabAccessAPI/ConnectionData.cs
index c266361..f4c2bd8 100644
--- a/FabAccessAPI/ConnectionData.cs
+++ b/FabAccessAPI/ConnectionData.cs
@@ -3,12 +3,18 @@ using System.Collections.Generic;
namespace FabAccessAPI
{
+ ///
+ /// Data to establish a connection to a server
+ /// With Data for Authentication
+ ///
public class ConnectionData
{
public Uri Host;
- public Mechanism Mechanism;
public string Username;
+
+ public SASLMechanismEnum Mechanism;
public Dictionary Properties;
+
public DateTime LastTime;
public override bool Equals(object? obj)
diff --git a/FabAccessAPI/ConnectionStatusChange.cs b/FabAccessAPI/ConnectionStatusChange.cs
deleted file mode 100644
index d6fd683..0000000
--- a/FabAccessAPI/ConnectionStatusChange.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-namespace FabAccessAPI
-{
- public enum ConnectionStatusChange
- {
- ///
- /// Client has established connection to server.
- ///
- Connected,
- ///
- /// Client has closed connection to server.
- ///
- Disconnected,
- ///
- /// Connection was lost and Client has reestablished connection to server.
- ///
- Reconnected,
- ///
- /// Connection was lost and can be reestablished with reconnect
- /// Connection should be closed if reconnecting fails.
- ///
- ConnectionLoss
- }
-}
diff --git a/FabAccessAPI/ConnectionStatusChanged.cs b/FabAccessAPI/ConnectionStatusChanged.cs
new file mode 100644
index 0000000..44cc80e
--- /dev/null
+++ b/FabAccessAPI/ConnectionStatusChanged.cs
@@ -0,0 +1,20 @@
+namespace FabAccessAPI
+{
+ public enum ConnectionStatusChanged
+ {
+ ///
+ /// API-Service has established connection to server
+ ///
+ Connected,
+
+ ///
+ /// API-Service has closed the connection to a server
+ ///
+ Disconnected,
+
+ ///
+ /// Connection was lost and the API-Service will try to reconnect automatically
+ ///
+ ConnectionLoss
+ }
+}
diff --git a/FabAccessAPI/Exceptions/AuthenticationException.cs b/FabAccessAPI/Exceptions/AuthenticationException.cs
new file mode 100644
index 0000000..5b3eb33
--- /dev/null
+++ b/FabAccessAPI/Exceptions/AuthenticationException.cs
@@ -0,0 +1,26 @@
+using System;
+
+namespace FabAccessAPI.Exceptions
+{
+ ///
+ /// Authenticating to a server has failed
+ /// InnerException will provide more information
+ ///
+ public class AuthenticationException : Exception
+ {
+ public AuthenticationException()
+ {
+
+ }
+
+ public AuthenticationException(string message) : base(message)
+ {
+
+ }
+
+ public AuthenticationException(string message, Exception inner) : base(message, inner)
+ {
+
+ }
+ }
+}
diff --git a/FabAccessAPI/Exceptions/ConnectingFailedException.cs b/FabAccessAPI/Exceptions/ConnectingFailedException.cs
deleted file mode 100644
index 9f96c18..0000000
--- a/FabAccessAPI/Exceptions/ConnectingFailedException.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-using System;
-
-namespace FabAccessAPI.Exceptions
-{
- public class ConnectingFailedException : Exception
- {
- public ConnectingFailedException()
- {
-
- }
-
- public ConnectingFailedException(string message) : base(message)
- {
-
- }
-
- public ConnectingFailedException(string message, Exception inner) : base(message, inner)
- {
-
- }
- }
-}
diff --git a/FabAccessAPI/Exceptions/ConnectionException.cs b/FabAccessAPI/Exceptions/ConnectionException.cs
new file mode 100644
index 0000000..e018c2b
--- /dev/null
+++ b/FabAccessAPI/Exceptions/ConnectionException.cs
@@ -0,0 +1,26 @@
+using System;
+
+namespace FabAccessAPI.Exceptions
+{
+ ///
+ /// Connecting to a server has failed
+ /// InnerException will provide more information
+ ///
+ public class ConnectionException : Exception
+ {
+ public ConnectionException()
+ {
+
+ }
+
+ public ConnectionException(string message) : base(message)
+ {
+
+ }
+
+ public ConnectionException(string message, Exception inner) : base(message, inner)
+ {
+
+ }
+ }
+}
diff --git a/FabAccessAPI/Exceptions/AuthenticationFailedException.cs b/FabAccessAPI/Exceptions/SASL/AuthenticationFailedException.cs
similarity index 94%
rename from FabAccessAPI/Exceptions/AuthenticationFailedException.cs
rename to FabAccessAPI/Exceptions/SASL/AuthenticationFailedException.cs
index cef9c6d..b31c304 100644
--- a/FabAccessAPI/Exceptions/AuthenticationFailedException.cs
+++ b/FabAccessAPI/Exceptions/SASL/AuthenticationFailedException.cs
@@ -1,6 +1,6 @@
using System;
-namespace FabAccessAPI.Exceptions
+namespace FabAccessAPI.Exceptions.SASL
{
public class AuthenticationFailedException : Exception
{
diff --git a/FabAccessAPI/Exceptions/BadMechanismException.cs b/FabAccessAPI/Exceptions/SASL/BadMechanismException.cs
similarity index 90%
rename from FabAccessAPI/Exceptions/BadMechanismException.cs
rename to FabAccessAPI/Exceptions/SASL/BadMechanismException.cs
index 087ddbe..0728be9 100644
--- a/FabAccessAPI/Exceptions/BadMechanismException.cs
+++ b/FabAccessAPI/Exceptions/SASL/BadMechanismException.cs
@@ -1,6 +1,6 @@
using System;
-namespace FabAccessAPI.Exceptions
+namespace FabAccessAPI.Exceptions.SASL
{
public class BadMechanismException : Exception
{
diff --git a/FabAccessAPI/Exceptions/InvalidCredentialsException.cs b/FabAccessAPI/Exceptions/SASL/InvalidCredentialsException.cs
similarity index 90%
rename from FabAccessAPI/Exceptions/InvalidCredentialsException.cs
rename to FabAccessAPI/Exceptions/SASL/InvalidCredentialsException.cs
index 3da236e..060a731 100644
--- a/FabAccessAPI/Exceptions/InvalidCredentialsException.cs
+++ b/FabAccessAPI/Exceptions/SASL/InvalidCredentialsException.cs
@@ -1,6 +1,6 @@
using System;
-namespace FabAccessAPI.Exceptions
+namespace FabAccessAPI.Exceptions.SASL
{
public class InvalidCredentialsException : Exception
{
diff --git a/FabAccessAPI/Exceptions/TimeoutException.cs b/FabAccessAPI/Exceptions/TimeoutException.cs
new file mode 100644
index 0000000..2f65b1d
--- /dev/null
+++ b/FabAccessAPI/Exceptions/TimeoutException.cs
@@ -0,0 +1,25 @@
+using System;
+
+namespace FabAccessAPI.Exceptions
+{
+ ///
+ /// Timeout on Connection
+ ///
+ public class TimeoutException : Exception
+ {
+ public TimeoutException()
+ {
+
+ }
+
+ public TimeoutException(string message) : base(message)
+ {
+
+ }
+
+ public TimeoutException(string message, Exception inner) : base(message, inner)
+ {
+
+ }
+ }
+}
diff --git a/FabAccessAPI/IAPI.cs b/FabAccessAPI/IAPI.cs
index 62fefa2..56cc8a3 100644
--- a/FabAccessAPI/IAPI.cs
+++ b/FabAccessAPI/IAPI.cs
@@ -1,63 +1,84 @@
-using Capnp.Rpc;
-using FabAccessAPI.Schema;
-using System;
+using System;
using System.Threading.Tasks;
+using Capnp.Rpc;
+using FabAccessAPI.Exceptions;
+using FabAccessAPI.Schema;
namespace FabAccessAPI
{
+ ///
+ /// Service to connect to a server and maintain the connection
+ ///
public interface IAPI
{
+ #region Information about a connection and the server
///
- /// Data to establish connection.
+ /// State of the conneciton, is the API-Service connecting to a server
///
- /// Without SecretProperties
- ConnectionData ConnectionData { get; }
+ bool IsConnecting { get; }
///
- /// Information about the established connection.
- ///
- ConnectionInfo ConnectionInfo { get; }
-
- ///
- /// Is API connected?
+ /// State of the conneciton, is the API-Service connected to a server
///
bool IsConnected { get; }
///
- /// Get session when connection is established
+ /// Information about the connection
///
- Session Session { get; }
+ /// When API-Service is not connected or trying to connected to a server
+ ConnectionData ConnectionData { get; }
///
- /// Event on changes in connection state.
+ /// Information about the server
+ /// Is only avalible if the API-Service is connected
///
- event EventHandler ConnectionStatusChanged;
+ /// When API-Service is not connected
+ ServerData ServerData { get; }
+ #endregion
+ #region Methods to connect to server
///
- /// Unbind all Events from ConnectionStatus Change
+ /// Connect to server with ConnectionData
+ /// If connection lost, the API-Server will try to reconnect
///
- void UnbindAllEvents();
-
- ///
- /// Connect to BFFH Server
- ///
- ///
+ /// Data to establish a connection to a server
+ /// When API-Service can not connect to a server
+ /// When API-Service can connect to a server but can not authenticate
+ /// When API-Service is allready connected
Task Connect(ConnectionData connectionData, TcpRpcClient tcpRpcClient = null);
///
- /// Disconnect from BFFH Server
+ /// Disconnect from a server
///
+ /// When API-Service is not connected or trying to connect
Task Disconnect();
///
- /// Reconnect after connection loss with the last ConnectionData
+ /// Try to connect to a server and get ServerData
+ /// The connection is not maintained
///
- Task Reconnect();
+ /// When API-Service can not connect to a server
+ Task TryToConnect(ConnectionData connectionData, TcpRpcClient tcpRpcClient = null);
+ #endregion
+
+ #region Session
+ ///
+ /// Get session after connection
+ ///
+ /// When API-Service is not connected
+ Session Session { get; }
+ #endregion
+
+ #region Events
+ ///
+ /// Event on changes in connection status
+ ///
+ event EventHandler ConnectionStatusChanged;
///
- /// Connect to Server and get ConnectionInfo.
- /// The Connection is not maintained.
+ /// Unbind all handlers from EventHandler
///
- Task TestConnection(ConnectionData connectionData, TcpRpcClient tcpRpcClient = null);
+ void UnbindEventHandler();
+ #endregion
}
}
diff --git a/FabAccessAPI/Mechanism.cs b/FabAccessAPI/Mechanism.cs
deleted file mode 100644
index 96a4e53..0000000
--- a/FabAccessAPI/Mechanism.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-using System;
-
-namespace FabAccessAPI
-{
- public enum Mechanism
- {
- PLAIN,
- }
-
- public static class MechanismString
- {
- public static string ToString(Mechanism mechanism)
- {
- switch(mechanism)
- {
- case Mechanism.PLAIN:
- return "PLAIN";
- default:
- throw new ArgumentException("Mechanism not known.");
- }
- }
- }
-
-}
diff --git a/FabAccessAPI/SASLMechanismEnum.cs b/FabAccessAPI/SASLMechanismEnum.cs
new file mode 100644
index 0000000..cc12c7c
--- /dev/null
+++ b/FabAccessAPI/SASLMechanismEnum.cs
@@ -0,0 +1,24 @@
+using System;
+
+namespace FabAccessAPI
+{
+ public enum SASLMechanismEnum
+ {
+ PLAIN,
+ }
+
+ public static class SASLMechanism
+ {
+ public static string ToString(SASLMechanismEnum mechanism)
+ {
+ switch(mechanism)
+ {
+ case SASLMechanismEnum.PLAIN:
+ return "PLAIN";
+ default:
+ throw new ArgumentException("Mechanism unknown.");
+ }
+ }
+ }
+
+}
diff --git a/FabAccessAPI/ConnectionInfo.cs b/FabAccessAPI/ServerData.cs
similarity index 88%
rename from FabAccessAPI/ConnectionInfo.cs
rename to FabAccessAPI/ServerData.cs
index 33c65e9..9601820 100644
--- a/FabAccessAPI/ConnectionInfo.cs
+++ b/FabAccessAPI/ServerData.cs
@@ -3,7 +3,7 @@ using System.Collections.Generic;
namespace FabAccessAPI
{
- public class ConnectionInfo
+ public class ServerData
{
public Schema.Version APIVersion;
public string ServerName;
diff --git a/FabAccessAPI_Test/API_Test.cs b/FabAccessAPI_Test/API_Test.cs
index 349fc89..d33b0fd 100644
--- a/FabAccessAPI_Test/API_Test.cs
+++ b/FabAccessAPI_Test/API_Test.cs
@@ -1,6 +1,7 @@
using Capnp.Rpc;
using FabAccessAPI;
using FabAccessAPI.Exceptions;
+using FabAccessAPI.Exceptions.SASL;
using NUnit.Framework;
using System;
using System.Collections.Generic;
@@ -19,7 +20,7 @@ namespace FabAccessAPI_Test
ConnectionData connectionData = new ConnectionData()
{
Host = new UriBuilder(TestEnv.SCHEMA, "UnkownHost" + TestEnv.TESTSERVER, TestEnv.TESTSERVER_PORT).Uri,
- Mechanism = Mechanism.PLAIN,
+ Mechanism = SASLMechanismEnum.PLAIN,
Username = "UnkownUser",
Properties = new Dictionary()
{
@@ -32,14 +33,14 @@ namespace FabAccessAPI_Test
{
await api.Connect(connectionData);
}
- catch (ConnectingFailedException)
+ catch (ConnectionException)
{
Assert.Pass();
}
Assert.Fail();
}
- [Test, Ignore("")]
+ [Test]
public async Task Connect_InvalidCredentials()
{
API api = new API();
@@ -50,7 +51,7 @@ namespace FabAccessAPI_Test
{
await api.Connect(connectionData);
}
- catch(InvalidCredentialsException)
+ catch(AuthenticationException exception) when (exception.InnerException is InvalidCredentialsException)
{
Assert.Pass();
}
@@ -68,11 +69,11 @@ namespace FabAccessAPI_Test
bool event_Disconnected = false;
api.ConnectionStatusChanged += (sender, eventArgs) =>
{
- if (eventArgs == ConnectionStatusChange.Connected)
+ if (eventArgs == ConnectionStatusChanged.Connected)
{
event_Connected = true;
}
- if(eventArgs == ConnectionStatusChange.Disconnected)
+ if(eventArgs == ConnectionStatusChanged.Disconnected)
{
event_Disconnected = true;
}
@@ -82,7 +83,7 @@ namespace FabAccessAPI_Test
bool HasSesion = api.Session != null;
bool HasConnectionData = api.ConnectionData != null;
- bool HasConnectionInfo = api.ConnectionInfo != null;
+ bool HasServerData = api.ServerData != null;
bool IsConnected = api.IsConnected;
await api.Disconnect();
@@ -95,7 +96,7 @@ namespace FabAccessAPI_Test
Assert.IsTrue(HasSesion, "HasSesion");
Assert.IsTrue(HasConnectionData, "HasConnectionData");
- Assert.IsTrue(HasConnectionInfo, "HasConnectionInfo");
+ Assert.IsTrue(HasServerData, "HasServerData");
Assert.IsTrue(IsConnected, "IsConnected");
Assert.IsFalse(api.IsConnected, "api.IsConnected");
@@ -109,9 +110,9 @@ namespace FabAccessAPI_Test
ConnectionData connectionData = TestEnv.CreateConnetionData(username);
- ConnectionInfo connectionInfo = await api.TestConnection(connectionData);
+ ServerData serverData = await api.TryToConnect(connectionData);
- Assert.IsNotNull(connectionInfo);
+ Assert.IsNotNull(serverData);
}
[TestCase("Admin1"), Explicit]
@@ -121,28 +122,23 @@ namespace FabAccessAPI_Test
ConnectionData connectionData = TestEnv.CreateConnetionData(username);
- bool event_Connected = false;
- bool event_ConnectionLoss = false;
- bool event_Reconnect = false;
- bool event_Disconnected = false;
+ int event_Connected = 0;
+ int event_ConnectionLoss = 0;
+ int event_Disconnected = 0;
api.ConnectionStatusChanged += (sender, eventArgs) =>
{
- if (eventArgs == ConnectionStatusChange.Connected)
+ if (eventArgs == ConnectionStatusChanged.Connected)
{
- event_Connected = true;
+ event_Connected++;
}
- if (eventArgs == ConnectionStatusChange.ConnectionLoss)
+ if (eventArgs == ConnectionStatusChanged.ConnectionLoss)
{
- event_ConnectionLoss = true;
+ event_ConnectionLoss++;
}
- if (eventArgs == ConnectionStatusChange.Reconnected)
+ if (eventArgs == ConnectionStatusChanged.Disconnected)
{
- event_Reconnect = true;
- }
- if (eventArgs == ConnectionStatusChange.Disconnected)
- {
- event_Disconnected = true;
+ event_Disconnected++;
}
};
@@ -161,16 +157,14 @@ namespace FabAccessAPI_Test
Thread.Sleep(3000);
// Stop here and connect with internet again
- await api.Reconnect();
await api.Disconnect();
- Thread.Sleep(3000);
+ Thread.Sleep(1000);
Assert.Multiple(() =>
{
- Assert.IsTrue(event_Connected, "event_Connected");
- Assert.IsTrue(event_ConnectionLoss, "event_ConnectionLoss");
- Assert.IsTrue(event_Reconnect, "event_Reconnect");
- Assert.IsTrue(event_Disconnected, "event_Disconnected");
+ Assert.AreEqual(2, event_Connected, "event_Connected");
+ Assert.AreEqual(1, event_ConnectionLoss, "event_ConnectionLoss");
+ Assert.AreEqual(1, event_Disconnected, "event_Disconnected");
});
}
diff --git a/FabAccessAPI_Test/API_TestEnv/User_Test.cs b/FabAccessAPI_Test/API_TestEnv/User_Test.cs
index f632af9..9b1ad0c 100644
--- a/FabAccessAPI_Test/API_TestEnv/User_Test.cs
+++ b/FabAccessAPI_Test/API_TestEnv/User_Test.cs
@@ -34,6 +34,7 @@ namespace FabAccessAPI_Test.API_TestEnv
[TestCase("Admin1", "NewMakerA1", "UseA", "ReadA", "DiscloseA")]
[Order(1)]
+ [Ignore("Deprecated")]
public async Task AddRoles(string username, string username2, params string[] roles)
{
API api = new API();
diff --git a/FabAccessAPI_Test/TestEnv.cs b/FabAccessAPI_Test/TestEnv.cs
index b83e951..d702b75 100644
--- a/FabAccessAPI_Test/TestEnv.cs
+++ b/FabAccessAPI_Test/TestEnv.cs
@@ -7,8 +7,8 @@ namespace FabAccessAPI_Test
public static class TestEnv
{
public const string SCHEMA = "fabaccess";
- public const string TESTSERVER = "127.0.0.1";//"bffh.lab.bln.kjknet.de";
- public const int TESTSERVER_PORT = 59666;
+ public const string TESTSERVER = "127.0.0.1";//"test.fab-access.org";
+ public const int TESTSERVER_PORT = 59661;
public const string PASSWORD = "secret";
public static ConnectionData CreateConnetionData(string username)
@@ -16,7 +16,7 @@ namespace FabAccessAPI_Test
ConnectionData connectionData = new ConnectionData()
{
Host = new UriBuilder(TestEnv.SCHEMA, TestEnv.TESTSERVER, TestEnv.TESTSERVER_PORT).Uri,
- Mechanism = Mechanism.PLAIN,
+ Mechanism = SASLMechanismEnum.PLAIN,
Username = username,
Properties = new Dictionary()
{