/// <summary> /// Parses the bytes received into a frame. Uses the internal operation state to do the callbacks. /// Returns true if a full operation (streamId) has been processed and there is one available. /// </summary> /// <returns>True if a full operation (streamId) has been processed.</returns> internal bool ReadParse(byte[] buffer, int length) { if (length <= 0) { return(false); } ProtocolVersion protocolVersion; if (_frameHeaderSize == 0) { //The server replies the first message with the max protocol version supported protocolVersion = FrameHeader.GetProtocolVersion(buffer); _serializer.ProtocolVersion = protocolVersion; _frameHeaderSize = FrameHeader.GetSize(protocolVersion); } else { protocolVersion = _serializer.ProtocolVersion; } //Use _readStream to buffer between messages, under low pressure, it should be null most of the times var stream = Interlocked.Exchange(ref _readStream, null); var operationCallbacks = new LinkedList <Action <MemoryStream> >(); var offset = 0; if (_minimalBuffer != null) { //use a negative offset to identify that there is a previous header buffer offset = -1 * _minimalBuffer.Length; } while (offset < length) { FrameHeader header; //The remaining body length to read from this buffer int remainingBodyLength; if (_receivingHeader == null) { if (length - offset < _frameHeaderSize) { _minimalBuffer = offset >= 0 ? Utils.SliceBuffer(buffer, offset, length - offset) : //it should almost never be the case there isn't enough bytes to read the header more than once // ReSharper disable once PossibleNullReferenceException Utils.JoinBuffers(_minimalBuffer, 0, _minimalBuffer.Length, buffer, 0, length); break; } if (offset >= 0) { header = FrameHeader.ParseResponseHeader(protocolVersion, buffer, offset); } else { header = FrameHeader.ParseResponseHeader(protocolVersion, _minimalBuffer, buffer); _minimalBuffer = null; } Logger.Verbose("Received #{0} from {1}", header.StreamId, Address); offset += _frameHeaderSize; remainingBodyLength = header.BodyLength; } else { header = _receivingHeader; remainingBodyLength = header.BodyLength - (int)stream.Length; _receivingHeader = null; } if (remainingBodyLength > length - offset) { //the buffer does not contains the body for this frame, buffer for later MemoryStream nextMessageStream; if (operationCallbacks.Count == 0 && stream != null) { //There hasn't been any operations completed with this buffer //And there is a previous stream: reuse it nextMessageStream = stream; } else { nextMessageStream = Configuration.BufferPool.GetStream(typeof(Connection) + "/Read"); } nextMessageStream.Write(buffer, offset, length - offset); Interlocked.Exchange(ref _readStream, nextMessageStream); _receivingHeader = header; break; } stream = stream ?? Configuration.BufferPool.GetStream(typeof(Connection) + "/Read"); OperationState state; if (header.Opcode != EventResponse.OpCode) { state = RemoveFromPending(header.StreamId); } else { //Its an event state = new OperationState(EventHandler); } stream.Write(buffer, offset, remainingBodyLength); // State can be null when the Connection is being closed concurrently // The original callback is being called with an error, use a Noop here var callback = state != null?state.SetCompleted() : OperationState.Noop; operationCallbacks.AddLast(CreateResponseAction(header, callback)); offset += remainingBodyLength; } return(InvokeReadCallbacks(stream, operationCallbacks)); }