private void OnConnected(Socket connectSocket, SocketAsyncEventArgs context, TaskCompletionSource <ClientTransport> taskCompletionSource) { try { if (connectSocket == null || !connectSocket.Connected) { // canceled. taskCompletionSource.SetException( new RpcTransportException( RpcError.ConnectionTimeoutError, "Connect timeout.", String.Format(CultureInfo.CurrentCulture, "Timeout: {0}", this.Configuration.ConnectTimeout) ) ); return; } #if !API_SIGNATURE_TEST MsgPackRpcClientProtocolsTrace.TraceEvent( MsgPackRpcClientProtocolsTrace.EndConnect, "Connected. {{ \"Socket\" : 0x{0:X}, \"RemoteEndPoint\" : \"{1}\", \"LocalEndPoint\" : \"{2}\" }}", ClientTransport.GetHandle(connectSocket), ClientTransport.GetRemoteEndPoint(connectSocket, context), ClientTransport.GetLocalEndPoint(connectSocket) ); #endif taskCompletionSource.SetResult(this.GetTransport(connectSocket)); } finally { context.Dispose(); } }
/// <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> /// 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> private 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)) { this.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 = this.BoundSocket; MsgPackRpcClientProtocolsTrace.TraceEvent( MsgPackRpcClientProtocolsTrace.BeginReceive, "Receive inbound data. {{ \"Socket\" : 0x{0:X}, \"RemoteEndPoint\" : \"{1}\", \"LocalEndPoint\" : \"{2}\" }}", GetHandle(socket), GetRemoteEndPoint(socket, context), GetLocalEndPoint(socket) ); this.ReceiveCore(context); } }
private void OnCompleted(object sender, SocketAsyncEventArgs e) { var socket = sender as Socket; #if MONO var userToken = e.UserToken as Tuple <TaskCompletionSource <ClientTransport>, ConnectTimeoutWatcher, Socket>; #else var userToken = e.UserToken as Tuple <TaskCompletionSource <ClientTransport>, ConnectTimeoutWatcher>; #endif var taskCompletionSource = userToken.Item1; var watcher = userToken.Item2; if (watcher != null) { this.EndConnectTimeoutWatch(watcher); } #if MONO var error = this.HandleSocketError(userToken.Item3 ?? socket, e); #else var error = this.HandleSocketError(e.ConnectSocket ?? socket, e); #endif if (error != null) { taskCompletionSource.SetException(error.Value.ToException()); return; } switch (e.LastOperation) { case SocketAsyncOperation.Connect: { #if MONO this.OnConnected(userToken.Item3, e, taskCompletionSource); #else this.OnConnected(e.ConnectSocket, e, taskCompletionSource); #endif break; } default: { #if !API_SIGNATURE_TEST MsgPackRpcClientProtocolsTrace.TraceEvent( MsgPackRpcClientProtocolsTrace.UnexpectedLastOperation, "Unexpected operation. {{ \"Socket\" : 0x{0:X}, \"RemoteEndPoint\" : \"{1}\", \"LocalEndPoint\" : \"{2}\", \"LastOperation\" : \"{3}\" }}", ClientTransport.GetHandle(socket), ClientTransport.GetRemoteEndPoint(socket, e), ClientTransport.GetLocalEndPoint(socket), e.LastOperation ); #endif taskCompletionSource.SetException(new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, "Unknown socket operation : {0}", e.LastOperation))); break; } } }
/// <summary> /// When overridden in the derived class, shutdowns the sending. /// </summary> protected virtual void ShutdownSending() { var socket = this.BoundSocket; MsgPackRpcClientProtocolsTrace.TraceEvent( MsgPackRpcClientProtocolsTrace.ShutdownSending, "Shutdown sending. {{ \"Socket\" : 0x{0:X}, \"RemoteEndPoint\" : \"{1}\", \"LocalEndPoint\" : \"{2}\" }}", GetHandle(socket), GetRemoteEndPoint(socket, default(MessageContext)), GetLocalEndPoint(socket) ); }
private void OnSocketOperationCompleted(object sender, SocketAsyncEventArgs e) { var socket = sender as Socket; var context = e.GetContext(); if (!this.HandleSocketError(socket, context)) { return; } switch (context.LastOperation) { case SocketAsyncOperation.Send: #if !SILVERLIGHT case SocketAsyncOperation.SendTo: case SocketAsyncOperation.SendPackets: #endif { var requestContext = context as ClientRequestContext; Contract.Assert(requestContext != null); this.OnSent(requestContext); break; } case SocketAsyncOperation.Receive: #if !SILVERLIGHT case SocketAsyncOperation.ReceiveFrom: case SocketAsyncOperation.ReceiveMessageFrom: #endif { var responseContext = context as ClientResponseContext; Contract.Assert(responseContext != null); this.OnReceived(responseContext); break; } default: { MsgPackRpcClientProtocolsTrace.TraceEvent( MsgPackRpcClientProtocolsTrace.UnexpectedLastOperation, "Unexpected operation. {{ \"Socket\" : 0x{0:X}, \"RemoteEndPoint\" : \"{1}\", \"LocalEndPoint\" : \"{2}\", \"LastOperation\" : \"{3}\" }}", GetHandle(socket), GetRemoteEndPoint(socket, e), GetLocalEndPoint(socket), context.LastOperation ); break; } } }
/// <summary> /// When overridden in the derived class, shutdowns the receiving. /// </summary> protected virtual void ShutdownReceiving() { var socket = this.BoundSocket; MsgPackRpcClientProtocolsTrace.TraceEvent( MsgPackRpcClientProtocolsTrace.ShutdownReceiving, "Shutdown receiving. {{ \"Socket\" : 0x{0:X}, \"RemoteEndPoint\" : \"{1}\", \"LocalEndPoint\" : \"{2}\" }}", GetHandle(socket), GetRemoteEndPoint(socket, default(MessageContext)), GetLocalEndPoint(socket) ); this.OnShutdownCompleted(new ShutdownCompletedEventArgs(( ShutdownSource )this._shutdownSource)); }
/// <summary> /// Raises internal shutdown completion routine. /// </summary> /// <param name="e">The <see cref="MsgPack.Rpc.Protocols.ShutdownCompletedEventArgs"/> instance containing the event data.</param> /// <exception cref="ArgumentNullException"> /// <paramref name="e"/> is <c>null</c>. /// </exception> protected virtual void OnShutdownCompleted(ShutdownCompletedEventArgs e) { if (e == null) { throw new ArgumentNullException("e"); } Contract.EndContractBlock(); var socket = Interlocked.Exchange(ref this._boundSocket, null); MsgPackRpcClientProtocolsTrace.TraceEvent( MsgPackRpcClientProtocolsTrace.TransportShutdownCompleted, "Transport shutdown is completed. {{ \"Socket\" : 0x{0:X}, \"RemoteEndPoint\" : \"{1}\", \"LocalEndPoint\" : \"{2}\" }}", GetHandle(socket), GetRemoteEndPoint(socket, default(MessageContext)), GetLocalEndPoint(socket) ); if (socket != null) { socket.Close(); } // Notify shutdown to waiting clients. // Note that it is OK from concurrency point of view because additional modifications are guarded via shutdown flag. var errorMessage = new RpcErrorMessage(RpcError.TransportError, String.Format(CultureInfo.CurrentCulture, "Transport is shutdown. Shutdown source is: {0}", e.Source), null); foreach (var entry in this._pendingRequestTable) { entry.Value(null, errorMessage.ToException(), false); } foreach (var entry in this._pendingNotificationTable) { entry.Value(errorMessage.ToException(), false); } this._pendingRequestTable.Clear(); this._pendingNotificationTable.Clear(); var handler = Interlocked.CompareExchange(ref this._shutdownCompleted, null, null); if (handler != null) { handler(this, e); } }
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; }
protected internal RpcErrorMessage?HandleSocketError(Socket socket, SocketAsyncEventArgs context) { if (context.SocketError.IsError() == false) { return(null); } MsgPackRpcClientProtocolsTrace.TraceEvent( MsgPackRpcClientProtocolsTrace.SocketError, "Socket error. {{ \"Socket\" : 0x{0:X}, \"RemoteEndpoint\" : \"{1}\", \"LocalEndpoint\" : \"{2}\", \"LastOperation\" : \"{3}\", \"SocketError\" : \"{4}\", \"ErrorCode\" : 0x{5:X} }}", ClientTransport.GetHandle(socket), ClientTransport.GetRemoteEndPoint(socket, context), ClientTransport.GetLocalEndPoint(socket), context.LastOperation, context.SocketError, ( int )context.SocketError ); return(context.SocketError.ToClientRpcError()); }
/// <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 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)); }
private void OnWaitTimeout(MessageContext context) { Contract.Assert(context != null); var asClientRequestContext = context as ClientRequestContext; var socket = this.BoundSocket; MsgPackRpcClientProtocolsTrace.TraceEvent( MsgPackRpcClientProtocolsTrace.WaitTimeout, "Wait timeout. {{ \"Socket\" : 0x{0:X}, \"RemoteEndPoint\" : \"{1}\", \"LocalEndPoint\" : \"{2}\", \"Operation\" : \"{3}\", \"MessageType\" : \"{4}\" \"MessageId\" : {5}, \"BytesTransferred\" : {6}, \"Timeout\" : \"{7}\" }}", GetHandle(socket), GetRemoteEndPoint(socket, context), GetLocalEndPoint(socket), asClientRequestContext != null ? "Send" : "Receive", asClientRequestContext == null ? MessageType.Response : asClientRequestContext.MessageType, context.MessageId, context.BytesTransferred, this._manager.Configuration.WaitTimeout ); var rpcError = new RpcErrorMessage( RpcError.TimeoutError, new MessagePackObject( new MessagePackObjectDictionary(3) { { RpcException.MessageKeyUtf8, asClientRequestContext != null ? "Wait timeout on sending." : "Wait timeout on receiving." }, { RpcException.DebugInformationKeyUtf8, String.Empty }, { RpcTimeoutException.ClientTimeoutKeyUtf8, this.Manager.Configuration.WaitTimeout.Value.Ticks } }, true ) ); this.RaiseError(context.MessageId, context.SessionId, GetRemoteEndPoint(socket, context), rpcError, false); this.ResetConnection(); }
private void HandleOrphan(int?messageId, long sessionId, EndPoint remoteEndPoint, RpcErrorMessage rpcError, MessagePackObject?returnValue) { var socket = this.BoundSocket; MsgPackRpcClientProtocolsTrace.TraceEvent( MsgPackRpcClientProtocolsTrace.OrphanError, "There are no handlers to handle message which has MessageID:{0}, SessionID:{1}. This may indicate runtime problem or due to client recycling. {{ \"Socket\" : 0x{2:X}, \"RemoteEndPoint\" : \"{3}\", \"LocalEndPoint\" : \"{4}\", \"SessionID\" :{1}, \"MessageID\" : {0}, \"Error\" : {5}, \"ReturnValue\" : {6}, \"CallStack\" : \"{7}\" }}", messageId == null ? "(null)" : messageId.Value.ToString(CultureInfo.InvariantCulture), sessionId, GetHandle(socket), remoteEndPoint, GetLocalEndPoint(socket), rpcError, returnValue, #if !SILVERLIGHT new StackTrace(0, true) #else new StackTrace() #endif ); this._manager.HandleOrphan(messageId, rpcError, returnValue); }
/// <summary> /// Releases unmanaged and - optionally - managed resources. /// </summary> /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param> protected virtual void Dispose(bool disposing) { if (disposing) { if (Interlocked.Exchange(ref this._isDisposed, 1) == 0) { try { var socket = this.BoundSocket; MsgPackRpcClientProtocolsTrace.TraceEvent( MsgPackRpcClientProtocolsTrace.DisposeTransport, "Dispose transport. {{ \"Socket\" : 0x{0:X}, \"RemoteEndPoint\" : \"{1}\", \"LocalEndPoint\" : \"{2}\" }}", GetHandle(socket), GetRemoteEndPoint(socket, default(MessageContext)), GetLocalEndPoint(socket) ); if (Interlocked.CompareExchange(ref this._shutdownSource, ( int )ShutdownSource.Disposing, 0) == 0) { var closingSocket = Interlocked.Exchange(ref this._boundSocket, null); if (closingSocket != null) { closingSocket.Close(); } } } catch (ObjectDisposedException) { MsgPackRpcClientProtocolsTrace.TraceEvent( MsgPackRpcClientProtocolsTrace.DisposeTransport, "Dispose transport. {{ \"Socket\" : \"Disposed\", \"RemoteEndPoint\" : \"Disposed\", \"LocalEndPoint\" : \"Disposed\" }}" ); } } } }
/// <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> /// Establishes logical connection, which specified to the managed transport protocol, for the server. /// </summary> /// <param name="targetEndPoint">The end point of target server.</param> /// <returns> /// <see cref="Task{T}"/> of <see cref="ClientTransport"/> which represents asynchronous establishment process specific to the managed transport. /// This value will not be <c>null</c>. /// </returns> protected sealed override Task <ClientTransport> ConnectAsyncCore(EndPoint targetEndPoint) { TaskCompletionSource <ClientTransport> source = new TaskCompletionSource <ClientTransport>(); var context = new SocketAsyncEventArgs(); context.RemoteEndPoint = targetEndPoint; context.Completed += this.OnCompleted; #if !API_SIGNATURE_TEST MsgPackRpcClientProtocolsTrace.TraceEvent( MsgPackRpcClientProtocolsTrace.BeginConnect, "Connecting. {{ \"EndPoint\" : \"{0}\", \"AddressFamily\" : {1}, \"PreferIPv4\" : {2}, \"OSSupportsIPv6\" : {3} }}", targetEndPoint, targetEndPoint.AddressFamily, this.Configuration.PreferIPv4, Socket.OSSupportsIPv6 ); #endif #if MONO var connectingSocket = new Socket(targetEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); #endif context.UserToken = Tuple.Create( source, this.BeginConnectTimeoutWatch( () => { #if !API_SIGNATURE_TEST MsgPackRpcClientProtocolsTrace.TraceEvent( MsgPackRpcClientProtocolsTrace.ConnectTimeout, "Connect timeout. {{ \"EndPoint\" : \"{0}\", \"AddressFamily\" : {1}, \"PreferIPv4\" : {2}, \"OSSupportsIPv6\" : {3}, \"ConnectTimeout\" : {4} }}", targetEndPoint, targetEndPoint.AddressFamily, this.Configuration.PreferIPv4, Socket.OSSupportsIPv6, this.Configuration.ConnectTimeout ); #endif #if MONO // Cancel ConnectAsync. connectingSocket.Close(); #else Socket.CancelConnectAsync(context); #endif } ) #if MONO , connectingSocket #endif ); #if MONO if (!connectingSocket.ConnectAsync(context)) #else if (!Socket.ConnectAsync(SocketType.Stream, ProtocolType.Tcp, context)) #endif { this.OnCompleted(null, context); } return(source.Task); }
/// <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); }