diff --git a/src/Opc.Ua.Core/Stack/Tcp/UaSCBinaryChannel.cs b/src/Opc.Ua.Core/Stack/Tcp/UaSCBinaryChannel.cs index 880d7a0af1..eb6ed265ad 100644 --- a/src/Opc.Ua.Core/Stack/Tcp/UaSCBinaryChannel.cs +++ b/src/Opc.Ua.Core/Stack/Tcp/UaSCBinaryChannel.cs @@ -820,6 +820,32 @@ namespace Opc.Ua.Bindings return TakeSavedChunks(); } + /// + /// Whether a final chunk is a whole message by itself, which most are: no + /// chunks of the message are saved, the partial message is not closed and + /// the chunk is within the message limits. The caller then processes the + /// chunk directly instead of passing it to , + /// and keeps owning its buffer. + /// + /// The body of the final chunk. + /// Whether the chunk is part of a request. + protected bool IsSingleChunkMessage(ArraySegment chunk, bool isServerContext) + { + if (chunk.Array == null) + { + return false; + } + lock (m_partialMessageLock) + { + if (m_partialMessageClosed || m_partialMessageChunks != null) + { + return false; + } + } + // a chunk over the limits takes the path that reports them. + return !MessageLimitsExceeded(isServerContext, chunk.Count, 1); + } + /// /// Detaches the chunks saved so far without offering another one. Used /// where the final chunk has already been saved and only the collection diff --git a/src/Opc.Ua.Core/Stack/Tcp/UaSCBinaryClientChannel.cs b/src/Opc.Ua.Core/Stack/Tcp/UaSCBinaryClientChannel.cs index 297e827beb..b39c58895d 100644 --- a/src/Opc.Ua.Core/Stack/Tcp/UaSCBinaryClientChannel.cs +++ b/src/Opc.Ua.Core/Stack/Tcp/UaSCBinaryClientChannel.cs @@ -2033,6 +2033,7 @@ namespace Opc.Ua.Bindings } BufferCollection? chunksToProcess = null; + ArraySegment singleChunk = default; try { // check for an abort. @@ -2062,11 +2063,18 @@ namespace Opc.Ua.Bindings return true; } - // get the chunks to process. - chunksToProcess = GetSavedChunks(requestId, messageBody, false, gateHeld: false); - - // get response. - operation.MessageBody = ParseResponse(chunksToProcess); + // get response: a response in one chunk, which most are, is decoded + // from that chunk without a collection of the chunks. + if (IsSingleChunkMessage(messageBody, isServerContext: false)) + { + singleChunk = messageBody; + operation.MessageBody = DecodeMessage(singleChunk, Quotas.MessageContext); + } + else + { + chunksToProcess = GetSavedChunks(requestId, messageBody, false, gateHeld: false); + operation.MessageBody = ParseResponse(chunksToProcess); + } if (operation.MessageBody == null) { @@ -2095,6 +2103,7 @@ namespace Opc.Ua.Bindings finally { chunksToProcess?.Release(BufferManager, "ProcessResponseMessage"); + ReturnBuffer(singleChunk, "ProcessResponseMessage"); } }