fe2a6db786
rust / build / test / clippy / fmt (push) Has been cancelled
Layout:
- src/ .NET 10 x64 reference: MxNativeCodec, MxNativeClient,
MxAsbClient, probes, tests, harnesses. Executable spec.
- design/ Architectural plan for the Rust port (M0–M6), error
model, protocol invariants, risks (R1–R16), adversarial
review log (review.md).
- rust/ Rust workspace. M0 skeleton + M1 codec parity.
mxaccess-codec: 215 unit tests + 2 cross-implementation
parity tests (byte-identical against .NET reference).
Other crates are M0 stubs awaiting M2+.
- captures/ Frida + netsh + pcap evidence per CLAUDE.md
("captures are evidence, not throwaway logs").
- analysis/ Decompiled C# (frida/proxy/decompiled-*),
Ghidra exports for native DLLs (`exports/` only —
working state at `projects/` and AVEVA's input
binaries at `input/` are gitignored).
- docs/ Reverse-engineering reference docs.
- tools/ Setup-LiveProbeEnv.ps1 (Infisical credential fetcher),
Compute-Crc.ps1 (.NET parity helper).
- .github/workflows/ Rust CI: fmt + build + test + clippy on Windows.
- LICENSE MIT (Joseph Doherty, 2026).
Verified:
- cargo test --workspace → 217 passed (215 unit + 2 .NET parity), 0 failed
- cargo clippy --workspace -- -D warnings → clean
- cargo fmt --all -- --check → clean
- cargo publish --dry-run -p mxaccess-codec → packages cleanly
Excluded from history (see .gitignore):
- **/bin, **/obj, **/target — build artifacts
- analysis/ghidra/projects/ — Ghidra working state (regenerable)
- analysis/ghidra/input/ — AVEVA proprietary DLLs (vendor IP)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
662 lines
23 KiB
C#
662 lines
23 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections.ObjectModel;
|
|
using System.Globalization;
|
|
using System.Linq;
|
|
using System.Net;
|
|
using System.ServiceModel;
|
|
using System.ServiceModel.Channels;
|
|
using System.ServiceModel.Discovery;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using System.Xml;
|
|
using ArchestrAServices.Common;
|
|
using ArchestrAServices.Proxy;
|
|
using Asb.Base.V2.ContractTypes;
|
|
|
|
namespace Asb.Base.V2;
|
|
|
|
public class BaseV2Client<T> : IBaseV2, IDisposable where T : class, IAuthenticateAsb
|
|
{
|
|
private readonly object connectionLock = new object();
|
|
|
|
private readonly string connectionUser = string.Empty;
|
|
|
|
private readonly string connectionApplication = string.Empty;
|
|
|
|
private readonly TimeSpan timeToWaitForConnection = DefaultTimeToWaitForConnection;
|
|
|
|
private IClientManagement clientManager;
|
|
|
|
private long connectionInProgress;
|
|
|
|
private ConnectContext<T> successfulConnectionResult;
|
|
|
|
private ManualResetEvent connectionEstablishedEvent;
|
|
|
|
private bool disposed;
|
|
|
|
public static TimeSpan DefaultTimeToWaitForConnection => TimeSpan.FromSeconds(20.0);
|
|
|
|
public SecureCommunicationModes SecureCommunicationMode { get; set; }
|
|
|
|
public DateTime ServiceLoadTime { get; set; }
|
|
|
|
public bool Connected
|
|
{
|
|
get
|
|
{
|
|
if (successfulConnectionResult?.ServiceChannel != null)
|
|
{
|
|
return successfulConnectionResult.ServiceChannel.State == CommunicationState.Opened;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public CommunicationState State
|
|
{
|
|
get
|
|
{
|
|
if (successfulConnectionResult != null && successfulConnectionResult.ServiceChannel != null)
|
|
{
|
|
return successfulConnectionResult.ServiceChannel.State;
|
|
}
|
|
return CommunicationState.Closed;
|
|
}
|
|
}
|
|
|
|
public ConnectionState DownstreamConnectionState { get; protected set; }
|
|
|
|
internal ConnectContext<T> Context => successfulConnectionResult;
|
|
|
|
protected T ChannelClient => successfulConnectionResult.ServiceClient;
|
|
|
|
public Guid ConnectionId
|
|
{
|
|
get
|
|
{
|
|
if (successfulConnectionResult != null)
|
|
{
|
|
return successfulConnectionResult.ConnectionId;
|
|
}
|
|
return Guid.Empty;
|
|
}
|
|
set
|
|
{
|
|
}
|
|
}
|
|
|
|
public BaseV2Client()
|
|
{
|
|
clientManager = new ClientManagement();
|
|
SecureCommunicationMode = RegistryHandler.SecureCommunicationMode;
|
|
}
|
|
|
|
public BaseV2Client(TimeSpan connectTimeout)
|
|
: this()
|
|
{
|
|
if (connectTimeout.TotalMilliseconds > 2147483647.0 || connectTimeout < TimeSpan.Zero)
|
|
{
|
|
throw new ArgumentOutOfRangeException("connectTimeout");
|
|
}
|
|
timeToWaitForConnection = connectTimeout;
|
|
}
|
|
|
|
internal BaseV2Client(TimeSpan connectTimeout, IClientManagement manager)
|
|
: this(manager)
|
|
{
|
|
if (connectTimeout.TotalMilliseconds > 2147483647.0 || connectTimeout < TimeSpan.Zero)
|
|
{
|
|
throw new ArgumentOutOfRangeException("connectTimeout");
|
|
}
|
|
timeToWaitForConnection = connectTimeout;
|
|
}
|
|
|
|
internal BaseV2Client(TimeSpan connectTimeout, IClientManagement manager, string application, string user)
|
|
: this(connectTimeout, manager)
|
|
{
|
|
connectionApplication = application;
|
|
connectionUser = user;
|
|
}
|
|
|
|
public BaseV2Client(TimeSpan connectTimeout, string application, string user)
|
|
: this(connectTimeout)
|
|
{
|
|
connectionApplication = application;
|
|
connectionUser = user;
|
|
}
|
|
|
|
internal BaseV2Client(IClientManagement manager)
|
|
{
|
|
clientManager = manager;
|
|
SecureCommunicationMode = RegistryHandler.SecureCommunicationMode;
|
|
}
|
|
|
|
public bool Connect(EndpointDiscoveryMetadata selectedMetadata, string solutionName, ClientAccess access, Action<string> errorCallback)
|
|
{
|
|
return Connect(selectedMetadata, solutionName, access, errorCallback, useSecureConnection: false);
|
|
}
|
|
|
|
public bool Connect(EndpointDiscoveryMetadata selectedMetadata, string solutionName, ClientAccess access, Action<string> errorCallback, bool useSecureConnection)
|
|
{
|
|
ServiceTrace.LogCsv("Connect (endpoint discovered) entry");
|
|
ServiceTrace.LogResume("Connect (endpoint discovered) entry");
|
|
if (Interlocked.CompareExchange(ref connectionInProgress, 1L, 0L) != 0L)
|
|
{
|
|
string text = string.Format(CultureInfo.InvariantCulture, "Connect: another connection is in progress, cannot attempt a connection using selected discovery metadata");
|
|
ServiceTrace.LogError(text);
|
|
errorCallback?.Invoke(text);
|
|
return false;
|
|
}
|
|
try
|
|
{
|
|
bool flag = EstablishConnection(selectedMetadata, solutionName, access, errorCallback, useSecureConnection);
|
|
ServiceTrace.LogSuspend("Connect (endpoint discovered) exit, {0}", flag ? "succeeded" : "failed");
|
|
ServiceTrace.LogCsv("Connect (endpoint discovered) exit [success]", flag);
|
|
return flag;
|
|
}
|
|
finally
|
|
{
|
|
Interlocked.Exchange(ref connectionInProgress, 0L);
|
|
}
|
|
}
|
|
|
|
public void Disconnect()
|
|
{
|
|
ServiceTrace.LogCsv("Disconnect entry");
|
|
ServiceTrace.LogResume("Disconnect entry");
|
|
if (successfulConnectionResult == null || !successfulConnectionResult.Success)
|
|
{
|
|
return;
|
|
}
|
|
SystemAuthenticationClientAuthentication.DisconnectSecureSession(successfulConnectionResult.ConnectionId, delegate(DisconnectRequest request)
|
|
{
|
|
if (successfulConnectionResult != null && successfulConnectionResult.Success)
|
|
{
|
|
if (successfulConnectionResult.ServiceClient != null && successfulConnectionResult.ServiceChannel != null && successfulConnectionResult.ServiceChannel.State != CommunicationState.Faulted)
|
|
{
|
|
ServiceTrace.LogCsv("Calling WCF channel Disconnect method");
|
|
successfulConnectionResult.ServiceClient.Disconnect(request);
|
|
ServiceTrace.LogCsv("Returned from WCF channel Disconnect method");
|
|
}
|
|
Reset();
|
|
}
|
|
});
|
|
DownstreamConnectionState = ConnectionState.Unknown;
|
|
ServiceTrace.LogSuspend("Disconnect exit");
|
|
ServiceTrace.LogCsv("Disconnect exit");
|
|
}
|
|
|
|
public void Abort()
|
|
{
|
|
Reset();
|
|
}
|
|
|
|
internal void InjectClientManager(IClientManagement manager)
|
|
{
|
|
clientManager = manager;
|
|
}
|
|
|
|
protected static ReadOnlyCollection<EndpointDiscoveryMetadata> DiscoverEndpoints(XmlQualifiedName contract, string scopeRule)
|
|
{
|
|
return DiscoverEndpoints(contract, scopeRule, null);
|
|
}
|
|
|
|
protected static ReadOnlyCollection<EndpointDiscoveryMetadata> DiscoverEndpoints(XmlQualifiedName contract, string scopeRule, bool? useSecureConnection)
|
|
{
|
|
List<EndpointDiscoveryMetadata> list = new ClientManagement().DiscoverEndpointsForContract(contract, scopeRule)?.ToList();
|
|
if (list == null)
|
|
{
|
|
ServiceTrace.LogWarning(string.Format(CultureInfo.InvariantCulture, "DiscoverEndpoints: null FindResponse finding contract {0} with access name {1}", new object[2] { contract.Name, scopeRule }));
|
|
return new ReadOnlyCollection<EndpointDiscoveryMetadata>(new EndpointDiscoveryMetadata[0]);
|
|
}
|
|
if (list.Count == 0)
|
|
{
|
|
ServiceTrace.LogInfo(string.Format(CultureInfo.InvariantCulture, "DiscoverEndpoints found no endpoints for contract {0} with access name {1}", new object[2] { contract.Name, scopeRule }));
|
|
return new ReadOnlyCollection<EndpointDiscoveryMetadata>(new EndpointDiscoveryMetadata[0]);
|
|
}
|
|
return new ReadOnlyCollection<EndpointDiscoveryMetadata>(list);
|
|
}
|
|
|
|
protected bool Connect(XmlQualifiedName contract, string scopeRule, ClientAccess access, Action<string> errorCallback)
|
|
{
|
|
return Connect(contract, scopeRule, access, errorCallback, useSecureConnection: false);
|
|
}
|
|
|
|
protected bool Connect(XmlQualifiedName contract, string scopeRule, ClientAccess access, Action<string> errorCallback, bool useSecureConnection)
|
|
{
|
|
ServiceTrace.LogCsv("Connect (discover endpoint) entry");
|
|
ServiceTrace.LogResume("Connect (discover endpoint) entry");
|
|
if (contract == null)
|
|
{
|
|
string text = string.Format(CultureInfo.InvariantCulture, "Connect: no contract provided, no connection is possible");
|
|
ServiceTrace.LogError(text);
|
|
errorCallback?.Invoke(text);
|
|
return false;
|
|
}
|
|
if (Interlocked.CompareExchange(ref connectionInProgress, 1L, 0L) != 0L)
|
|
{
|
|
string text2 = string.Format(CultureInfo.InvariantCulture, "Connect: another connection is in progress, cannot attempt a connection with a specified contract and access name");
|
|
ServiceTrace.LogError(text2);
|
|
errorCallback?.Invoke(text2);
|
|
return false;
|
|
}
|
|
try
|
|
{
|
|
List<EndpointDiscoveryMetadata> list = clientManager.DiscoverEndpointsForContract(contract, scopeRule)?.ToList();
|
|
if (list == null)
|
|
{
|
|
string text3 = string.Format(CultureInfo.InvariantCulture, "Connect: null FindResponse finding contract {0} with access name {1}", new object[2] { contract.Name, scopeRule });
|
|
ServiceTrace.LogError(text3);
|
|
errorCallback?.Invoke(text3);
|
|
return false;
|
|
}
|
|
if (list.Count == 0)
|
|
{
|
|
string text4 = string.Format(CultureInfo.InvariantCulture, "Connect found no endpoints for contract {0} with access name {1}", new object[2] { contract.Name, scopeRule });
|
|
ServiceTrace.LogError(text4);
|
|
errorCallback?.Invoke(text4);
|
|
return false;
|
|
}
|
|
EndpointDiscoveryMetadata selectedMetadata;
|
|
if (list.Count > 1)
|
|
{
|
|
Random random = new Random(DateTime.Now.Millisecond);
|
|
selectedMetadata = list[random.Next(list.Count)];
|
|
}
|
|
else
|
|
{
|
|
selectedMetadata = list[0];
|
|
}
|
|
bool flag = EstablishConnection(selectedMetadata, string.Empty, access, errorCallback, useSecureConnection);
|
|
ServiceTrace.LogSuspend("Connect (discover endpoint) exit, {0}", flag ? "succeeded" : "failed");
|
|
ServiceTrace.LogCsv("Connect (discover endpoint) exit [success]", flag);
|
|
return flag;
|
|
}
|
|
finally
|
|
{
|
|
Interlocked.Exchange(ref connectionInProgress, 0L);
|
|
}
|
|
}
|
|
|
|
protected UserTokenContract EncryptUserToken(UserToken originalToken)
|
|
{
|
|
if (originalToken == null)
|
|
{
|
|
return null;
|
|
}
|
|
UserTokenContract userTokenContract = new UserTokenContract
|
|
{
|
|
Encryption = originalToken.Encryption,
|
|
HostName = originalToken.HostName,
|
|
IdType = originalToken.IdType,
|
|
LocationId = originalToken.LocationId,
|
|
UserName = originalToken.UserName,
|
|
Validity = originalToken.Validity
|
|
};
|
|
SystemAuthenticationClientAuthentication clientAuthenticator = SysAuthenticatorClientCache.GetClientAuthenticator(successfulConnectionResult.ConnectionId);
|
|
if (clientAuthenticator == null || !clientAuthenticator.SecureSessionEstablished)
|
|
{
|
|
throw new InvalidOperationException("Cannot encrypt user token due to session in wrong state");
|
|
}
|
|
if (originalToken.Password != null)
|
|
{
|
|
byte[] bytes = Encoding.UTF8.GetBytes(originalToken.Password);
|
|
userTokenContract.Password = clientAuthenticator.Encypher(bytes, clientAuthenticator.EncryptionKey);
|
|
}
|
|
if (originalToken.SamlToken != null)
|
|
{
|
|
userTokenContract.SamlToken = clientAuthenticator.Encypher(originalToken.SamlToken, clientAuthenticator.EncryptionKey);
|
|
}
|
|
if (originalToken.JwtToken != null)
|
|
{
|
|
byte[] bytes2 = Encoding.UTF8.GetBytes(originalToken.JwtToken);
|
|
userTokenContract.JwtToken = clientAuthenticator.Encypher(bytes2, clientAuthenticator.EncryptionKey);
|
|
}
|
|
if (originalToken.X509Certificate != null)
|
|
{
|
|
userTokenContract.X509Certificate = clientAuthenticator.Encypher(originalToken.X509Certificate, clientAuthenticator.EncryptionKey);
|
|
}
|
|
return userTokenContract;
|
|
}
|
|
|
|
protected void Reset()
|
|
{
|
|
successfulConnectionResult?.Dispose();
|
|
successfulConnectionResult = null;
|
|
}
|
|
|
|
private bool EstablishConnection(EndpointDiscoveryMetadata selectedMetadata, string solutionName, ClientAccess access, Action<string> errorCallback, bool useSecureConnection)
|
|
{
|
|
ServiceTrace.LogCsv("CreateConnectContext entry");
|
|
ServiceTrace.LogResume("CreateConnectContext entry");
|
|
if (selectedMetadata == null)
|
|
{
|
|
errorCallback?.Invoke("No discovery endpoint metadata provided, cannot connect");
|
|
return false;
|
|
}
|
|
if (string.IsNullOrWhiteSpace(solutionName))
|
|
{
|
|
solutionName = new ASBSolutionManager().GetASBSolutionName(selectedMetadata, out var errorMessage);
|
|
if (!string.IsNullOrEmpty(errorMessage))
|
|
{
|
|
errorCallback?.Invoke(errorMessage);
|
|
return false;
|
|
}
|
|
}
|
|
lock (connectionLock)
|
|
{
|
|
successfulConnectionResult = null;
|
|
connectionEstablishedEvent = new ManualResetEvent(initialState: false);
|
|
}
|
|
using (CancellationTokenSource cancellationTokenSource = new CancellationTokenSource())
|
|
{
|
|
SpinConnectionTasks(selectedMetadata.ListenUris, solutionName, access, errorCallback, useSecureConnection, cancellationTokenSource.Token);
|
|
bool flag = connectionEstablishedEvent.WaitOne(timeToWaitForConnection);
|
|
ServiceTrace.LogVerbose("{0} task connected with the service", flag ? "A" : "No");
|
|
cancellationTokenSource.Cancel();
|
|
}
|
|
bool result;
|
|
lock (connectionLock)
|
|
{
|
|
connectionEstablishedEvent.Dispose();
|
|
connectionEstablishedEvent = null;
|
|
result = true;
|
|
if (successfulConnectionResult == null)
|
|
{
|
|
result = false;
|
|
errorCallback?.Invoke("No connection was established with a service endpoint");
|
|
}
|
|
}
|
|
ServiceTrace.LogSuspend("CreateConnectContext exit");
|
|
ServiceTrace.LogCsv("CreateConnectContext exit");
|
|
return result;
|
|
}
|
|
|
|
private void SpinConnectionTasks(Collection<Uri> listenUris, string solutionName, ClientAccess access, Action<string> errorCallback, bool useSecureConnection, CancellationToken cancellationToken)
|
|
{
|
|
ServiceTrace.LogVerbose("Spinning tasks for {0} listen Uris", listenUris.Count);
|
|
foreach (Uri endpointUri in listenUris)
|
|
{
|
|
string selectedEndpointUri = endpointUri.ToString();
|
|
Task.Factory.StartNew(delegate
|
|
{
|
|
NetTcpBindingSecurityMode securityMode = (useSecureConnection ? NetTcpBindingSecurityMode.CertificateEncryption : NetTcpBindingSecurityMode.None);
|
|
Binding binding = SvcUtilities.GetBinding(selectedEndpointUri, securityMode);
|
|
binding.OpenTimeout = new TimeSpan(0, 10, 0);
|
|
binding.ReceiveTimeout = new TimeSpan(0, 10, 0);
|
|
binding.SendTimeout = new TimeSpan(0, 10, 0);
|
|
binding.CloseTimeout = new TimeSpan(0, 10, 0);
|
|
EndpointAddress serviceEndpointAddress = new EndpointAddress(selectedEndpointUri);
|
|
if (useSecureConnection)
|
|
{
|
|
IPHostEntry hostEntry = Dns.GetHostEntry(endpointUri.Host);
|
|
serviceEndpointAddress = new EndpointAddress(endpointUri, EndpointIdentity.CreateDnsIdentity(hostEntry.HostName));
|
|
}
|
|
InternalConnect(serviceEndpointAddress, binding, solutionName, access, errorCallback, cancellationToken);
|
|
}, cancellationToken, TaskCreationOptions.None, TaskScheduler.Default);
|
|
}
|
|
}
|
|
|
|
private void InternalConnect(EndpointAddress serviceEndpointAddress, Binding binding, string solutionName, ClientAccess access, Action<string> errorCallback, CancellationToken cancellationToken)
|
|
{
|
|
if (serviceEndpointAddress == null)
|
|
{
|
|
throw new ArgumentNullException("serviceEndpointAddress");
|
|
}
|
|
if (binding == null)
|
|
{
|
|
throw new ArgumentNullException("binding");
|
|
}
|
|
if (string.IsNullOrEmpty(solutionName))
|
|
{
|
|
throw new ArgumentNullException("solutionName");
|
|
}
|
|
try
|
|
{
|
|
EstablishChannelConnection(serviceEndpointAddress, binding, solutionName, access, errorCallback, cancellationToken);
|
|
}
|
|
catch (CommunicationException ex)
|
|
{
|
|
ServiceTrace.LogWarning("BaseV2Client caught CommunicationException opening channel: " + ex.Message);
|
|
errorCallback?.Invoke("BaseV2Client caught CommunicationException opening channel: " + ex.Message);
|
|
if (ex.InnerException != null)
|
|
{
|
|
ServiceTrace.LogWarning(" " + ex.InnerException.Message);
|
|
errorCallback?.Invoke(" " + ex.InnerException.Message);
|
|
}
|
|
}
|
|
catch (TimeoutException ex2)
|
|
{
|
|
ServiceTrace.LogWarning("BaseV2Client caught TimeoutException opening channel: " + ex2.Message);
|
|
errorCallback?.Invoke("BaseV2Client caught TimeoutException opening channel: " + ex2.Message);
|
|
if (ex2.InnerException != null)
|
|
{
|
|
ServiceTrace.LogWarning(" " + ex2.InnerException.Message);
|
|
errorCallback?.Invoke(" " + ex2.InnerException.Message);
|
|
}
|
|
}
|
|
catch (Exception ex3)
|
|
{
|
|
ServiceTrace.LogWarning("BaseV2Client caught exception opening channel: " + ex3.Message);
|
|
errorCallback?.Invoke("BaseV2Client caught exception opening channel: " + ex3.Message);
|
|
}
|
|
}
|
|
|
|
private void EstablishChannelConnection(EndpointAddress serviceEndpointAddress, Binding binding, string solutionName, ClientAccess access, Action<string> errorCallback, CancellationToken cancellationToken)
|
|
{
|
|
ConnectContext<T> connectContext = clientManager.CreateConnectContext<T>(serviceEndpointAddress, binding, connectionApplication, connectionUser, cancellationToken);
|
|
if ((connectContext != null && connectContext.ServiceChannelFactory?.State == CommunicationState.Closed) || (connectContext != null && connectContext.ServiceChannel?.State == CommunicationState.Closed))
|
|
{
|
|
return;
|
|
}
|
|
if (clientManager.EstablishSecureSession(connectContext, solutionName, access))
|
|
{
|
|
ServiceTrace.LogVerbose("Secure connection with endpoint {0} is established using solution {1}", serviceEndpointAddress.Uri.AbsoluteUri, solutionName);
|
|
bool flag = false;
|
|
lock (connectionLock)
|
|
{
|
|
if (successfulConnectionResult == null)
|
|
{
|
|
flag = true;
|
|
successfulConnectionResult = connectContext;
|
|
ServiceTrace.LogVerbose("Secure connection with endpoint {0} using solution {1} is first, signal success", serviceEndpointAddress.Uri.AbsoluteUri, solutionName);
|
|
connectionEstablishedEvent?.Set();
|
|
}
|
|
}
|
|
if (!flag)
|
|
{
|
|
ServiceTrace.LogVerbose("Secure connection with endpoint {0} using solution {1} is NOT first, disconnect", serviceEndpointAddress.Uri.AbsoluteUri, solutionName);
|
|
clientManager.DisconnectSecureSession(connectContext);
|
|
connectContext.Dispose();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (connectContext != null && !string.IsNullOrEmpty(connectContext.ErrorMessage))
|
|
{
|
|
errorCallback?.Invoke(connectContext.ErrorMessage);
|
|
}
|
|
ServiceTrace.LogVerbose("Connection with endpoint {0} using solution {1} rejected", serviceEndpointAddress.Uri.AbsoluteUri, solutionName);
|
|
if (connectionEstablishedEvent != null)
|
|
{
|
|
connectionEstablishedEvent.Set();
|
|
}
|
|
}
|
|
}
|
|
|
|
public void OnConnect(ulong timeout, ConsumerMetadata metadata)
|
|
{
|
|
ServiceTrace.LogCsv("OnConnect entry");
|
|
ServiceTrace.LogResume("OnConnect entry");
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public ArchestrAResult KeepAlive()
|
|
{
|
|
ServiceTrace.LogCsv("KeepAlive entry");
|
|
ServiceTrace.LogResume("KeepAlive entry");
|
|
ArchestrAResult archestrAResult = PrepareToSend(new KeepAliveRequest(), delegate(ConnectedRequest request, T channelClient)
|
|
{
|
|
ServiceTrace.LogVerbose("Calling WCF channel KeepAlive method");
|
|
KeepAliveResponse keepAliveResponse = channelClient.KeepAlive((KeepAliveRequest)request);
|
|
ServiceTrace.LogVerbose("Returned from WCF channel KeepAlive method");
|
|
if (keepAliveResponse == null)
|
|
{
|
|
return ArchestrAResult.MakeResult(ArchestrAError.BadNoCommunication, 0).AddErrorMessage("No response from the service");
|
|
}
|
|
UpdateDownstreamConnectionState(keepAliveResponse);
|
|
return keepAliveResponse.Result;
|
|
});
|
|
ServiceTrace.LogSuspend("KeepAlive exit with {0}", archestrAResult.Success ? "success" : "failure");
|
|
ServiceTrace.LogCsv("KeepAlive exit [success]", archestrAResult.Success);
|
|
return archestrAResult;
|
|
}
|
|
|
|
public ArchestrAResult GetStatusItems(out string[] items)
|
|
{
|
|
ServiceTrace.LogCsv("GetStatusItems entry");
|
|
ServiceTrace.LogResume("GetStatusItems entry");
|
|
string[] receivedItems = null;
|
|
ArchestrAResult archestrAResult = PrepareToSend(new GetStatusItemsRequest(), delegate(ConnectedRequest request, T channelClient)
|
|
{
|
|
ServiceTrace.LogCsv("Calling WCF channel GetStatusItems method");
|
|
GetStatusItemsResponse statusItems = channelClient.GetStatusItems((GetStatusItemsRequest)request);
|
|
ServiceTrace.LogCsv("Returned from WCF channel GetStatusItems method");
|
|
if (statusItems == null)
|
|
{
|
|
return ArchestrAResult.MakeResult(ArchestrAError.BadNoCommunication, 0).AddErrorMessage("No response from the service");
|
|
}
|
|
UpdateDownstreamConnectionState(statusItems);
|
|
receivedItems = statusItems.Items;
|
|
return statusItems.Result;
|
|
});
|
|
ServiceTrace.LogSuspend("GetStatusItems exit with {0}", archestrAResult.Success ? "success" : "failure");
|
|
ServiceTrace.LogCsv("GetStatusItems exit [success]", archestrAResult.Success);
|
|
items = receivedItems;
|
|
return archestrAResult;
|
|
}
|
|
|
|
public ArchestrAResult GetStatus(string[] itemsToReturn, out NamedValue[] items)
|
|
{
|
|
ServiceTrace.LogCsv("GetStatus entry");
|
|
ServiceTrace.LogResume("GetStatus entry");
|
|
NamedValue[] receivedItems = null;
|
|
ArchestrAResult archestrAResult = PrepareToSend(new GetStatusRequest
|
|
{
|
|
ItemsToReturn = itemsToReturn
|
|
}, delegate(ConnectedRequest request, T channelClient)
|
|
{
|
|
ServiceTrace.LogCsv("Calling WCF channel GetStatus method");
|
|
GetStatusResponse status = channelClient.GetStatus((GetStatusRequest)request);
|
|
ServiceTrace.LogCsv("Returned from WCF channel GetStatus method");
|
|
if (status == null)
|
|
{
|
|
return ArchestrAResult.MakeResult(ArchestrAError.BadNoCommunication, 0).AddErrorMessage("No response from the service");
|
|
}
|
|
UpdateDownstreamConnectionState(status);
|
|
receivedItems = status.Items;
|
|
return status.Result;
|
|
});
|
|
ServiceTrace.LogSuspend("GetStatus exit with {0}", archestrAResult.Success ? "success" : "failure");
|
|
ServiceTrace.LogCsv("GetStatus exit [success]", archestrAResult.Success);
|
|
items = receivedItems;
|
|
return archestrAResult;
|
|
}
|
|
|
|
public void OnDisconnect()
|
|
{
|
|
ServiceTrace.LogCsv("OnDisconnect entry");
|
|
ServiceTrace.LogResume("OnDisconnect entry");
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
protected ArchestrAResult PrepareToSend(ConnectedRequest request, Func<ConnectedRequest, T, ArchestrAResult> processFunc)
|
|
{
|
|
if (request == null)
|
|
{
|
|
return ArchestrAResult.MakeResult(ArchestrAError.BadNoCommunication, 0).AddErrorMessage("No request message provided for signature");
|
|
}
|
|
if (processFunc == null)
|
|
{
|
|
return ArchestrAResult.MakeResult(ArchestrAError.BadNoCommunication, 0).AddErrorMessage("No processing function provided");
|
|
}
|
|
ArchestrAResult archestrAResult = SignRequest(request);
|
|
if (!archestrAResult.Success)
|
|
{
|
|
return archestrAResult;
|
|
}
|
|
T channelClient = ChannelClient;
|
|
if (channelClient == null)
|
|
{
|
|
return ArchestrAResult.MakeResult(ArchestrAError.BadNoCommunication, 0).AddErrorMessage("No connected client for the current connection Id");
|
|
}
|
|
return processFunc(request, channelClient);
|
|
}
|
|
|
|
protected ArchestrAResult SignRequest(ConnectedRequest request)
|
|
{
|
|
ArchestrAResult result = ArchestrAResult.MakeResult(ArchestrAError.InvalidConnectionId, 0);
|
|
if (successfulConnectionResult != null && successfulConnectionResult.ServiceClient != null && State != CommunicationState.Faulted)
|
|
{
|
|
SystemAuthenticationClientAuthentication clientAuthenticator = SysAuthenticatorClientCache.GetClientAuthenticator(successfulConnectionResult.ConnectionId);
|
|
if (clientAuthenticator != null)
|
|
{
|
|
clientAuthenticator.Sign(request, forceHmac: false);
|
|
return ArchestrAResult.MakeGoodResult();
|
|
}
|
|
result.AddErrorMessage("Unable to find a signer using the current connection Id");
|
|
}
|
|
else
|
|
{
|
|
result.AddErrorMessage("Unable to use the client proxy for the current connection Id");
|
|
}
|
|
return result;
|
|
}
|
|
|
|
protected void UpdateDownstreamConnectionState(ConnectedResponse response)
|
|
{
|
|
if (response != null)
|
|
{
|
|
if (response.DownstreamConnectionState <= 6)
|
|
{
|
|
DownstreamConnectionState = (ConnectionState)response.DownstreamConnectionState;
|
|
return;
|
|
}
|
|
ServiceTrace.LogWarning(string.Format(CultureInfo.InvariantCulture, "ASB Base V2 client failed to parse downstream connection state value of {0}", new object[1] { response.DownstreamConnectionState }));
|
|
DownstreamConnectionState = ConnectionState.Unknown;
|
|
}
|
|
}
|
|
|
|
~BaseV2Client()
|
|
{
|
|
Dispose(disposing: false);
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
Dispose(disposing: true);
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
|
|
private void Dispose(bool disposing)
|
|
{
|
|
if (disposed)
|
|
{
|
|
return;
|
|
}
|
|
if (disposing)
|
|
{
|
|
if (successfulConnectionResult != null)
|
|
{
|
|
successfulConnectionResult.Dispose();
|
|
}
|
|
successfulConnectionResult = null;
|
|
}
|
|
disposed = true;
|
|
Reset();
|
|
}
|
|
}
|