From d450ef96a89795f224cd49055548d5705919879f Mon Sep 17 00:00:00 2001 From: Martin Regen Date: Mon, 23 Jan 2017 13:19:12 +0100 Subject: [PATCH] make TransportChannel generic --- .../Client.Net4/CustomTransportChannel.cs | 391 ++---------------- .../Samples/Client.Net4/Program.cs | 3 +- .../Samples/NetCoreConsoleClient/Program.cs | 3 +- .../Opc.Ua.Core/Stack/Https/HttpsListener.cs | 12 +- .../Opc.Ua.Core/Stack/Tcp/TcpMessageSocket.cs | 28 +- ...annel.cs => UaSCBinaryTransportChannel.cs} | 19 +- 6 files changed, 77 insertions(+), 379 deletions(-) rename Stack/Opc.Ua.Core/Stack/Tcp/{TcpTransportChannel.cs => UaSCBinaryTransportChannel.cs} (95%) diff --git a/SampleApplications/Samples/Client.Net4/CustomTransportChannel.cs b/SampleApplications/Samples/Client.Net4/CustomTransportChannel.cs index 76a3e8785e..88970aa1c8 100644 --- a/SampleApplications/Samples/Client.Net4/CustomTransportChannel.cs +++ b/SampleApplications/Samples/Client.Net4/CustomTransportChannel.cs @@ -10,9 +10,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ -using System; - -namespace Opc.Ua.Bindings +namespace Opc.Ua.Bindings.Custom { /// /// Creates a new CustomTransportChannel with ITransportChannel interface. @@ -25,380 +23,45 @@ namespace Opc.Ua.Bindings /// the transport channel public ITransportChannel Create() { - return new CustomTransportChannel(); + return new UaSCBinaryTransportChannel(new CustomMessageSocketFactory()); } } /// - /// Wraps the CustomTransportChannel and provides an ITransportChannel implementation. + /// Creates a new CustomMessageSocket with IMessageSocket interface. /// - public class CustomTransportChannel : ITransportChannel + public class CustomMessageSocketFactory : IMessageSocketFactory { - #region IDisposable Members /// - /// Frees any unmanaged resources. + /// The method creates a new instance of a proxy message socket /// - public void Dispose() - { - Dispose(true); - } - - /// - /// An overrideable version of the Dispose. - /// - protected virtual void Dispose(bool disposing) + /// the message socket + public IMessageSocket Create( + IMessageSink sink, + BufferManager bufferManager, + int receiveBufferSize + ) { - if (disposing) - { - Utils.SilentDispose(m_channel); - m_channel = null; - } + // plug in your custom socket implementation here + return new CustomMessageSocket(sink, bufferManager, receiveBufferSize); } - #endregion + } - #region ITransportChannel Members + /// + /// Implement a CustomMessageSocket with IMessageSocket interface. + /// + public class CustomMessageSocket : TcpMessageSocket // IMessageSocket + { + #region Constructors /// - /// A masking indicating which features are implemented. + /// Creates an unconnected socket. /// - public TransportChannelFeatures SupportedFeatures - { - get { return TransportChannelFeatures.Open | TransportChannelFeatures.BeginOpen | TransportChannelFeatures.Reconnect | TransportChannelFeatures.BeginSendRequest; } - } - - /// - /// Gets the description for the endpoint used by the channel. - /// - public EndpointDescription EndpointDescription - { - get { return m_settings.Description; } - } - - /// - /// Gets the configuration for the channel. - /// - public EndpointConfiguration EndpointConfiguration - { - get { return m_settings.Configuration; } - } - - /// - /// Gets the context used when serializing messages exchanged via the channel. - /// - public ServiceMessageContext MessageContext - { - get { return m_quotas.MessageContext; } - } - - /// - /// Gets or sets the default timeout for requests send via the channel. - /// - public int OperationTimeout - { - get { return m_operationTimeout; } - set { m_operationTimeout = value; } - } - - /// - /// Initializes a secure channel with the endpoint identified by the URL. - /// - /// The URL for the endpoint. - /// The settings to use when creating the channel. - /// Thrown if any communication error occurs. - public void Initialize( - Uri url, - TransportChannelSettings settings) - { - SaveSettings(url, settings); - } - - /// - /// Opens a secure channel with the endpoint identified by the URL. - /// - /// Thrown if any communication error occurs. - public void Open() - { - // opens when the first request is called to preserve previous behavoir. - } - - /// - /// Begins an asynchronous operation to open a secure channel with the endpoint identified by the URL. - /// - /// The callback to call when the operation completes. - /// The callback data to return with the callback. - /// - /// The result which must be passed to the EndOpen method. - /// - /// Thrown if any communication error occurs. - /// - public IAsyncResult BeginOpen(AsyncCallback callback, object callbackData) - { - lock (m_lock) - { - // create the channel. - m_channel = new TcpClientChannel( - Guid.NewGuid().ToString(), - m_bufferManager, - new TcpMessageSocketFactory(), - m_quotas, - m_settings.ClientCertificate, - m_settings.ServerCertificate, - m_settings.Description); - - // begin connect operation. - return m_channel.BeginConnect(this.m_url, m_operationTimeout, callback, callbackData); - } - } - - /// - /// Completes an asynchronous operation to open a secure channel. - /// - /// The result returned from the BeginOpen call. - /// Thrown if any communication error occurs. - /// - public void EndOpen(IAsyncResult result) - { - m_channel.EndConnect(result); - } - - /// - /// Closes any existing secure channel and opens a new one. - /// - /// Thrown if any communication error occurs. - /// - /// Calling this method will cause outstanding requests over the current secure channel to fail. - /// - public void Reconnect() - { - Utils.Trace("CustomTransportChannel RECONNECT: Reconnecting to {0}.", m_url); - - lock (m_lock) - { - // the new channel must be created first because WinSock will reuse sockets and this - // can result in messages sent over the old socket arriving as messages on the new socket. - // if this happens the new channel is shutdown because of a security violation. - TcpClientChannel channel = m_channel; - m_channel = null; - - // reconnect. - OpenOnDemand(); - - // begin connect operation. - IAsyncResult result = m_channel.BeginConnect(m_url, m_operationTimeout, null, null); - m_channel.EndConnect(result); - - // close existing channel. - if (channel != null) - { - try - { - channel.Close(1000); - } - catch (Exception) - { - // do nothing. - } - finally - { - channel.Dispose(); - } - } - } - } - - /// - /// Begins an asynchronous operation to close the existing secure channel and open a new one. - /// - /// The callback to call when the operation completes. - /// The callback data to return with the callback. - /// - /// The result which must be passed to the EndReconnect method. - /// - /// Thrown if any communication error occurs. - /// - public IAsyncResult BeginReconnect(AsyncCallback callback, object callbackData) - { - throw new NotImplementedException(); - } - - /// - /// Completes an asynchronous operation to close the existing secure channel and open a new one. - /// - /// The result returned from the BeginReconnect call. - /// Thrown if any communication error occurs. - /// - public void EndReconnect(IAsyncResult result) - { - throw new NotImplementedException(); - } - - /// - /// Closes the secure channel. - /// - /// Thrown if any communication error occurs. - public void Close() - { - if (m_channel != null) - { - lock (m_lock) - { - if (m_channel != null) - { - m_channel.Close(1000); - m_channel = null; - } - } - } - } - - /// - /// Begins an asynchronous operation to close the secure channel. - /// - /// The callback to call when the operation completes. - /// The callback data to return with the callback. - /// - /// The result which must be passed to the EndClose method. - /// - /// Thrown if any communication error occurs. - /// - public IAsyncResult BeginClose(AsyncCallback callback, object callbackData) - { - throw new NotImplementedException(); - } - - /// - /// Completes an asynchronous operation to close the secure channel. - /// - /// The result returned from the BeginClose call. - /// Thrown if any communication error occurs. - /// - public void EndClose(IAsyncResult result) - { - throw new NotImplementedException(); - } - - /// - /// Sends a request over the secure channel. - /// - /// The request to send. - /// The response returned by the server. - /// Thrown if any communication error occurs. - public IServiceResponse SendRequest(IServiceRequest request) - { - IAsyncResult result = BeginSendRequest(request, null, null); - return EndSendRequest(result); - } - - /// - /// Begins an asynchronous operation to send a request over the secure channel. - /// - /// The request to send. - /// The callback to call when the operation completes. - /// The callback data to return with the callback. - /// - /// The result which must be passed to the EndSendRequest method. - /// - /// Thrown if any communication error occurs. - /// - public IAsyncResult BeginSendRequest(IServiceRequest request, AsyncCallback callback, object callbackData) - { - TcpClientChannel channel = m_channel; - - if (channel == null) - { - lock (m_lock) - { - if (m_channel == null) - { - OpenOnDemand(); - } - - channel = m_channel; - } - } - - return channel.BeginSendRequest(request, m_operationTimeout, callback, callbackData); - } - - /// - /// Completes an asynchronous operation to send a request over the secure channel. - /// - /// The result returned from the BeginSendRequest call. - /// - /// Thrown if any communication error occurs. - /// - public IServiceResponse EndSendRequest(IAsyncResult result) - { - TcpClientChannel channel = m_channel; - - if (channel == null) - { - throw ServiceResultException.Create(StatusCodes.BadSecureChannelClosed, "Channel has been closed."); - } - - return channel.EndSendRequest(result); - } - - /// - /// Saves the settings so the channel can be opened later. - /// - /// The URL. - /// The settings. - private void SaveSettings(Uri url, TransportChannelSettings settings) - { - // save the settings. - m_url = url; - m_settings = settings; - m_operationTimeout = settings.Configuration.OperationTimeout; - - // initialize the quotas. - m_quotas = new TcpChannelQuotas(); - - m_quotas.MaxBufferSize = m_settings.Configuration.MaxBufferSize; - m_quotas.MaxMessageSize = m_settings.Configuration.MaxMessageSize; - m_quotas.ChannelLifetime = m_settings.Configuration.ChannelLifetime; - m_quotas.SecurityTokenLifetime = m_settings.Configuration.SecurityTokenLifetime; - - m_quotas.MessageContext = new ServiceMessageContext(); - - m_quotas.MessageContext.MaxArrayLength = m_settings.Configuration.MaxArrayLength; - m_quotas.MessageContext.MaxByteStringLength = m_settings.Configuration.MaxByteStringLength; - m_quotas.MessageContext.MaxMessageSize = m_settings.Configuration.MaxMessageSize; - m_quotas.MessageContext.MaxStringLength = m_settings.Configuration.MaxStringLength; - m_quotas.MessageContext.NamespaceUris = m_settings.NamespaceUris; - m_quotas.MessageContext.ServerUris = new StringTable(); - m_quotas.MessageContext.Factory = m_settings.Factory; - - m_quotas.CertificateValidator = settings.CertificateValidator; - - // create the buffer manager. - m_bufferManager = new BufferManager("Client", (int)Int32.MaxValue, settings.Configuration.MaxBufferSize); - } - - /// - /// Opens the channel before sending the request. - /// - private void OpenOnDemand() - { - // create the channel. - m_channel = new TcpClientChannel( - Guid.NewGuid().ToString(), - m_bufferManager, - new TcpMessageSocketFactory(), - m_quotas, - m_settings.ClientCertificate, - m_settings.ServerCertificate, - m_settings.Description); - } - #endregion - - #region Private Fields - private object m_lock = new object(); - private Uri m_url; - private int m_operationTimeout; - private TransportChannelSettings m_settings; - private TcpChannelQuotas m_quotas; - private BufferManager m_bufferManager; - private TcpClientChannel m_channel; + public CustomMessageSocket( + IMessageSink sink, + BufferManager bufferManager, + int receiveBufferSize) : + base(sink, bufferManager, receiveBufferSize) + { } #endregion } } diff --git a/SampleApplications/Samples/Client.Net4/Program.cs b/SampleApplications/Samples/Client.Net4/Program.cs index 97cf0f8ea9..7a06f0f518 100644 --- a/SampleApplications/Samples/Client.Net4/Program.cs +++ b/SampleApplications/Samples/Client.Net4/Program.cs @@ -31,8 +31,7 @@ using System; using System.Windows.Forms; using Opc.Ua.Configuration; using Opc.Ua.Client.Controls; -using System.Threading.Tasks; -using Opc.Ua.Bindings; +using Opc.Ua.Bindings.Custom; namespace Opc.Ua.Sample { diff --git a/SampleApplications/Samples/NetCoreConsoleClient/Program.cs b/SampleApplications/Samples/NetCoreConsoleClient/Program.cs index aab6cf1d8e..f4a89a5dc9 100644 --- a/SampleApplications/Samples/NetCoreConsoleClient/Program.cs +++ b/SampleApplications/Samples/NetCoreConsoleClient/Program.cs @@ -96,7 +96,8 @@ namespace NetCoreConsoleClient config.SecurityConfiguration.ApplicationCertificate.StoreType, config.SecurityConfiguration.ApplicationCertificate.StorePath, config.ApplicationUri, - config.ApplicationName + config.ApplicationName, + config.SecurityConfiguration.ApplicationCertificate.SubjectName ); config.SecurityConfiguration.ApplicationCertificate.Certificate = certificate; diff --git a/Stack/Opc.Ua.Core/Stack/Https/HttpsListener.cs b/Stack/Opc.Ua.Core/Stack/Https/HttpsListener.cs index 7cb4433b97..3a10b6188f 100644 --- a/Stack/Opc.Ua.Core/Stack/Https/HttpsListener.cs +++ b/Stack/Opc.Ua.Core/Stack/Https/HttpsListener.cs @@ -63,11 +63,11 @@ namespace Opc.Ua.Bindings /// /// Manages the connections for a UA HTTPS server. /// - public partial class UaHttpsChannelListener : IDisposable, ITransportListener + public partial class UaHttpsChannelListener : ITransportListener { #region Constructors /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// public UaHttpsChannelListener() { @@ -173,7 +173,7 @@ namespace Opc.Ua.Bindings public void Start() { Startup.Listener = this; - + m_host = new WebHostBuilder(); HttpsConnectionFilterOptions httpsOptions = new HttpsConnectionFilterOptions(); @@ -222,7 +222,7 @@ namespace Opc.Ua.Bindings await context.Response.WriteAsync(string.Empty); return; } - + byte[] buffer = new byte[(int)context.Request.ContentLength]; lock (m_lock) { @@ -230,7 +230,7 @@ namespace Opc.Ua.Bindings task.Wait(); } - IServiceRequest input = (IServiceRequest) BinaryDecoder.DecodeMessage(buffer, null, m_quotas.MessageContext); + IServiceRequest input = (IServiceRequest)BinaryDecoder.DecodeMessage(buffer, null, m_quotas.MessageContext); // extract the JWT token from the HTTP headers. if (input.RequestHeader == null) @@ -271,7 +271,7 @@ namespace Opc.Ua.Bindings null); IServiceResponse output = m_callback.EndProcessRequest(result); - + byte[] response = BinaryEncoder.EncodeMessage(output, m_quotas.MessageContext); context.Response.ContentLength = response.Length; context.Response.ContentType = context.Request.ContentType; diff --git a/Stack/Opc.Ua.Core/Stack/Tcp/TcpMessageSocket.cs b/Stack/Opc.Ua.Core/Stack/Tcp/TcpMessageSocket.cs index edb56ab497..9f37e6a922 100644 --- a/Stack/Opc.Ua.Core/Stack/Tcp/TcpMessageSocket.cs +++ b/Stack/Opc.Ua.Core/Stack/Tcp/TcpMessageSocket.cs @@ -18,6 +18,32 @@ using MessageSocketError = System.Net.Sockets.SocketError; namespace Opc.Ua.Bindings { + /// + /// Creates a transport channel with TCP transport, UA-SC security and UA Binary encoding + /// + + public class TcpTransportChannel : UaSCBinaryTransportChannel + { + public TcpTransportChannel() : + base(new TcpMessageSocketFactory()) + { + } + } + + /// + /// Creates a new TcpTransportChannel with ITransportChannel interface. + /// + public class TcpTransportChannelFactory : ITransportChannelFactory + { + /// + /// The method creates a new instance of a proxy transport channel + /// + /// the transport channel + public ITransportChannel Create() + { + return new TcpTransportChannel(); + } + } /// /// Handles async event callbacks from a socket @@ -99,7 +125,7 @@ namespace Opc.Ua.Bindings } /// - /// Creates a new ProxyMessageSocket with IMessageSocket interface. + /// Creates a new TcpMessageSocket with IMessageSocket interface. /// public class TcpMessageSocketFactory : IMessageSocketFactory { diff --git a/Stack/Opc.Ua.Core/Stack/Tcp/TcpTransportChannel.cs b/Stack/Opc.Ua.Core/Stack/Tcp/UaSCBinaryTransportChannel.cs similarity index 95% rename from Stack/Opc.Ua.Core/Stack/Tcp/TcpTransportChannel.cs rename to Stack/Opc.Ua.Core/Stack/Tcp/UaSCBinaryTransportChannel.cs index c8ead648e1..1e9f2fa5d2 100644 --- a/Stack/Opc.Ua.Core/Stack/Tcp/TcpTransportChannel.cs +++ b/Stack/Opc.Ua.Core/Stack/Tcp/UaSCBinaryTransportChannel.cs @@ -15,10 +15,18 @@ using System; namespace Opc.Ua.Bindings { /// - /// Wraps the TcpTransportChannel and provides an ITransportChannel implementation. + /// Creates a transport channel for the ITransportChannel interface. + /// Implements the UA-SC security and UA Binary encoding. + /// The socket layer requires a IMessageSocketFactory implementation. /// - public class TcpTransportChannel : ITransportChannel + public class UaSCBinaryTransportChannel : ITransportChannel { + #region Constructors + public UaSCBinaryTransportChannel(IMessageSocketFactory messageSocketFactory) + { + m_messageSocketFactory = messageSocketFactory; + } + #endregion #region IDisposable Members /// /// Frees any unmanaged resources. @@ -123,7 +131,7 @@ namespace Opc.Ua.Bindings m_channel = new TcpClientChannel( Guid.NewGuid().ToString(), m_bufferManager, - new TcpMessageSocketFactory(), + m_messageSocketFactory, m_quotas, m_settings.ClientCertificate, m_settings.ServerCertificate, @@ -154,7 +162,7 @@ namespace Opc.Ua.Bindings /// public void Reconnect() { - Utils.Trace("TcpTransportChannel RECONNECT: Reconnecting to {0}.", m_url); + Utils.Trace("TransportChannel RECONNECT: Reconnecting to {0}.", m_url); lock (m_lock) { @@ -368,7 +376,7 @@ namespace Opc.Ua.Bindings m_channel = new TcpClientChannel( Guid.NewGuid().ToString(), m_bufferManager, - new TcpMessageSocketFactory(), + m_messageSocketFactory, m_quotas, m_settings.ClientCertificate, m_settings.ServerCertificate, @@ -384,6 +392,7 @@ namespace Opc.Ua.Bindings private TcpChannelQuotas m_quotas; private BufferManager m_bufferManager; private TcpClientChannel m_channel; + private IMessageSocketFactory m_messageSocketFactory; #endregion } }