private 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); } this.RaiseError(messageId, context.SessionId, GetRemoteEndPoint(this.BoundSocket, context), rpcError, context.CompletedSynchronously); context.NextProcess = this.DumpCorrupttedData; }
/// <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; } }
/// <summary> /// Called when asynchronous 'Send' operation is completed. /// </summary> /// <param name="context">Context information.</param> /// <returns> /// <c>true</c>, if the subsequent request is already received; /// <c>false</c>, otherwise. /// </returns> /// <exception cref="InvalidOperationException"> /// This instance is not in 'Sending' state. /// </exception> /// <exception cref="ObjectDisposedException"> /// This instance is disposed. /// </exception> protected virtual void OnSent(ClientRequestContext context) { if (MsgPackRpcClientProtocolsTrace.ShouldTrace(MsgPackRpcClientProtocolsTrace.SentOutboundData)) { var socket = this.BoundSocket; MsgPackRpcClientProtocolsTrace.TraceEvent( MsgPackRpcClientProtocolsTrace.SentOutboundData, "Sent request/notification. {{ \"SessionID\" : {0}, \"Socket\" : 0x{1:X}, \"RemoteEndPoint\" : \"{2}\", \"LocalEndPoint\" : \"{3}\", \"Type\" : \"{4}\", \"MessageID\" : {5}, \"Method\" : \"{6}\", \"BytesTransferred\" : {7} }}", context.SessionId, GetHandle(socket), GetRemoteEndPoint(socket, context), GetLocalEndPoint(socket), context.MessageType, context.MessageId, context.MethodName, context.BytesTransferred ); } context.StopWatchTimeout(); context.Timeout -= this.OnSendTimeout; context.ClearBuffers(); if (context.MessageType == MessageType.Notification) { try { Action <Exception, bool> handler = null; try { this._pendingNotificationTable.TryRemove(context.SessionId, out handler); } finally { var rpcError = context.SocketError.ToClientRpcError(); if (handler != null) { handler(rpcError.IsSuccess ? null : rpcError.ToException(), context.CompletedSynchronously); } } } finally { this.Manager.ReturnRequestContext(context); this.OnProcessFinished(); this.Manager.ReturnTransport(this); } } else { if (this.Manager.Configuration.WaitTimeout != null && (this.Manager.Configuration.WaitTimeout.Value - context.ElapsedTime).TotalMilliseconds < 1.0) { this.OnWaitTimeout(context); this.Manager.ReturnRequestContext(context); this.Manager.ReturnTransport(this); return; } var responseContext = this.Manager.GetResponseContext(this, context.RemoteEndPoint); if (this.Manager.Configuration.WaitTimeout != null) { responseContext.Timeout += this.OnReceiveTimeout; responseContext.StartWatchTimeout(this.Manager.Configuration.WaitTimeout.Value - context.ElapsedTime); } this.Manager.ReturnRequestContext(context); this.Receive(responseContext); } }
/// <summary> /// Sends a request or notification message with the specified context. /// </summary> /// <param name="context">The context information.</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> /// <exception cref="ObjectDisposedException"> /// This instance has been disposed. /// </exception> /// <exception cref="InvalidOperationException"> /// This instance is in shutdown. /// Or the message ID or session ID is duplicated. /// </exception> /// <exception cref="RpcException"> /// Failed to send request or notification to the server. /// </exception> public void Send(ClientRequestContext context) { if (context == null) { throw new ArgumentNullException("context"); } if (!Object.ReferenceEquals(context.BoundTransport, this)) { throw new ArgumentException("Context is not bound to this object.", "context"); } this.VerifyIsNotDisposed(); if (this.IsClientShutdown) { throw new InvalidOperationException("This transport is in shutdown."); } Contract.EndContractBlock(); if (this.IsServerShutdown) { throw new RpcErrorMessage(RpcError.TransportError, "Server did shutdown socket.", null).ToException(); } context.Prepare(this.CanUseChunkedBuffer); if (context.MessageType == MessageType.Request) { if (!this._pendingRequestTable.TryAdd(context.MessageId.Value, context.RequestCompletionCallback)) { throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, "Message ID '{0}' is already used.", context.MessageId)); } } else { if (!this._pendingNotificationTable.TryAdd(context.SessionId, context.NotificationCompletionCallback)) { throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, "Session ID '{0}' is already used.", context.MessageId)); } } if (MsgPackRpcClientProtocolsTrace.ShouldTrace(MsgPackRpcClientProtocolsTrace.SendOutboundData)) { var socket = this.BoundSocket; MsgPackRpcClientProtocolsTrace.TraceEvent( MsgPackRpcClientProtocolsTrace.SendOutboundData, "Send request/notification. {{ \"SessionID\" : {0}, \"Socket\" : 0x{1:X}, \"RemoteEndPoint\" : \"{2}\", \"LocalEndPoint\" : \"{3}\", \"Type\" : \"{4}\", \"MessageID\" : {5}, \"Method\" : \"{6}\", \"BytesTransferring\" : {7} }}", context.SessionId, GetHandle(socket), GetRemoteEndPoint(socket, context), GetLocalEndPoint(socket), context.MessageType, context.MessageId, context.MethodName, context.SendingBuffer.Sum(segment => ( long )segment.Count) ); } // Because exceptions here means client error, it should be handled like other client error. // Therefore, no catch clauses here. ApplyFilters(this._afterSerializationFilters, context); if (this.Manager.Configuration.WaitTimeout != null) { context.Timeout += this.OnSendTimeout; context.StartWatchTimeout(this.Manager.Configuration.WaitTimeout.Value); } this.SendCore(context); }