/// <summary> /// Deserializes each frame header and copies the body bytes into a single buffer. /// </summary> /// <returns>True if a full operation (streamId) has been processed.</returns> internal bool ReadParse(byte[] buffer, int length) { if (length <= 0) { return(false); } // Check if protocol version has already been determined (first message) ProtocolVersion protocolVersion; var headerLength = Volatile.Read(ref _frameHeaderSize); var serializer = Volatile.Read(ref _serializer); if (headerLength == 0) { // The server replies the first message with the max protocol version supported protocolVersion = FrameHeader.GetProtocolVersion(buffer); serializer = serializer.CloneWithProtocolVersion(protocolVersion); headerLength = protocolVersion.GetHeaderSize(); Volatile.Write(ref _serializer, serializer); Volatile.Write(ref _frameHeaderSize, headerLength); _frameHeaderSize = headerLength; } else { protocolVersion = serializer.ProtocolVersion; } // Use _readStream to buffer between messages, when the body is not contained in a single read call var stream = Interlocked.Exchange(ref _readStream, null); var previousHeader = Interlocked.Exchange(ref _receivingHeader, null); if (previousHeader != null && stream == null) { // This connection has been disposed return(false); } var operationCallbacks = new LinkedList <Action <MemoryStream, long> >(); var offset = 0; while (offset < length) { FrameHeader header; int remainingBodyLength; // check if header has not been read yet if (previousHeader == null) { header = ReadHeader(buffer, ref offset, length, headerLength, protocolVersion); if (header == null) { // There aren't enough bytes to read the header break; } Connection.Logger.Verbose("Received #{0} from {1}", header.StreamId, EndPoint.EndpointFriendlyName); remainingBodyLength = header.BodyLength; } else { header = previousHeader; previousHeader = null; remainingBodyLength = header.BodyLength - (int)stream.Length; } if (remainingBodyLength > length - offset) { // The buffer does not contains the body for the current frame, store it for later StoreReadState(header, stream, buffer, offset, length, operationCallbacks.Count > 0); break; } // Get read stream stream = stream ?? Configuration.BufferPool.GetStream(Connection.StreamReadTag); // Get callback Action <IRequestError, Response, long> callback; if (header.Opcode == EventResponse.OpCode) { callback = EventHandler; } else { var state = RemoveFromPending(header.StreamId); // State can be null when the Connection is being closed concurrently // The original callback is being called with an error, use a Noop here callback = state != null?state.SetCompleted() : OperationState.Noop; } // Write to read stream stream.Write(buffer, offset, remainingBodyLength); // Add callback with deserialize from stream operationCallbacks.AddLast(CreateResponseAction(serializer, header, callback)); offset += remainingBodyLength; } // Invoke callbacks with read stream return(Connection.InvokeReadCallbacks(stream, operationCallbacks, GetTimestamp())); }
/// <summary> /// Deserializes each frame header and copies the body bytes into a single buffer. /// </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; var headerLength = Volatile.Read(ref _frameHeaderSize); if (headerLength == 0) { // The server replies the first message with the max protocol version supported protocolVersion = FrameHeader.GetProtocolVersion(buffer); _serializer.ProtocolVersion = protocolVersion; headerLength = FrameHeader.GetSize(protocolVersion); Volatile.Write(ref _frameHeaderSize, headerLength); } else { protocolVersion = _serializer.ProtocolVersion; } // Use _readStream to buffer between messages, when the body is not contained in a single read call var stream = Interlocked.Exchange(ref _readStream, null); var previousHeader = Interlocked.Exchange(ref _receivingHeader, null); if (previousHeader != null && stream == null) { // This connection has been disposed return(false); } var operationCallbacks = new LinkedList <Action <MemoryStream> >(); var offset = 0; while (offset < length) { var header = previousHeader; int remainingBodyLength; if (header == null) { header = ReadHeader(buffer, ref offset, length, headerLength, protocolVersion); if (header == null) { // There aren't enough bytes to read the header break; } Connection.Logger.Verbose("Received #{0} from {1}", header.StreamId, Address); remainingBodyLength = header.BodyLength; } else { previousHeader = null; remainingBodyLength = header.BodyLength - (int)stream.Length; } if (remainingBodyLength > length - offset) { // The buffer does not contains the body for the current frame, store it for later StoreReadState(header, stream, buffer, offset, length, operationCallbacks.Count > 0); break; } stream = stream ?? Configuration.BufferPool.GetStream(Connection.StreamReadTag); var state = header.Opcode != EventResponse.OpCode ? RemoveFromPending(header.StreamId) : 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(Connection.InvokeReadCallbacks(stream, operationCallbacks)); }