/// <summary> /// Unpack Message Type part on response message. /// </summary> /// <param name="context">Context information.</param> /// <returns> /// <c>true</c>, if the pipeline is finished; /// <c>false</c>, the pipeline is interruppted because extra data is needed. /// </returns> bool UnpackMessageType(ClientResponseContext context) { if (!context.ReadFromHeaderUnpacker()) { MsgPackRpcClientProtocolsTrace.TraceEvent(MsgPackRpcClientProtocolsTrace.NeedMessageType, "Message Type is needed. {{ \"SessionID\" : {0} }}", context.SessionId); return(false); } int numericType; try { numericType = context.HeaderUnpacker.LastReadData.AsInt32(); } catch (InvalidOperationException) { HandleDeserializationError(context, "Invalid response message stream. Message Type must be Int32 compatible integer.", () => context.UnpackingBuffer.ToArray()); return(context.nextProcess(context)); } var type = (MessageType)numericType; if (type != MessageType.Response) { HandleDeserializationError( context, string.Format(CultureInfo.CurrentCulture, "Unknown message type '{0:x8}'", numericType), () => context.UnpackingBuffer.ToArray() ); return(context.nextProcess(context)); } context.nextProcess = UnpackMessageId; return(context.nextProcess(context)); }
void DrainRemainingReceivedData(ClientResponseContext context) { // Process remaining binaries. This pipeline recursively call this method on other thread. if (!context.nextProcess(context)) { // Draining was not ended. Try to take next bytes. Receive(context); } // This method must be called on other thread on the above pipeline, so exit this thread. }
/// <summary> /// Unpack response message array header. /// </summary> /// <param name="context">Context information.</param> /// <returns> /// <c>true</c>, if the pipeline is finished; /// <c>false</c>, the pipeline is interruppted because extra data is needed. /// </returns> internal bool UnpackResponseHeader(ClientResponseContext context) { Contract.Assert(context != null); if (context.RootUnpacker == null) { context.UnpackingBuffer = new ByteArraySegmentStream(context.ReceivedData); context.RootUnpacker = Unpacker.Create(context.UnpackingBuffer, false); context.RenewSessionId(); } if (!context.ReadFromRootUnpacker()) { MsgPackRpcClientProtocolsTrace.TraceEvent(MsgPackRpcClientProtocolsTrace.NeedRequestHeader, "Array header is needed. {{ \"SessionID\" : {0} }}", context.SessionId); return(false); } if (!context.RootUnpacker.IsArrayHeader) { HandleDeserializationError(context, "Invalid response message stream. Message must be array.", () => context.UnpackingBuffer.ToArray()); return(context.nextProcess(context)); } if (context.RootUnpacker.ItemsCount != 4) { HandleDeserializationError( context, string.Format( CultureInfo.CurrentCulture, "Invalid response message stream. Message must be valid size array. Actual size is {0}.", context.RootUnpacker.ItemsCount ), () => context.UnpackingBuffer.ToArray() ); return(context.nextProcess(context)); } context.HeaderUnpacker = context.RootUnpacker.ReadSubtree(); context.nextProcess = UnpackMessageType; return(context.nextProcess(context)); }
/// <summary> /// Unpack Message ID part on response message. /// </summary> /// <param name="context">Context information.</param> /// <returns> /// <c>true</c>, if the pipeline is finished; /// <c>false</c>, the pipeline is interruppted because extra data is needed. /// </returns> bool UnpackMessageId(ClientResponseContext context) { if (!context.ReadFromHeaderUnpacker()) { MsgPackRpcClientProtocolsTrace.TraceEvent(MsgPackRpcClientProtocolsTrace.NeedMessageId, "Message ID is needed. {{ \"SessionID\" : {0} }}", context.SessionId); return(false); } try { context.MessageId = unchecked ((int)context.HeaderUnpacker.LastReadData.AsUInt32()); } catch (InvalidOperationException) { HandleDeserializationError( context, "Invalid response message stream. ID must be UInt32 compatible integer.", () => context.UnpackingBuffer.ToArray() ); return(context.nextProcess(context)); } context.nextProcess = UnpackError; return(context.nextProcess(context)); }
/// <summary> /// Unpack result part on response message. /// </summary> /// <param name="context">Context information.</param> /// <returns> /// <c>true</c>, if the pipeline is finished; /// <c>false</c>, the pipeline is interruppted because extra data is needed. /// </returns> bool UnpackResult(ClientResponseContext context) { Contract.Assert(context.UnpackingBuffer.CanSeek); if (context.resultStartAt == -1) { context.resultStartAt = context.UnpackingBuffer.Position; } var skipped = context.SkipResultSegment(); if (skipped == null) { MsgPackRpcClientProtocolsTrace.TraceEvent(MsgPackRpcClientProtocolsTrace.NeedResult, "Result value is needed. {{ \"SessionID\" : {0} }}", context.SessionId); return(false); } context.resultBuffer = new ByteArraySegmentStream(context.UnpackingBuffer.GetBuffer(context.resultStartAt, context.UnpackingBuffer.Position - context.resultStartAt)); context.nextProcess = Dispatch; return(context.nextProcess(context)); }
/// <summary> /// Dispatch response message. /// </summary> /// <param name="context">Context information.</param> /// <returns> /// <c>true</c>, if the pipeline is finished; /// <c>false</c>, the pipeline is interruppted because extra data is needed. /// </returns> bool Dispatch(ClientResponseContext context) { Contract.Assert(context.MessageId != null); try { Action <ClientResponseContext, Exception, bool> handler = null; try { pendingRequestTable.TryRemove(context.MessageId.Value, out handler); } finally { // Best effort to rescue from ThreadAbortException... if (handler != null) { handler(context, null, context.CompletedSynchronously); } else { HandleOrphan(context); } } } finally { context.ClearBuffers(); OnProcessFinished(); } if (context.UnpackingBuffer.Length > 0) { // Subsequent request is already arrived. context.nextProcess = UnpackResponseHeader; return(context.nextProcess(context)); } else { // Try receive subsequent. return(true); } }
/// <summary> /// Called when asynchronous 'Receive' operation is completed. /// </summary> /// <param name="context">Context information.</param> /// <exception cref="InvalidOperationException"> /// This instance is not in 'Idle' nor 'Receiving' state. /// </exception> /// <exception cref="ObjectDisposedException"> /// This instance is disposed. /// </exception> protected virtual void OnReceived(ClientResponseContext context) { if (context == null) { throw new ArgumentNullException(nameof(context)); } Contract.EndContractBlock(); if (MsgPackRpcClientProtocolsTrace.ShouldTrace(MsgPackRpcClientProtocolsTrace.ReceiveInboundData)) { var socket = BoundSocket; MsgPackRpcClientProtocolsTrace.TraceEvent( MsgPackRpcClientProtocolsTrace.ReceiveInboundData, "Receive response. {{ \"SessionID\" : {0}, \"Socket\" : 0x{1:X}, \"RemoteEndPoint\" : \"{2}\", \"LocalEndPoint\" : \"{3}\", \"BytesTransfered\" : {4} }}", context.SessionId, GetHandle(socket), GetRemoteEndPoint(socket, context), GetLocalEndPoint(socket), context.BytesTransferred ); } if (context.BytesTransferred == 0) { if (Interlocked.CompareExchange(ref shutdownSource, (int)ShutdownSource.Server, 0) == 0) { // Server sent shutdown response. ShutdownReceiving(); // recv() returns 0 when the server socket shutdown gracefully. var socket = BoundSocket; MsgPackRpcClientProtocolsTrace.TraceEvent( MsgPackRpcClientProtocolsTrace.DetectServerShutdown, "Server shutdown current socket. {{ \"Socket\" : 0x{0:X}, \"RemoteEndPoint\" : \"{1}\", \"LocalEndPoint\" : \"{2}\" }}", GetHandle(socket), GetRemoteEndPoint(socket, context), GetLocalEndPoint(socket) ); } else if (IsClientShutdown) { // Client was started shutdown. ShutdownReceiving(); } if (!context.ReceivedData.Any(segment => 0 < segment.Count)) { // There are no data to handle. FinishReceiving(context); return; } } else { context.ShiftCurrentReceivingBuffer(); } if (MsgPackRpcClientProtocolsTrace.ShouldTrace(MsgPackRpcClientProtocolsTrace.DeserializeResponse)) { MsgPackRpcClientProtocolsTrace.TraceEvent( MsgPackRpcClientProtocolsTrace.DeserializeResponse, "Deserialize response. {{ \"SessionID\" : {0}, \"Length\" : {1} }}", context.SessionId, context.ReceivedData.Sum(item => (long)item.Count) ); } // Go deserialization pipeline. // Exceptions here means message error. try { ApplyFilters(BeforeDeserializationFilters, context); } catch (RpcException ex) { HandleDeserializationError(context, TryDetectMessageId(context), new RpcErrorMessage(ex.RpcError, ex.Message, ex.DebugInformation), "Filter rejects message.", () => context.ReceivedData.SelectMany(s => s.AsEnumerable()).ToArray()); FinishReceiving(context); return; } if (!context.nextProcess(context)) { if (IsServerShutdown) { ShutdownReceiving(); } else if (CanResumeReceiving) { // Wait to arrive more data from server. ReceiveCore(context); return; } //this.FinishReceiving( context ); //return; } FinishReceiving(context); }