示例#1
0
        public void HandleReceiveComplete(SNIPacket packet, uint sniErrorCode)
        {
            using (TrySNIEventScope.Create(nameof(SNIMarsConnection)))
            {
                if (sniErrorCode != TdsEnums.SNI_SUCCESS)
                {
                    lock (DemuxerSync)
                    {
                        HandleReceiveError(packet);
                        SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIMarsConnection), EventType.ERR, "MARS Session Id {0}, Handled receive error code: {1}", args0: _lowerHandle?.ConnectionId, args1: sniErrorCode);
                        return;
                    }
                }

                State state     = State.Demux;
                State nextState = State.Demux;

                SNISMUXHeader handleHeader  = default;
                SNIMarsHandle handleSession = null;
                SNIPacket     handlePacket  = null;

                while (state != State.Error && state != State.Finish)
                {
                    switch (state)
                    {
                    case State.Demux:
                        lock (DemuxerSync)
                        {
                            switch (_demuxState)
                            {
                            case DemuxState.Header:
                                int taken = packet.TakeData(_headerBytes, _headerCount, SNISMUXHeader.HEADER_LENGTH - _headerCount);
                                _headerCount += taken;
                                SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIMarsConnection), EventType.INFO, "MARS Session Id {0}, took {1} header bytes", args0: _lowerHandle?.ConnectionId, args1: packet.DataLeft, args2: taken);
                                if (_headerCount == SNISMUXHeader.HEADER_LENGTH)
                                {
                                    _header.Read(_headerBytes);
                                    _payloadLength = (int)_header.Length;
                                    _payloadCount  = 0;
                                    _demuxState    = DemuxState.Payload;
                                    state          = State.Demux;
                                    SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIMarsConnection), EventType.INFO, "MARS Session Id {0}, header complete, _payloadLength {1}", args0: _lowerHandle?.ConnectionId, args1: _payloadLength);
                                    goto case DemuxState.Payload;
                                }
                                else
                                {
                                    state = State.Receive;
                                }
                                break;

                            case DemuxState.Payload:
                                if (packet.DataLeft == _payloadLength && _partial == null)
                                {
                                    // if the data in the packet being processed is exactly and only the data that is going to sent
                                    // on to the parser then don't copy it to a new packet just forward the current packet once we've
                                    // fiddled the data pointer so that it skips the header data
                                    _partial = packet;
                                    packet   = null;
                                    _partial.SetDataToRemainingContents();
                                    _demuxState = DemuxState.Dispatch;
                                    state       = State.Demux;
                                    SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIMarsConnection), EventType.INFO, "MARS Session Id {0}, forwarding packet contents", args0: _lowerHandle?.ConnectionId, args1: _header.SessionId);
                                    goto case DemuxState.Dispatch;
                                }
                                else
                                {
                                    if (_partial == null)
                                    {
                                        _partial = RentPacket(headerSize: 0, dataSize: _payloadLength);
                                    }
                                    SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIMarsConnection), EventType.INFO, "MARS Session Id {0}, reconstructing packet contents", args0: _lowerHandle?.ConnectionId, args1: _header.SessionId);
                                    int wanted      = _payloadLength - _payloadCount;
                                    int transferred = SNIPacket.TransferData(packet, _partial, wanted);
                                    _payloadCount += transferred;
                                    SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIMarsConnection), EventType.INFO, "MARS Session Id {0}, took {1} payload bytes", args0: _lowerHandle?.ConnectionId, args1: transferred);

                                    if (_payloadCount == _payloadLength)
                                    {
                                        // payload is complete so dispatch the current packet
                                        _demuxState = DemuxState.Dispatch;
                                        state       = State.Receive;
                                        goto case DemuxState.Dispatch;
                                    }
                                    else if (packet.DataLeft == 0)
                                    {
                                        // no more data in the delivered packet so wait for a new one
                                        _demuxState = DemuxState.Payload;
                                        state       = State.Receive;
                                    }
                                    else
                                    {
                                        // data left in the delivered packet so start the demux loop
                                        // again and decode the next packet in the input
                                        _headerCount = 0;
                                        _demuxState  = DemuxState.Header;
                                        state        = State.Demux;
                                    }
                                }

                                break;

                            case DemuxState.Dispatch:
                                if (_sessions.TryGetValue(_header.SessionId, out SNIMarsHandle session))
                                {
                                    SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIMarsConnection), EventType.INFO, "MARS Session Id {0}, Current Session assigned to Session Id {1}", args0: _lowerHandle?.ConnectionId, args1: _header.SessionId);
                                    switch ((SNISMUXFlags)_header.Flags)
                                    {
                                    case SNISMUXFlags.SMUX_DATA:
                                        handleSession = session;
                                        session       = null;
                                        handleHeader  = _header.Clone();
                                        handlePacket  = _partial;
                                        _partial      = null;
                                        // move to the state for sending the data to the mars handle and setup
                                        // the state that should be moved to after that operation has succeeded
                                        state = State.HandleData;
                                        if (packet != null && packet.DataLeft > 0)
                                        {
                                            nextState = State.Demux;
                                        }
                                        else
                                        {
                                            nextState = State.Receive;
                                        }
                                        break;

                                    case SNISMUXFlags.SMUX_ACK:
                                        handleSession = session;
                                        session       = null;
                                        handleHeader  = _header.Clone();
                                        ReturnPacket(_partial);
                                        _partial = null;
                                        // move to the state for sending the data to the mars handle and setup
                                        // the state that should be moved to after that operation has succeeded
                                        state = State.HandleAck;
                                        if (packet != null && packet.DataLeft > 0)
                                        {
                                            nextState = State.Demux;
                                        }
                                        else
                                        {
                                            nextState = State.Receive;
                                        }
                                        break;

                                    case SNISMUXFlags.SMUX_FIN:
                                        ReturnPacket(_partial);
                                        _partial = null;
                                        _sessions.Remove(_header.SessionId);
                                        SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIMarsConnection), EventType.INFO, "SMUX_FIN | MARS Session Id {0}, SMUX_FIN flag received, Current Header Session Id {1} removed", args0: _lowerHandle?.ConnectionId, args1: _header.SessionId);
                                        break;

                                    default:
                                        Debug.Fail("unknown smux packet flag");
                                        break;
                                    }

                                    // a full packet has been decoded and queued for sending by setting the state or the
                                    // handle it was sent to no longer exists and the handle has been dropped. Now reset the
                                    // demuxer state ready to recode another packet
                                    _header.Clear();
                                    _headerCount = 0;
                                    _demuxState  = DemuxState.Header;

                                    // if the state is set to demux more data and there is no data left then change
                                    // the state to request more data
                                    if (state == State.Demux && (packet == null || packet.DataLeft == 0))
                                    {
                                        if (packet != null)
                                        {
                                            SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIMarsConnection), EventType.INFO, "MARS Session Id {0}, run out of data , queuing receive", args0: _lowerHandle?.ConnectionId, args1: _header.SessionId);
                                        }
                                        state = State.Receive;
                                    }
                                }
                                else
                                {
                                    SNILoadHandle.SingletonInstance.LastError = new SNIError(SNIProviders.SMUX_PROV, 0, SNICommon.InvalidParameterError, string.Empty);
                                    HandleReceiveError(packet);
                                    SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIMarsConnection), EventType.ERR, "Current Header Session Id {0} not found, MARS Session Id {1} will be destroyed, New SNI error created: {2}", args0: _header.SessionId, args1: _lowerHandle?.ConnectionId, args2: sniErrorCode);
                                    packet = null;
                                    _lowerHandle.Dispose();
                                    _lowerHandle = null;
                                    state        = State.Error;
                                }
                                break;
                            }
                        }
                        break;

                    case State.HandleAck:
                        Debug.Assert(handleSession != null, "dispatching ack to null SNIMarsHandle");
                        Debug.Assert(!Monitor.IsEntered(DemuxerSync), "do not dispatch ack to session handle while holding the demuxer lock");
                        try
                        {
                            handleSession.HandleAck(handleHeader.Highwater);
                            SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIMarsConnection), EventType.INFO, "SMUX_ACK | MARS Session Id {0}, Current Session {1} handled ack", args0: _lowerHandle?.ConnectionId, args1: _header.SessionId);
                        }
                        catch (Exception e)
                        {
                            SNICommon.ReportSNIError(SNIProviders.SMUX_PROV, SNICommon.InternalExceptionError, e);
                        }
                        finally
                        {
                            handleHeader  = default;
                            handleSession = null;
                        }
                        state     = nextState;
                        nextState = State.Finish;
                        break;

                    case State.HandleData:
                        Debug.Assert(handleSession != null, "dispatching data to null SNIMarsHandle");
                        Debug.Assert(handlePacket != null, "dispatching null data to SNIMarsHandle");
                        Debug.Assert(!Monitor.IsEntered(DemuxerSync), "do not dispatch data to session handle while holding the demuxer lock");
                        // do not ReturnPacket(handlePacket) the receiver is responsible for returning the packet
                        // once it has been used because it can take sync and async paths from to the receiver and
                        // only the reciever can make the decision on when it is completed and can be returned
                        try
                        {
                            handleSession.HandleReceiveComplete(handlePacket, handleHeader);
                            SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIMarsConnection), EventType.INFO, "SMUX_DATA | MARS Session Id {0}, Current Session {1} completed receiving Data", args0: _lowerHandle?.ConnectionId, args1: _header.SessionId);
                        }
                        finally
                        {
                            handleHeader  = default;
                            handleSession = null;
                            handlePacket  = null;
                        }
                        state     = nextState;
                        nextState = State.Finish;
                        break;

                    case State.Receive:
                        if (packet != null)
                        {
                            Debug.Assert(packet.DataLeft == 0, "loop exit with data remaining");
                            ReturnPacket(packet);
                            packet = null;
                        }

                        lock (DemuxerSync)
                        {
                            uint receiveResult = ReceiveAsync(ref packet);
                            if (receiveResult == TdsEnums.SNI_SUCCESS_IO_PENDING)
                            {
                                SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIMarsConnection), EventType.INFO, "MARS Session Id {0}, SMUX DATA Header SNI Packet received with code {1}", args0: ConnectionId, args1: receiveResult);
                                packet = null;
                            }
                            else
                            {
                                HandleReceiveError(packet);
                                SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIMarsConnection), EventType.ERR, "MARS Session Id {0}, Handled receive error code: {1}", args0: _lowerHandle?.ConnectionId, args1: receiveResult);
                            }
                        }
                        state = State.Finish;
                        break;
                    }
                }
            }
        }