/// <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> private 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) { this.HandleDeserializationError( context, "Invalid response message stream. ID must be UInt32 compatible integer.", () => context.UnpackingBuffer.ToArray() ); return(context.NextProcess(context)); } context.NextProcess = this.UnpackError; return(context.NextProcess(context)); }
private 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. this.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 ) { this.HandleDeserializationError( context, "Invalid response message stream. Message must be array.", () => context.UnpackingBuffer.ToArray() ); return context.NextProcess( context ); } if ( context.RootUnpacker.ItemsCount != 4 ) { this.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 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) { this.HandleDeserializationError(context, "Invalid response message stream. Message must be array.", () => context.UnpackingBuffer.ToArray()); return(context.NextProcess(context)); } if (context.RootUnpacker.ItemsCount != 4) { this.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 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> private 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) { this.HandleDeserializationError(context, "Invalid response message stream. Message Type must be Int32 compatible integer.", () => context.UnpackingBuffer.ToArray()); return(context.NextProcess(context)); } MessageType type = ( MessageType )numericType; if (type != MessageType.Response) { this.HandleDeserializationError( context, String.Format(CultureInfo.CurrentCulture, "Unknown message type '{0:x8}'", numericType), () => context.UnpackingBuffer.ToArray() ); return(context.NextProcess(context)); } context.NextProcess = this.UnpackMessageId; 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> private bool Dispatch(ClientResponseContext context) { Contract.Assert(context.MessageId != null); try { Action <ClientResponseContext, Exception, bool> handler = null; try { this._pendingRequestTable.TryRemove(context.MessageId.Value, out handler); } finally { // Best effort to rescue from ThreadAbortException... if (handler != null) { handler(context, null, context.CompletedSynchronously); } else { this.HandleOrphan(context); } } } finally { context.ClearBuffers(); this.OnProcessFinished(); } if (context.UnpackingBuffer.Length > 0) { // Subsequent request is already arrived. context.NextProcess = this.UnpackResponseHeader; return(context.NextProcess(context)); } else { // Try receive subsequent. return(true); } }
/// <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> private 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 = this.Dispatch; return(context.NextProcess(context)); }
/// <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> private 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 ) { this.HandleDeserializationError( context, "Invalid response message stream. Message Type must be Int32 compatible integer.", () => context.UnpackingBuffer.ToArray() ); return context.NextProcess( context ); } MessageType type = ( MessageType )numericType; if ( type != MessageType.Response ) { this.HandleDeserializationError( context, String.Format( CultureInfo.CurrentCulture, "Unknown message type '{0:x8}'", numericType ), () => context.UnpackingBuffer.ToArray() ); return context.NextProcess( context ); } context.NextProcess = this.UnpackMessageId; 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> private bool Dispatch( ClientResponseContext context ) { Contract.Assert( context.MessageId != null ); try { Action<ClientResponseContext, Exception, bool> handler = null; try { this._pendingRequestTable.TryRemove( context.MessageId.Value, out handler ); } finally { // Best effort to rescue from ThreadAbortException... if ( handler != null ) { handler( context, null, context.CompletedSynchronously ); } else { this.HandleOrphan( context ); } } } finally { context.ClearBuffers(); this.OnProcessFinished(); } if ( context.UnpackingBuffer.Length > 0 ) { // Subsequent request is already arrived. context.NextProcess = this.UnpackResponseHeader; return context.NextProcess( context ); } else { // Try receive subsequent. return true; } }
/// <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> private 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 = this.Dispatch; 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> private 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 ) { this.HandleDeserializationError( context, "Invalid response message stream. ID must be UInt32 compatible integer.", () => context.UnpackingBuffer.ToArray() ); return context.NextProcess( context ); } context.NextProcess = this.UnpackError; return context.NextProcess( context ); }
/// <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("context"); } Contract.EndContractBlock(); if (MsgPackRpcClientProtocolsTrace.ShouldTrace(MsgPackRpcClientProtocolsTrace.ReceiveInboundData)) { var socket = this.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 this._shutdownSource, ( int )ShutdownSource.Server, 0) == 0) { // Server sent shutdown response. this.ShutdownReceiving(); // recv() returns 0 when the server socket shutdown gracefully. var socket = this.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 (this.IsClientShutdown) { // Client was started shutdown. this.ShutdownReceiving(); } if (!context.ReceivedData.Any(segment => 0 < segment.Count)) { // There are no data to handle. this.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(this._beforeDeserializationFilters, context); } catch (RpcException ex) { this.HandleDeserializationError(context, TryDetectMessageId(context), new RpcErrorMessage(ex.RpcError, ex.Message, ex.DebugInformation), "Filter rejects message.", () => context.ReceivedData.SelectMany(s => s.AsEnumerable()).ToArray()); this.FinishReceiving(context); return; } if (!context.NextProcess(context)) { if (this.IsServerShutdown) { this.ShutdownReceiving(); } else if (this.CanResumeReceiving) { // Wait to arrive more data from server. this.ReceiveCore(context); return; } this.FinishReceiving(context); return; } }