Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix reopen secure channel without activate #2577

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
5 changes: 5 additions & 0 deletions Stack/Opc.Ua.Core/Stack/Tcp/ChannelAsyncOperation.cs
Expand Up @@ -311,6 +311,11 @@ public async Task<T> EndAsync(int timeout, bool throwOnError = true, Cancellatio
}
}
}

/// <summary>
/// Return the result of the operation.
/// </summary>
public ServiceResult Error => m_error ?? ServiceResult.Good;
#endregion

#region IAsyncResult Members
Expand Down
14 changes: 6 additions & 8 deletions Stack/Opc.Ua.Core/Stack/Tcp/UaSCBinaryChannel.cs
Expand Up @@ -99,7 +99,7 @@ public partial class UaSCUaBinaryChannel : IMessageSink, IDisposable
m_discoveryOnly = false;
m_uninitialized = true;

m_state = TcpChannelState.Closed;
m_state = (int)TcpChannelState.Closed;
m_receiveBufferSize = quotas.MaxBufferSize;
m_sendBufferSize = quotas.MaxBufferSize;
m_activeWriteRequests = 0;
Expand Down Expand Up @@ -735,16 +735,14 @@ protected int MaxResponseChunkCount
/// </summary>
protected TcpChannelState State
{
get { return m_state; }
get => (TcpChannelState)m_state;

set
{
if (m_state != value)
if (Interlocked.Exchange(ref m_state, (int)value) != (int)value)
{
Utils.LogTrace("ChannelId {0}: in {1} state.", ChannelId, value);
Utils.LogInfo("ChannelId {0}: in {1} state.", ChannelId, value);
}
mregen marked this conversation as resolved.
Show resolved Hide resolved

m_state = value;
}
}

Expand Down Expand Up @@ -842,7 +840,7 @@ protected static int CalculateChunkCount(int messageSize, int bufferSize)
private int m_maxResponseChunkCount;
private string m_contextId;

private TcpChannelState m_state;
private int m_state;
private uint m_channelId;
private string m_globalChannelId;
private long m_sequenceNumber;
Expand All @@ -858,7 +856,7 @@ protected static int CalculateChunkCount(int messageSize, int bufferSize)
/// <summary>
/// The possible channel states.
/// </summary>
public enum TcpChannelState
public enum TcpChannelState : int
{
/// <summary>
/// The channel is closed.
Expand Down
92 changes: 55 additions & 37 deletions Stack/Opc.Ua.Core/Stack/Tcp/UaSCBinaryClientChannel.cs
Expand Up @@ -219,25 +219,9 @@
{
try
{
_ = await operation.EndAsync(timeout, true, ct).ConfigureAwait(false);
}
catch (ServiceResultException e)
{
switch (e.StatusCode)
{
case StatusCodes.BadRequestInterrupted:
case StatusCodes.BadSecureChannelClosed:
{
break;
}

default:
{
Utils.LogWarning(e, "ChannelId {0}: Could not gracefully close the channel. Reason={1}", ChannelId, e.Result.StatusCode);
break;
}
_ = await operation.EndAsync(timeout, false, ct).ConfigureAwait(false);
ValidateChannelCloseError(operation.Error);
}
}
catch (Exception e)
{
Utils.LogError(e, "ChannelId {0}: Could not gracefully close the channel.", ChannelId);
Expand All @@ -261,23 +245,7 @@
try
{
operation.End(timeout, false);
}
catch (ServiceResultException e)
{
switch (e.StatusCode)
{
case StatusCodes.BadRequestInterrupted:
case StatusCodes.BadSecureChannelClosed:
{
break;
}

default:
{
Utils.LogWarning(e, "ChannelId {0}: Could not gracefully close the channel. Reason={1}", ChannelId, e.Result.StatusCode);
break;
}
}
ValidateChannelCloseError(operation.Error);
}
catch (Exception e)
{
Expand Down Expand Up @@ -323,9 +291,10 @@
if (m_queuedOperations != null)
{
operation = BeginOperation(timeout, callback, state);
m_queuedOperations.Add(new QueuedOperation(operation, timeout, request));

if (firstCall)
bool validConnectOperation = QueueConnectOperation(operation, timeout, request);

if (firstCall && validConnectOperation)
{
BeginConnect(m_url, timeout, OnConnectOnDemandComplete, null);
}
Expand Down Expand Up @@ -811,6 +780,55 @@
}
}

/// <summary>
/// Validates the result of a channel close operation.
/// </summary>
private void ValidateChannelCloseError(ServiceResult error)
{
if (ServiceResult.IsBad(error))
{
StatusCode statusCode = error.StatusCode;
switch ((uint)statusCode)
{
case StatusCodes.BadRequestInterrupted:
case StatusCodes.BadSecureChannelClosed:
{
break;
}

default:
{
Utils.LogWarning("ChannelId {0}: Could not gracefully close the channel. Reason={1}", ChannelId, error);

Check warning on line 801 in Stack/Opc.Ua.Core/Stack/Tcp/UaSCBinaryClientChannel.cs

View check run for this annotation

Codecov / codecov/patch

Stack/Opc.Ua.Core/Stack/Tcp/UaSCBinaryClientChannel.cs#L801

Added line #L801 was not covered by tests
break;
}
}
}
}

/// <summary>
/// Queues an operation for sending after the channel is connected.
/// Inserts operations that create or activate a session or don't require a session first.
/// </summary>
/// <returns>true if a valid service call for BeginConnect is queued.</returns>
private bool QueueConnectOperation(WriteOperation operation, int timeout, IServiceRequest request)
{
var queuedOperation = new QueuedOperation(operation, timeout, request);

// operations that must be sent first and which allow for a connect.
if (request.TypeId == DataTypeIds.ActivateSessionRequest ||
request.TypeId == DataTypeIds.CreateSessionRequest ||
request.TypeId == DataTypeIds.GetEndpointsRequest ||
request.TypeId == DataTypeIds.FindServersOnNetworkRequest ||
request.TypeId == DataTypeIds.FindServersRequest)
{
m_queuedOperations.Insert(0, queuedOperation);
return true;
}

m_queuedOperations.Add(queuedOperation);
return false;

Check warning on line 829 in Stack/Opc.Ua.Core/Stack/Tcp/UaSCBinaryClientChannel.cs

View check run for this annotation

Codecov / codecov/patch

Stack/Opc.Ua.Core/Stack/Tcp/UaSCBinaryClientChannel.cs#L828-L829

Added lines #L828 - L829 were not covered by tests
}

/// <summary>
/// Called when the socket is connected.
/// </summary>
Expand Down
6 changes: 4 additions & 2 deletions Stack/Opc.Ua.Core/Stack/Tcp/UaSCBinaryTransportChannel.cs
Expand Up @@ -359,9 +359,11 @@
if (channel == null)
{
channel = CreateChannel();
if (Interlocked.CompareExchange(ref m_channel, channel, null) != null)
var currentChannel = Interlocked.CompareExchange(ref m_channel, channel, null);

Check warning on line 362 in Stack/Opc.Ua.Core/Stack/Tcp/UaSCBinaryTransportChannel.cs

View check run for this annotation

Codecov / codecov/patch

Stack/Opc.Ua.Core/Stack/Tcp/UaSCBinaryTransportChannel.cs#L362

Added line #L362 was not covered by tests
if (currentChannel != null)
{
channel = m_channel;
Utils.SilentDispose(channel);
channel = currentChannel;

Check warning on line 366 in Stack/Opc.Ua.Core/Stack/Tcp/UaSCBinaryTransportChannel.cs

View check run for this annotation

Codecov / codecov/patch

Stack/Opc.Ua.Core/Stack/Tcp/UaSCBinaryTransportChannel.cs#L365-L366

Added lines #L365 - L366 were not covered by tests
romanett marked this conversation as resolved.
Show resolved Hide resolved
}
}

Expand Down