Skip to content

Commit

Permalink
Handle special cases for chunking. (#1816)
Browse files Browse the repository at this point in the history
- Chunk count one compare is incorrect
- Instead of a static configuration calculate the chunk count limits dynamically, as changing buffer size in the negotiation was not taken into account and could lead to reduced message size.
- Remove the static chunk count calculation.
  • Loading branch information
mregen committed May 12, 2022
1 parent fd9a91a commit 88a96a1
Show file tree
Hide file tree
Showing 7 changed files with 40 additions and 79 deletions.
54 changes: 1 addition & 53 deletions Stack/Opc.Ua.Core/Stack/Tcp/ChannelQuotas.cs
Expand Up @@ -25,9 +25,7 @@ public ChannelQuotas()
{
m_messageContext = ServiceMessageContext.GlobalContext;
m_maxMessageSize = TcpMessageLimits.DefaultMaxMessageSize;
m_maxBufferSize = TcpMessageLimits.DefaultMaxMessageSize;
m_maxRequestChunkCount = TcpMessageLimits.DefaultMaxChunkCount;
m_maxResponseChunkCount = TcpMessageLimits.DefaultMaxChunkCount;
m_maxBufferSize = TcpMessageLimits.DefaultMaxBufferSize;
m_channelLifetime = TcpMessageLimits.DefaultChannelLifetime;
m_securityTokenLifetime = TcpMessageLimits.DefaultSecurityTokenLifeTime;
}
Expand Down Expand Up @@ -165,62 +163,12 @@ public int SecurityTokenLifetime
}
}
}

/// <summary>
/// The maximum number of chunks in any request message (used in the Acknowledge Message).
/// The Client shall abort the Message with a Bad_RequestTooLarge StatusCode if a request Message exceeds this value.
/// A value of zero indicates that the Server has no limit.
/// </summary>
public int MaxRequestChunkCount
{
get
{
lock (m_lock)
{
return m_maxRequestChunkCount;
}
}

set
{
lock (m_lock)
{
m_maxRequestChunkCount = value;
}
}
}

/// <summary>
/// The maximum number of chunks in any response message (used in the Hello Message).
/// The Server shall abort the Message with a Bad_ResponseTooLarge Error Message if a response Message exceeds this value.
/// A value of zero indicates that the Client has no limit.
/// </summary>
public int MaxResponseChunkCount
{
get
{
lock (m_lock)
{
return m_maxResponseChunkCount;
}
}

set
{
lock (m_lock)
{
m_maxResponseChunkCount = value;
}
}
}
#endregion

#region Private Fields
private object m_lock = new object();
private int m_maxMessageSize;
private int m_maxBufferSize;
private int m_maxRequestChunkCount;
private int m_maxResponseChunkCount;
private int m_channelLifetime;
private int m_securityTokenLifetime;
private IServiceMessageContext m_messageContext;
Expand Down
2 changes: 1 addition & 1 deletion Stack/Opc.Ua.Core/Stack/Tcp/TcpMessageType.cs
Expand Up @@ -174,7 +174,7 @@ public static class TcpMessageLimits
public const int MinBodySize = 1;

/// <summary>
/// The minimum send or receive buffer size.
/// The maximum send or receive buffer size.
/// </summary>
public const int MaxBufferSize = 8192 * 18;

Expand Down
10 changes: 8 additions & 2 deletions Stack/Opc.Ua.Core/Stack/Tcp/TcpServerChannel.cs
Expand Up @@ -95,7 +95,9 @@ private class ReverseConnectAsyncResult : AsyncResultBase
public IMessageSocket Socket;
}

/// <remarks/>
/// <summary>
/// Begin a reverse connect.
/// </summary>
public IAsyncResult BeginReverseConnect(uint channelId, Uri endpointUrl, AsyncCallback callback, object callbackData, int timeout)
{
ChannelId = channelId;
Expand All @@ -114,7 +116,7 @@ public IAsyncResult BeginReverseConnect(uint channelId, Uri endpointUrl, AsyncCa
}

/// <summary>
///
/// End the reverse connect.
/// </summary>
public void EndReverseConnect(IAsyncResult result)
{
Expand Down Expand Up @@ -415,11 +417,15 @@ private bool ProcessHelloMessage(ArraySegment<byte> messageChunk)
}

// update the max chunk count.
MaxResponseChunkCount = CalculateChunkCount(MaxResponseMessageSize, SendBufferSize);

if (maxChunkCount > 0 && maxChunkCount < MaxResponseChunkCount)
{
MaxResponseChunkCount = (int)maxChunkCount;
}

MaxRequestChunkCount = CalculateChunkCount(MaxRequestMessageSize, ReceiveBufferSize);

// send acknowledge.
byte[] buffer = BufferManager.TakeBuffer(SendBufferSize, "ProcessHelloMessage");

Expand Down
14 changes: 3 additions & 11 deletions Stack/Opc.Ua.Core/Stack/Tcp/TcpTransportListener.cs
Expand Up @@ -123,14 +123,6 @@ protected virtual void Dispose(bool disposing)
m_quotas.MaxMessageSize = configuration.MaxMessageSize;
m_quotas.ChannelLifetime = configuration.ChannelLifetime;
m_quotas.SecurityTokenLifetime = configuration.SecurityTokenLifetime;

if (configuration.MaxBufferSize != 0)
{
int maxChunkCount = configuration.MaxMessageSize / configuration.MaxBufferSize;
m_quotas.MaxRequestChunkCount = maxChunkCount;
m_quotas.MaxResponseChunkCount = maxChunkCount;
}

messageContext.MaxArrayLength = configuration.MaxArrayLength;
messageContext.MaxByteStringLength = configuration.MaxByteStringLength;
messageContext.MaxMessageSize = configuration.MaxMessageSize;
Expand Down Expand Up @@ -226,7 +218,7 @@ public void ChannelClosed(uint channelId)
/// </summary>
public event EventHandler<ConnectionStatusEventArgs> ConnectionStatusChanged;

/// <remarks/>
/// <inheritdoc/>
public void CreateReverseConnection(Uri url, int timeout)
{
TcpServerChannel channel = new TcpServerChannel(
Expand Down Expand Up @@ -673,10 +665,10 @@ internal TcpConnectionWaitingEventArgs(string serverUrl, Uri endpointUrl, IMessa
Socket = socket;
}

/// <remarks/>
/// <inheritdoc/>
public override object Handle => Socket;

/// <remarks/>
/// <inheritdoc/>
internal IMessageSocket Socket { get; }
}
}
30 changes: 26 additions & 4 deletions Stack/Opc.Ua.Core/Stack/Tcp/UaSCBinaryChannel.cs
Expand Up @@ -125,8 +125,8 @@ public partial class UaSCUaBinaryChannel : IMessageSink, IDisposable
m_maxRequestMessageSize = quotas.MaxMessageSize;
m_maxResponseMessageSize = quotas.MaxMessageSize;

m_maxRequestChunkCount = quotas.MaxRequestChunkCount;
m_maxResponseChunkCount = quotas.MaxResponseChunkCount;
m_maxRequestChunkCount = CalculateChunkCount(m_maxRequestMessageSize, m_sendBufferSize);
m_maxResponseChunkCount = CalculateChunkCount(m_maxResponseMessageSize, m_receiveBufferSize);

CalculateSymmetricKeySizes();
}
Expand Down Expand Up @@ -561,7 +561,7 @@ protected bool MessageLimitsExceeded(bool isRequest, int messageSize, int chunkC
{
if (isRequest)
{
if (this.MaxRequestChunkCount > 0 && this.MaxRequestChunkCount <= chunkCount)
if (this.MaxRequestChunkCount > 0 && this.MaxRequestChunkCount < chunkCount)
{
return true;
}
Expand All @@ -573,7 +573,7 @@ protected bool MessageLimitsExceeded(bool isRequest, int messageSize, int chunkC
}
else
{
if (this.MaxResponseChunkCount > 0 && this.MaxResponseChunkCount <= chunkCount)
if (this.MaxResponseChunkCount > 0 && this.MaxResponseChunkCount < chunkCount)
{
return true;
}
Expand Down Expand Up @@ -778,6 +778,28 @@ public IEncodeable MessageBody
}
#endregion

#region Protected Methods
/// <summary>
/// Calculate the chunk count which can be used for messages based on buffer size.
/// </summary>
/// <param name="messageSize">The message size to be used.</param>
/// <param name="bufferSize">The buffer available for a message.</param>
/// <returns>The chunk count.</returns>
protected static int CalculateChunkCount(int messageSize, int bufferSize)
{
if (bufferSize > 0)
{
int chunkCount = messageSize / bufferSize;
if (chunkCount * bufferSize < messageSize)
{
chunkCount++;
}
return chunkCount;
}
return 1;
}
#endregion

#region Private Fields
private object m_lock = new object();
private IMessageSocket m_socket;
Expand Down
1 change: 1 addition & 0 deletions Stack/Opc.Ua.Core/Stack/Tcp/UaSCBinaryClientChannel.cs
Expand Up @@ -439,6 +439,7 @@ private bool ProcessAcknowledgeMessage(ArraySegment<byte> messageChunk)
}

// update the max chunk count.
MaxRequestChunkCount = CalculateChunkCount(MaxRequestMessageSize, SendBufferSize);
if (maxChunkCount > 0 && maxChunkCount < MaxRequestChunkCount)
{
MaxRequestChunkCount = (int)maxChunkCount;
Expand Down
8 changes: 0 additions & 8 deletions Stack/Opc.Ua.Core/Stack/Tcp/UaSCBinaryTransportChannel.cs
Expand Up @@ -403,14 +403,6 @@ private void SaveSettings(Uri url, TransportChannelSettings settings)
m_quotas.MaxMessageSize = m_settings.Configuration.MaxMessageSize;
m_quotas.ChannelLifetime = m_settings.Configuration.ChannelLifetime;
m_quotas.SecurityTokenLifetime = m_settings.Configuration.SecurityTokenLifetime;

if (m_settings.Configuration.MaxBufferSize != 0)
{
int maxChunkCount = m_settings.Configuration.MaxMessageSize / m_settings.Configuration.MaxBufferSize;
m_quotas.MaxRequestChunkCount = maxChunkCount;
m_quotas.MaxResponseChunkCount = maxChunkCount;
}

m_quotas.MessageContext = new ServiceMessageContext() {
MaxArrayLength = m_settings.Configuration.MaxArrayLength,
MaxByteStringLength = m_settings.Configuration.MaxByteStringLength,
Expand Down

0 comments on commit 88a96a1

Please sign in to comment.