コード例 #1
0
        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());
        }
コード例 #2
0
        /// <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);
            }
        }
コード例 #3
0
        /// <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));
        }
コード例 #4
0
 void FinishReceiving(ClientResponseContext context)
 {
     context.StopWatchTimeout();
     context.Timeout -= OnReceiveTimeout;
     Manager.ReturnResponseContext(context);
     //this.Manager.ReturnTransport( this );
 }
コード例 #5
0
 /// <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);
     }
 }
コード例 #6
0
        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.
        }
コード例 #7
0
        /// <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);
        }
コード例 #8
0
        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);
        }
コード例 #9
0
        /// <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);
        }
コード例 #10
0
        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);
        }
コード例 #11
0
        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;
        }
コード例 #12
0
        /// <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));
        }
コード例 #13
0
        /// <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));
        }
コード例 #14
0
        /// <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);
            }
        }
コード例 #15
0
        /// <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));
        }
コード例 #16
0
 static bool InvalidFlow(ClientResponseContext context)
 {
     throw new InvalidOperationException("Invalid state transition.");
 }
コード例 #17
0
 void HandleDeserializationError(ClientResponseContext context, string message, Func <byte[]> invalidRequestHeaderProvider)
 {
     HandleDeserializationError(context, context.MessageId, new RpcErrorMessage(RpcError.RemoteRuntimeError, "Invalid stream.", message), message, invalidRequestHeaderProvider);
 }
コード例 #18
0
        /// <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);
        }
コード例 #19
0
 /// <summary>
 ///		Performs protocol specific asynchronous 'Receive' operation.
 /// </summary>
 /// <param name="context">Context information.</param>
 protected abstract void ReceiveCore(ClientResponseContext context);