static int?TryDetectMessageId(ClientResponseContext context) { if (context.MessageId != null) { return(context.MessageId); } using var stream = new ByteArraySegmentStream(context.ReceivedData); using var unpacker = Unpacker.Create(stream); if (!unpacker.Read() || !unpacker.IsArrayHeader || unpacker.LastReadData != 4) { // Not a response message return(null); } if (!unpacker.Read() || !unpacker.LastReadData.IsTypeOf <int>().GetValueOrDefault() || unpacker.LastReadData != (int)MessageType.Response) { // Not a response message or invalid message type return(null); } if (!unpacker.Read() || !unpacker.LastReadData.IsTypeOf <int>().GetValueOrDefault()) { // Invalid message ID. return(null); } return(unpacker.LastReadData.AsInt32()); }
/// <summary> /// Receives byte stream from remote end point. /// </summary> /// <param name="context">Context information.</param> /// <exception cref="InvalidOperationException"> /// This instance is not in 'Idle' state. /// </exception> /// <exception cref="ObjectDisposedException"> /// This instance is disposed. /// </exception> void Receive(ClientResponseContext context) { Contract.Assert(context != null); Contract.Assert(context.BoundTransport == this, "Context is not bound to this object."); // First, drain last received request. if (context.ReceivedData.Any(segment => 0 < segment.Count)) { DrainRemainingReceivedData(context); } else { // There might be dirty data due to client shutdown. context.ReceivedData.Clear(); Array.Clear(context.CurrentReceivingBuffer, 0, context.CurrentReceivingBuffer.Length); context.PrepareReceivingBuffer(); var socket = BoundSocket; MsgPackRpcClientProtocolsTrace.TraceEvent( MsgPackRpcClientProtocolsTrace.BeginReceive, "Receive inbound data. {{ \"Socket\" : 0x{0:X}, \"RemoteEndPoint\" : \"{1}\", \"LocalEndPoint\" : \"{2}\" }}", GetHandle(socket), GetRemoteEndPoint(socket, context), GetLocalEndPoint(socket) ); ReceiveCore(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> 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 FinishReceiving(ClientResponseContext context) { context.StopWatchTimeout(); context.Timeout -= OnReceiveTimeout; Manager.ReturnResponseContext(context); //this.Manager.ReturnTransport( this ); }
/// <summary> /// Performs protocol specific asynchronous 'Receive' operation. /// </summary> /// <param name="context">Context information.</param> protected sealed override void ReceiveCore(ClientResponseContext context) { if (!BoundSocket.ReceiveFromAsync(context.SocketContext)) { context.SetCompletedSynchronously(); OnReceived(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> /// Returns the response context to the pool. /// </summary> /// <param name="context">The response to the pool.</param> /// <exception cref="ArgumentNullException"> /// <paramref name="context"/> is <c>null</c>. /// </exception> protected internal void ReturnResponseContext(ClientResponseContext context) { if (context == null) { throw new ArgumentNullException(nameof(context)); } Contract.EndContractBlock(); context.Clear(); context.UnboundTransport(); ResponseContextPool.Return(context); }
void HandleOrphan(ClientResponseContext context) { var error = ErrorInterpreter.UnpackError(context); MessagePackObject?returnValue = null; if (error.IsSuccess) { returnValue = Unpacking.UnpackObject(context.resultBuffer); } HandleOrphan(context.MessageId, context.SessionId, GetRemoteEndPoint(BoundSocket, context), error, returnValue); }
/// <summary> /// Returns the specified context to the <see cref="Manager"/>. /// </summary> /// <param name="context">The <see cref="ClientResponseContext"/> to be returned.</param> /// <exception cref="ArgumentNullException"> /// <paramref name="context"/> is <c>null</c>. /// </exception> /// <exception cref="ArgumentException"> /// <paramref name="context"/> is not bound to this transport. /// </exception> public void ReturnContext(ClientResponseContext context) { if (context == null) { throw new ArgumentNullException(nameof(context)); } if (!ReferenceEquals(context.BoundTransport, this)) { throw new ArgumentException("Context is not bound to this transport.", nameof(context)); } Contract.EndContractBlock(); Manager.ReturnResponseContext(context); }
internal bool DumpCorrupttedData(ClientResponseContext context) { if (context.BytesTransferred == 0) { context.Clear(); return(false); } if (Manager.Configuration.DumpCorruptResponse) { using var dumpStream = OpenDumpStream(context.SessionStartedAt, context.RemoteEndPoint, context.SessionId, MessageType.Response, context.MessageId); dumpStream.Write(context.CurrentReceivingBuffer, context.CurrentReceivingBufferOffset, context.BytesTransferred); dumpStream.Flush(); } context.ShiftCurrentReceivingBuffer(); return(true); }
void HandleDeserializationError(ClientResponseContext context, int?messageId, RpcErrorMessage rpcError, string message, Func <byte[]> invalidRequestHeaderProvider) { MsgPackRpcClientProtocolsTrace.TraceRpcError( rpcError.Error, "Deserialization error. {0} {{ \"Message ID\" : {1}, \"Error\" : {2} }}", message, messageId == null ? "(null)" : messageId.ToString(), rpcError ); if (invalidRequestHeaderProvider != null && MsgPackRpcClientProtocolsTrace.ShouldTrace(MsgPackRpcClientProtocolsTrace.DumpInvalidResponseHeader)) { var array = invalidRequestHeaderProvider(); MsgPackRpcClientProtocolsTrace.TraceData(MsgPackRpcClientProtocolsTrace.DumpInvalidResponseHeader, BitConverter.ToString(array), array); } RaiseError(messageId, context.SessionId, GetRemoteEndPoint(BoundSocket, context), rpcError, context.CompletedSynchronously); context.nextProcess = DumpCorrupttedData; }
/// <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 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> /// 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)); }
static bool InvalidFlow(ClientResponseContext context) { throw new InvalidOperationException("Invalid state transition."); }
void HandleDeserializationError(ClientResponseContext context, string message, Func <byte[]> invalidRequestHeaderProvider) { HandleDeserializationError(context, context.MessageId, new RpcErrorMessage(RpcError.RemoteRuntimeError, "Invalid stream.", message), message, invalidRequestHeaderProvider); }
/// <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); }
/// <summary> /// Performs protocol specific asynchronous 'Receive' operation. /// </summary> /// <param name="context">Context information.</param> protected abstract void ReceiveCore(ClientResponseContext context);