/// <summary> /// Unpack request/notification 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 UnpackRequestHeader( ServerRequestContext context ) { Contract.Assert( context != null ); if ( context.RootUnpacker == null ) { context.UnpackingBuffer = new ByteArraySegmentStream( context.ReceivedData ); context.RootUnpacker = Unpacker.Create( context.UnpackingBuffer, false ); Interlocked.Increment( ref this._processing ); context.RenewSessionId(); if ( this._manager.Server.Configuration.ReceiveTimeout != null ) { context.Timeout += this.OnReceiveTimeout; context.StartWatchTimeout( this._manager.Server.Configuration.ReceiveTimeout.Value ); } if ( MsgPackRpcServerProtocolsTrace.ShouldTrace( MsgPackRpcServerProtocolsTrace.NewSession ) ) { MsgPackRpcServerProtocolsTrace.TraceEvent( MsgPackRpcServerProtocolsTrace.NewSession, "New session is created. {{ \"SessionID\" : {0} }}", context.SessionId ); } } if ( !context.ReadFromRootUnpacker() ) { MsgPackRpcServerProtocolsTrace.TraceEvent( MsgPackRpcServerProtocolsTrace.NeedRequestHeader, "Array header is needed. {{ \"SessionID\" : {0} }}", context.SessionId ); return false; } if ( !context.RootUnpacker.IsArrayHeader ) { this.HandleDeserializationError( context, "Invalid request/notify message stream. Message must be array.", () => context.UnpackingBuffer.ToArray() ); return true; } if ( context.RootUnpacker.ItemsCount != 3 && context.RootUnpacker.ItemsCount != 4 ) { this.HandleDeserializationError( context, String.Format( CultureInfo.CurrentCulture, "Invalid request/notify message stream. Message must be valid size array. Actual size is {0}.", context.RootUnpacker.ItemsCount ), () => context.UnpackingBuffer.ToArray() ); return true; } context.HeaderUnpacker = context.RootUnpacker.ReadSubtree(); context.NextProcess = UnpackMessageType; return context.NextProcess( context ); }
private static ServerRequestContext CreateRequestContext( MemoryStream arguments ) { var result = new ServerRequestContext(); arguments.CopyTo( result.ArgumentsBuffer ); result.ArgumentsBuffer.Position = 0; result.ArgumentsUnpacker = Unpacker.Create( result.ArgumentsBuffer, false ); return result; }
private static void TestCore( Action<ServerRequestContext, ServerTransport> test ) { using ( var server = new RpcServer() ) using ( var manager = new NullServerTransportManager( server ) ) using ( var transport = new NullServerTransport( manager ) ) using ( var target = new ServerRequestContext() ) { test( target, transport ); } }
/// <summary> /// Returns the request context to the pool. /// </summary> /// <param name="context">The context to the pool.</param> /// <exception cref="ArgumentNullException"> /// <paramref name="context"/> is <c>null</c>. /// </exception> protected internal void ReturnRequestContext(ServerRequestContext context) { if (context == null) { throw new ArgumentNullException("context"); } Contract.EndContractBlock(); context.Clear(); context.UnboundTransport(); this.RequestContextPool.Return(context); }
protected override void ReceiveCore(ServerRequestContext context) { this.OnReceiving(); try { InProcPacket.ProcessReceive(this._inboundQueue, this._pendingPackets, context, this._receivingCancellationTokenSource.Token); } catch (OperationCanceledException) { context.SocketError = SocketError.OperationAborted; } this.OnReceived(context); this.OnReceived(); }
internal void StartReceive(ServerRequestContext context) { if (this._receivingTask == null) { Contract.Assert(context.BoundTransport == this); this._receivingTask = Task.Factory.StartNew( this.DoReceiveLoop, context, this._receivingCancellationTokenSource.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default ); } }
/// <summary> /// Unpack array header of Arguments part on request/notification 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 UnpackArgumentsHeader(ServerRequestContext context) { Contract.Assert(context != null); if (!context.ReadFromHeaderUnpacker()) { MsgPackRpcServerProtocolsTrace.TraceEvent( MsgPackRpcServerProtocolsTrace.NeedArgumentsArrayHeader, "Arguments array header is needed. {{ \"SessionID\" : {0} }}", context.SessionId ); return(false); } if (!context.HeaderUnpacker.IsArrayHeader) { this.HandleDeserializationError( context, "Invalid request/notify message stream. Arguments must be array.", () => context.UnpackingBuffer.ToArray() ); return(true); } // TODO: Avoid actual unpacking to improve performance. context.ArgumentsBufferUnpacker = context.HeaderUnpacker.ReadSubtree(); if (Int32.MaxValue < context.ArgumentsBufferUnpacker.ItemsCount) { this.HandleDeserializationError( context, RpcError.MessageTooLargeError, "Too many arguments.", context.ArgumentsBufferUnpacker.ItemsCount.ToString("#,0", CultureInfo.CurrentCulture), () => context.UnpackingBuffer.ToArray() ); return(true); } context.ArgumentsCount = unchecked (( int )(context.ArgumentsBufferUnpacker.ItemsCount)); context.ArgumentsBufferPacker = Packer.Create(context.ArgumentsBuffer, false); context.ArgumentsBufferPacker.PackArrayHeader(context.ArgumentsCount); context.UnpackedArgumentsCount = 0; context.NextProcess = this.UnpackArguments; return(context.NextProcess(context)); }
private static void InitializeBuffers(ServerRequestContext target) { target.ArgumentsBuffer.WriteByte(1); target.ArgumentsBufferPacker = Packer.Create(target.ArgumentsBuffer, false); target.ArgumentsBufferUnpacker = Unpacker.Create(target.ArgumentsBuffer, false); target.ArgumentsCount = 2; target.UnpackingBuffer = new ByteArraySegmentStream(target.ReceivedData); target.UnpackedArgumentsCount = 3; target.RootUnpacker = Unpacker.Create(target.UnpackingBuffer, false); target.HeaderUnpacker = Unpacker.Create(target.UnpackingBuffer, false); target.ArgumentsUnpacker = Unpacker.Create(target.UnpackingBuffer, false); target.SetCompletedSynchronously(); target.MessageId = 123; target.MessageType = MessageType.Request; target.MethodName = "Method"; target.NextProcess = _ => true; }
private static void InitializeBuffers( ServerRequestContext target ) { target.ArgumentsBuffer.WriteByte( 1 ); target.ArgumentsBufferPacker = Packer.Create( target.ArgumentsBuffer, false ); target.ArgumentsBufferUnpacker = Unpacker.Create( target.ArgumentsBuffer, false ); target.ArgumentsCount = 2; target.UnpackingBuffer = new ByteArraySegmentStream( target.ReceivedData ); target.UnpackedArgumentsCount = 3; target.RootUnpacker = Unpacker.Create( target.UnpackingBuffer, false ); target.HeaderUnpacker = Unpacker.Create( target.UnpackingBuffer, false ); target.ArgumentsUnpacker = Unpacker.Create( target.UnpackingBuffer, false ); target.SetCompletedSynchronously(); target.MessageId = 123; target.MessageType = MessageType.Request; target.MethodName = "Method"; target.NextProcess = _ => true; }
public void TestRaiseServerError_RpcServerServerErrorEventOccurredWithSpeicifiedValues() { bool occurred = false; using ( var server = new RpcServer() ) using ( var context = new ServerRequestContext() ) { var exception = new InvalidOperationException(); server.ServerError += ( sender, e ) => { Assert.That( e.Exception, Is.EqualTo( exception ) ); occurred = true; }; using ( var target = new Target( server ) ) { target.RaiseServerError( exception ); Assert.That( occurred ); } } }
/// <summary> /// Performs protocol specific asynchronous 'Receive' operation. /// </summary> /// <param name="context">Context information.</param> protected sealed override void ReceiveCore(ServerRequestContext context) { bool isAsyncOperationStarted; try { isAsyncOperationStarted = this.BoundSocket.ReceiveAsync(context.SocketContext); } catch (ObjectDisposedException) { // Canceled. return; } if (!isAsyncOperationStarted) { context.SetCompletedSynchronously(); this.OnReceived(context); } }
/// <summary> /// Dispatch request/notification message via the <see cref="MsgPack.Rpc.Server.Dispatch.Dispatcher"/>. /// </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(ServerRequestContext context) { Contract.Assert(context != null); context.StopWatchTimeout(); context.Timeout -= this.OnReceiveTimeout; context.ClearBuffers(); var isNotification = context.MessageType == MessageType.Notification; try { this._dispatcher.Dispatch( this, context ); } finally { context.ClearDispatchContext(); if (isNotification) { this.OnSessionFinished(); } } context.NextProcess = this.UnpackRequestHeader; if (context.UnpackingBuffer.Length > 0) { // Subsequent request is already arrived. return(context.NextProcess(context)); } else { // Try receive subsequent. return(true); } }
public void TestRaiseServerError_RpcServerServerErrorEventOccurredWithSpeicifiedValues() { bool occurred = false; using (var server = new RpcServer()) using (var context = new ServerRequestContext()) { var exception = new InvalidOperationException(); server.ServerError += (sender, e) => { Assert.That(e.Exception, Is.EqualTo(exception)); occurred = true; }; using (var target = new Target(server)) { target.RaiseServerError(exception); Assert.That(occurred); } } }
/// <summary> /// Performs protocol specific asynchronous 'Receive' operation. /// </summary> /// <param name="context">Context information.</param> protected sealed override void ReceiveCore(ServerRequestContext context) { // Manager stores the socket which is dedicated socket to this transport in the AcceptSocket property. bool isAsyncOperationStarted; try { isAsyncOperationStarted = this.BoundSocket.ReceiveFromAsync(context.SocketContext); } catch (ObjectDisposedException) { // Canceled. return; } if (!isAsyncOperationStarted) { context.SetCompletedSynchronously(); this.OnReceived(context); } }
public void TestRaiseClientError_RpcServerClientErrorEventOccurredWithSpeicifiedValues() { bool occurred = false; using ( var server = new RpcServer() ) using ( var context = new ServerRequestContext() ) { var error = RpcError.ArgumentError; server.ClientError += ( sender, e ) => { Assert.That( e.MessageId, Is.EqualTo( context.MessageId ) ); Assert.That( e.RemoteEndPoint, Is.EqualTo( context.RemoteEndPoint ) ); Assert.That( e.RpcError.Error, Is.EqualTo( error ) ); Assert.That( e.SessionId, Is.EqualTo( context.SessionId ) ); occurred = true; }; using ( var target = new Target( server ) ) { target.RaiseClientError( context, new RpcErrorMessage( error, "Test" ) ); Assert.That( occurred ); } } }
public void TestRaiseClientError_RpcServerClientErrorEventOccurredWithSpeicifiedValues() { bool occurred = false; using (var server = new RpcServer()) using (var context = new ServerRequestContext()) { var error = RpcError.ArgumentError; server.ClientError += (sender, e) => { Assert.That(e.MessageId, Is.EqualTo(context.MessageId)); Assert.That(e.RemoteEndPoint, Is.EqualTo(context.RemoteEndPoint)); Assert.That(e.RpcError.Error, Is.EqualTo(error)); Assert.That(e.SessionId, Is.EqualTo(context.SessionId)); occurred = true; }; using (var target = new Target(server)) { target.RaiseClientError(context, new RpcErrorMessage(error, "Test")); Assert.That(occurred); } } }
/// <summary> /// Unpack array header of Arguments part on request/notification 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 UnpackArgumentsHeader( ServerRequestContext context ) { Contract.Assert( context != null ); if ( !context.ReadFromHeaderUnpacker() ) { MsgPackRpcServerProtocolsTrace.TraceEvent( MsgPackRpcServerProtocolsTrace.NeedArgumentsArrayHeader, "Arguments array header is needed. {{ \"SessionID\" : {0} }}", context.SessionId ); return false; } if ( !context.HeaderUnpacker.IsArrayHeader ) { this.HandleDeserializationError( context, "Invalid request/notify message stream. Arguments must be array.", () => context.UnpackingBuffer.ToArray() ); return true; } // TODO: Avoid actual unpacking to improve performance. context.ArgumentsBufferUnpacker = context.HeaderUnpacker.ReadSubtree(); if ( Int32.MaxValue < context.ArgumentsBufferUnpacker.ItemsCount ) { this.HandleDeserializationError( context, RpcError.MessageTooLargeError, "Too many arguments.", context.ArgumentsBufferUnpacker.ItemsCount.ToString( "#,0", CultureInfo.CurrentCulture ), () => context.UnpackingBuffer.ToArray() ); return true; } context.ArgumentsCount = unchecked( ( int )( context.ArgumentsBufferUnpacker.ItemsCount ) ); context.ArgumentsBufferPacker = Packer.Create( context.ArgumentsBuffer, false ); context.ArgumentsBufferPacker.PackArrayHeader( context.ArgumentsCount ); context.UnpackedArgumentsCount = 0; context.NextProcess = this.UnpackArguments; return context.NextProcess( context ); }
protected override void ReceiveCore( ServerRequestContext context ) { Contract.Requires( context != null ); }
/// <summary> /// Performs protocol specific asynchronous 'Receive' operation. /// </summary> /// <param name="context">Context information.</param> protected override void ReceiveCore( ServerRequestContext context ) { return; }
private static bool InvalidFlow(ServerRequestContext context) { throw new InvalidOperationException("Invalid state transition."); }
protected override void ReceiveCore( ServerRequestContext context ) { this.OnReceiving(); try { InProcPacket.ProcessReceive( this._inboundQueue, this._pendingPackets, context, this._receivingCancellationTokenSource.Token ); } catch ( OperationCanceledException ) { context.SocketError = SocketError.OperationAborted; } this.OnReceived( context ); this.OnReceived(); }
/// <summary> /// Unpack Method Name part on request/notification 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 UnpackMethodName( ServerRequestContext context ) { Contract.Assert( context != null ); if ( !context.ReadFromHeaderUnpacker() ) { MsgPackRpcServerProtocolsTrace.TraceEvent( MsgPackRpcServerProtocolsTrace.NeedMethodName, "Method Name is needed. {{ \"SessionID\" : {0} }}", context.SessionId ); return false; } try { context.MethodName = context.HeaderUnpacker.LastReadData.AsString(); } catch ( InvalidOperationException ) { this.HandleDeserializationError( context, "Invalid request/notify message stream. Method name must be UTF-8 string.", () => context.UnpackingBuffer.ToArray() ); return true; } context.NextProcess = this.UnpackArgumentsHeader; return context.NextProcess( context ); }
/// <summary> /// Returns the request context to the pool. /// </summary> /// <param name="context">The context to the pool.</param> /// <exception cref="ArgumentNullException"> /// <paramref name="context"/> is <c>null</c>. /// </exception> protected internal void ReturnRequestContext( ServerRequestContext context ) { if ( context == null ) { throw new ArgumentNullException( "context" ); } Contract.EndContractBlock(); context.Clear(); context.UnboundTransport(); this.RequestContextPool.Return( context ); }
/// <summary> /// Unpack array elements of Arguments part on request/notification 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 UnpackArguments( ServerRequestContext context ) { Contract.Assert( context != null ); while ( context.UnpackedArgumentsCount < context.ArgumentsCount ) { if ( !context.ReadFromArgumentsBufferUnpacker() ) { MsgPackRpcServerProtocolsTrace.TraceEvent( MsgPackRpcServerProtocolsTrace.NeedArgumentsElement, "Arguments array element is needed. {0}/{1} {{ \"SessionID\" : {2} }}", context.UnpackedArgumentsCount, context.ArgumentsCount, context.SessionId ); return false; } context.ArgumentsBufferPacker.Pack( context.ArgumentsBufferUnpacker.LastReadData ); context.UnpackedArgumentsCount++; } context.ArgumentsBuffer.Position = 0; context.ArgumentsUnpacker = Unpacker.Create( context.ArgumentsBuffer, false ); context.NextProcess = this.Dispatch; return context.NextProcess( context ); }
/// <summary> /// Performs protocol specific asynchronous 'Receive' operation. /// </summary> /// <param name="context">Context information.</param> protected sealed override void ReceiveCore( ServerRequestContext context ) { // Manager stores the socket which is dedicated socket to this transport in the AcceptSocket property. bool isAsyncOperationStarted; try { isAsyncOperationStarted = this.BoundSocket.ReceiveFromAsync( context.SocketContext ); } catch( ObjectDisposedException ) { // Canceled. return; } if ( !isAsyncOperationStarted ) { context.SetCompletedSynchronously(); this.OnReceived( context ); } }
/// <summary> /// Unpack Message ID part on request 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( ServerRequestContext context ) { Contract.Assert( context != null ); if ( !context.ReadFromHeaderUnpacker() ) { MsgPackRpcServerProtocolsTrace.TraceEvent( MsgPackRpcServerProtocolsTrace.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 request message stream. ID must be UInt32 compatible integer.", () => context.UnpackingBuffer.ToArray() ); return true; } context.NextProcess = this.UnpackMethodName; return context.NextProcess( context ); }
/// <summary> /// Performs protocol specific asynchronous 'Receive' operation. /// </summary> /// <param name="context">Context information.</param> protected override void ReceiveCore(ServerRequestContext context) { return; }
private static bool InvalidFlow( ServerRequestContext context ) { throw new InvalidOperationException( "Invalid state transition." ); }
/// <summary> /// Raises <see cref="E:RpcServer.ClientError"/> event on the hosting <see cref="RpcServer"/>. /// </summary> /// <param name="context">The <see cref="ServerRequestContext"/> which holds client information.</param> /// <param name="rpcError">The <see cref="RpcErrorMessage"/> representing the error.</param> internal void RaiseClientError( ServerRequestContext context, RpcErrorMessage rpcError ) { Contract.Requires( context != null ); Contract.Requires( !rpcError.IsSuccess ); this.Server.RaiseClientError( context, rpcError ); }
/// <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 override void OnReceived( ServerRequestContext context ) { Task.Factory.StartNew( state => base.OnReceived( state as ServerRequestContext ), context ); }
internal ServerResponseContext GetResponseContext( ServerRequestContext requestContext ) { Contract.Requires( requestContext != null ); Contract.Requires( requestContext.MessageId != null ); Contract.Requires( requestContext.BoundTransport != null ); Contract.Ensures( Contract.Result<ServerResponseContext>() != null ); return this.GetResponseContext( requestContext.BoundTransport, requestContext.RemoteEndPoint, requestContext.SessionId, requestContext.MessageId.Value ); }
/// <summary> /// Invokes target service operation asynchronously. /// </summary> /// <param name="requestContext"> /// The context object to hold request data. /// Note that properties of the context is only valid until this method returns. /// That is, it will be unpredectable state in the asynchronous operation. /// </param> /// <param name="responseContext"> /// The context object to pack response value or error. /// This is <c>null</c> for the notification messages. /// </param> /// <returns> /// <see cref="Task"/> to control entire process including sending response. /// </returns> public abstract Task InvokeAsync( ServerRequestContext requestContext, ServerResponseContext responseContext );
/// <summary> /// Performs protocol specific asynchronous 'Receive' operation. /// </summary> /// <param name="context">Context information.</param> protected sealed override void ReceiveCore( ServerRequestContext context ) { bool isAsyncOperationStarted; try { isAsyncOperationStarted = this.BoundSocket.ReceiveAsync( context.SocketContext ); } catch( ObjectDisposedException ) { // Canceled. return; } if ( !isAsyncOperationStarted ) { context.SetCompletedSynchronously(); this.OnReceived( context ); } }
/// <summary> /// Raises the <see cref="E:ClientError"/> event. /// </summary> /// <param name="context">The context information.</param> /// <param name="rpcError">The RPC error.</param> internal void RaiseClientError( ServerRequestContext context, RpcErrorMessage rpcError ) { this.OnClientError( new RpcClientErrorEventArgs( rpcError ) { RemoteEndPoint = context.RemoteEndPoint, SessionId = context.SessionId, MessageId = context.MessageId } ); }
/// <summary> /// Dispatch request/notification message via the <see cref="MsgPack.Rpc.Server.Dispatch.Dispatcher"/>. /// </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( ServerRequestContext context ) { Contract.Assert( context != null ); context.StopWatchTimeout(); context.Timeout -= this.OnReceiveTimeout; context.ClearBuffers(); var isNotification = context.MessageType == MessageType.Notification; try { this._dispatcher.Dispatch( this, context ); } finally { context.ClearDispatchContext(); if ( isNotification ) { this.OnSessionFinished(); } } context.NextProcess = this.UnpackRequestHeader; if ( context.UnpackingBuffer.Length > 0 ) { // Subsequent request is already arrived. return context.NextProcess( context ); } else { // Try receive subsequent. return true; } }
internal void StartReceive( ServerRequestContext context ) { if ( this._receivingTask == null ) { Contract.Assert( context.BoundTransport == this ); this._receivingTask = Task.Factory.StartNew( this.DoReceiveLoop, context, this._receivingCancellationTokenSource.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default ); } }
protected override void ReceiveCore(ServerRequestContext context) { Contract.Requires(context != null); }
/// <summary> /// Unpack Message Type part on request/notification 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( ServerRequestContext context ) { Contract.Assert( context != null ); if ( !context.ReadFromHeaderUnpacker() ) { MsgPackRpcServerProtocolsTrace.TraceEvent( MsgPackRpcServerProtocolsTrace.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 request/notify message stream. Message Type must be Int32 compatible integer.", () => context.UnpackingBuffer.ToArray() ); return true; } MessageType type = ( MessageType )numericType; context.MessageType = type; switch ( type ) { case MessageType.Request: { context.NextProcess = this.UnpackMessageId; break; } case MessageType.Notification: { context.NextProcess = this.UnpackMethodName; break; } default: { this.HandleDeserializationError( context, String.Format( CultureInfo.CurrentCulture, "Unknown message type '{0:x8}'", numericType ), () => context.UnpackingBuffer.ToArray() ); return true; } } return context.NextProcess( context ); }