/// <summary> /// Process a receive completion /// </summary> /// <param name="packet">SNI packet</param> /// <param name="sniErrorCode">SNI error code</param> public void HandleReceiveComplete(SNIPacket packet, uint sniErrorCode) { long scopeID = SqlClientEventSource.Log.TrySNIScopeEnterEvent("<sc.SNI.SNIMarsConnection.HandleReceiveComplete |SNI|INFO|SCOPE>"); try { SNISMUXHeader currentHeader = null; SNIPacket currentPacket = null; SNIMarsHandle currentSession = null; if (sniErrorCode != TdsEnums.SNI_SUCCESS) { lock (this) { HandleReceiveError(packet); SqlClientEventSource.Log.TrySNITraceEvent("<sc.SNI.SNIMarsConnection.HandleReceiveComplete |SNI|ERR> not successful."); return; } } while (true) { lock (this) { if (_currentHeaderByteCount != SNISMUXHeader.HEADER_LENGTH) { currentHeader = null; currentPacket = null; currentSession = null; while (_currentHeaderByteCount != SNISMUXHeader.HEADER_LENGTH) { int bytesTaken = packet.TakeData(_headerBytes, _currentHeaderByteCount, SNISMUXHeader.HEADER_LENGTH - _currentHeaderByteCount); _currentHeaderByteCount += bytesTaken; if (bytesTaken == 0) { sniErrorCode = ReceiveAsync(ref packet); if (sniErrorCode == TdsEnums.SNI_SUCCESS_IO_PENDING) { SqlClientEventSource.Log.TrySNITraceEvent("<sc.SNI.SNIMarsConnection.HandleReceiveComplete |SNI|ERR> not successful."); return; } HandleReceiveError(packet); return; } } _currentHeader.Read(_headerBytes); _dataBytesLeft = (int)_currentHeader.length; _currentPacket = _lowerHandle.RentPacket(headerSize: 0, dataSize: (int)_currentHeader.length); } currentHeader = _currentHeader; currentPacket = _currentPacket; if (_currentHeader.flags == (byte)SNISMUXFlags.SMUX_DATA) { if (_dataBytesLeft > 0) { int length = packet.TakeData(_currentPacket, _dataBytesLeft); _dataBytesLeft -= length; if (_dataBytesLeft > 0) { sniErrorCode = ReceiveAsync(ref packet); if (sniErrorCode == TdsEnums.SNI_SUCCESS_IO_PENDING) { return; } HandleReceiveError(packet); return; } } } _currentHeaderByteCount = 0; if (!_sessions.ContainsKey(_currentHeader.sessionId)) { SNILoadHandle.SingletonInstance.LastError = new SNIError(SNIProviders.SMUX_PROV, 0, SNICommon.InvalidParameterError, Strings.SNI_ERROR_5); HandleReceiveError(packet); _lowerHandle.Dispose(); _lowerHandle = null; return; } if (_currentHeader.flags == (byte)SNISMUXFlags.SMUX_FIN) { _sessions.Remove(_currentHeader.sessionId); } else { currentSession = _sessions[_currentHeader.sessionId]; } } if (currentHeader.flags == (byte)SNISMUXFlags.SMUX_DATA) { currentSession.HandleReceiveComplete(currentPacket, currentHeader); } if (_currentHeader.flags == (byte)SNISMUXFlags.SMUX_ACK) { try { currentSession.HandleAck(currentHeader.highwater); } catch (Exception e) { SNICommon.ReportSNIError(SNIProviders.SMUX_PROV, SNICommon.InternalExceptionError, e); } Debug.Assert(_currentPacket == currentPacket, "current and _current are not the same"); ReturnPacket(currentPacket); currentPacket = null; _currentPacket = null; } lock (this) { if (packet.DataLeft == 0) { sniErrorCode = ReceiveAsync(ref packet); if (sniErrorCode == TdsEnums.SNI_SUCCESS_IO_PENDING) { return; } HandleReceiveError(packet); return; } } } } finally { SqlClientEventSource.Log.TrySNIScopeLeaveEvent(scopeID); } }
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; } } } }
/// <summary> /// Process a receive completion /// </summary> /// <param name="packet">SNI packet</param> /// <param name="sniErrorCode">SNI error code</param> public void HandleReceiveComplete(SNIPacket packet, uint sniErrorCode) { SNISMUXHeader currentHeader = null; SNIPacket currentPacket = null; SNIMarsHandle currentSession = null; if (sniErrorCode != TdsEnums.SNI_SUCCESS) { lock (this) { HandleReceiveError(packet); return; } } while (true) { lock (this) { if (_currentHeaderByteCount != SNISMUXHeader.HEADER_LENGTH) { currentHeader = null; currentPacket = null; currentSession = null; while (_currentHeaderByteCount != SNISMUXHeader.HEADER_LENGTH) { int bytesTaken = packet.TakeData(_headerBytes, _currentHeaderByteCount, SNISMUXHeader.HEADER_LENGTH - _currentHeaderByteCount); _currentHeaderByteCount += bytesTaken; if (bytesTaken == 0) { packet.Dispose(); packet = null; sniErrorCode = ReceiveAsync(ref packet); if (sniErrorCode == TdsEnums.SNI_SUCCESS_IO_PENDING) { return; } HandleReceiveError(packet); return; } } _currentHeader.Read(_headerBytes); _dataBytesLeft = (int)_currentHeader.length; _currentPacket = new SNIPacket((int)_currentHeader.length); } currentHeader = _currentHeader; currentPacket = _currentPacket; if (_currentHeader.flags == (byte)SNISMUXFlags.SMUX_DATA) { if (_dataBytesLeft > 0) { int length = packet.TakeData(_currentPacket, _dataBytesLeft); _dataBytesLeft -= length; if (_dataBytesLeft > 0) { packet.Dispose(); packet = null; sniErrorCode = ReceiveAsync(ref packet); if (sniErrorCode == TdsEnums.SNI_SUCCESS_IO_PENDING) { return; } HandleReceiveError(packet); return; } } } _currentHeaderByteCount = 0; if (!_sessions.ContainsKey(_currentHeader.sessionId)) { SNILoadHandle.SingletonInstance.LastError = new SNIError(SNIProviders.SMUX_PROV, 0, SNICommon.InvalidParameterError, string.Empty); HandleReceiveError(packet); _lowerHandle.Dispose(); _lowerHandle = null; return; } if (_currentHeader.flags == (byte)SNISMUXFlags.SMUX_FIN) { _sessions.Remove(_currentHeader.sessionId); } else { currentSession = _sessions[_currentHeader.sessionId]; } } if (currentHeader.flags == (byte)SNISMUXFlags.SMUX_DATA) { currentSession.HandleReceiveComplete(currentPacket, currentHeader); } if (_currentHeader.flags == (byte)SNISMUXFlags.SMUX_ACK) { try { currentSession.HandleAck(currentHeader.highwater); } catch (Exception e) { SNICommon.ReportSNIError(SNIProviders.SMUX_PROV, SNICommon.InternalExceptionError, e); } } lock (this) { if (packet.DataLeft == 0) { packet.Dispose(); packet = null; sniErrorCode = ReceiveAsync(ref packet); if (sniErrorCode == TdsEnums.SNI_SUCCESS_IO_PENDING) { return; } HandleReceiveError(packet); return; } } } }
/// <summary> /// Process a receive completion /// </summary> /// <param name="packet">SNI packet</param> /// <param name="sniErrorCode">SNI error code</param> public void HandleReceiveComplete(SNIPacket packet, uint sniErrorCode) { long scopeID = SqlClientEventSource.Log.TrySNIScopeEnterEvent(s_className); try { SNISMUXHeader currentHeader = null; SNIPacket currentPacket = null; SNIMarsHandle currentSession = null; if (sniErrorCode != TdsEnums.SNI_SUCCESS) { lock (this) { HandleReceiveError(packet); SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.ERR, "MARS Session Id {0}, Handled receive error code: {1}", args0: _lowerHandle?.ConnectionId, args1: sniErrorCode); return; } } while (true) { lock (this) { if (_currentHeaderByteCount != SNISMUXHeader.HEADER_LENGTH) { currentHeader = null; currentPacket = null; currentSession = null; while (_currentHeaderByteCount != SNISMUXHeader.HEADER_LENGTH) { int bytesTaken = packet.TakeData(_headerBytes, _currentHeaderByteCount, SNISMUXHeader.HEADER_LENGTH - _currentHeaderByteCount); _currentHeaderByteCount += bytesTaken; if (bytesTaken == 0) { sniErrorCode = ReceiveAsync(ref packet); SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.INFO, "MARS Session Id {0}, Non-SMUX Header SNI Packet received with code {1}", args0: ConnectionId, args1: sniErrorCode); if (sniErrorCode == TdsEnums.SNI_SUCCESS_IO_PENDING) { return; } HandleReceiveError(packet); SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.ERR, "MARS Session Id {0}, Handled receive error code: {1}", args0: _lowerHandle?.ConnectionId, args1: sniErrorCode); return; } } _currentHeader.Read(_headerBytes); _dataBytesLeft = (int)_currentHeader.length; _currentPacket = _lowerHandle.RentPacket(headerSize: 0, dataSize: (int)_currentHeader.length); #if DEBUG SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.INFO, "MARS Session Id {0}, _dataBytesLeft {1}, _currentPacket {2}, Reading data of length: _currentHeader.length {3}", args0: _lowerHandle?.ConnectionId, args1: _dataBytesLeft, args2: currentPacket?._id, args3: _currentHeader?.length); #endif } currentHeader = _currentHeader; currentPacket = _currentPacket; if (_currentHeader.flags == (byte)SNISMUXFlags.SMUX_DATA) { if (_dataBytesLeft > 0) { int length = packet.TakeData(_currentPacket, _dataBytesLeft); _dataBytesLeft -= length; if (_dataBytesLeft > 0) { sniErrorCode = ReceiveAsync(ref packet); SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.INFO, "MARS Session Id {0}, SMUX DATA Header SNI Packet received with code {1}, _dataBytesLeft {2}", args0: ConnectionId, args1: sniErrorCode, args2: _dataBytesLeft); if (sniErrorCode == TdsEnums.SNI_SUCCESS_IO_PENDING) { return; } HandleReceiveError(packet); SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.ERR, "MARS Session Id {0}, Handled receive error code: {1}", args0: _lowerHandle?.ConnectionId, args1: sniErrorCode); return; } } } _currentHeaderByteCount = 0; if (!_sessions.ContainsKey(_currentHeader.sessionId)) { SNILoadHandle.SingletonInstance.LastError = new SNIError(SNIProviders.SMUX_PROV, 0, SNICommon.InvalidParameterError, Strings.SNI_ERROR_5); HandleReceiveError(packet); SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.ERR, "Current Header Session Id {0} not found, MARS Session Id {1} will be destroyed, New SNI error created: {2}", args0: _currentHeader?.sessionId, args1: _lowerHandle?.ConnectionId, args2: sniErrorCode); _lowerHandle.Dispose(); _lowerHandle = null; return; } if (_currentHeader.flags == (byte)SNISMUXFlags.SMUX_FIN) { _sessions.Remove(_currentHeader.sessionId); SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.INFO, "SMUX_FIN | MARS Session Id {0}, SMUX_FIN flag received, Current Header Session Id {1} removed", args0: _lowerHandle?.ConnectionId, args1: _currentHeader?.sessionId); } else { currentSession = _sessions[_currentHeader.sessionId]; SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.INFO, "MARS Session Id {0}, Current Session assigned to Session Id {1}", args0: _lowerHandle?.ConnectionId, args1: _currentHeader?.sessionId); } } if (currentHeader.flags == (byte)SNISMUXFlags.SMUX_DATA) { currentSession.HandleReceiveComplete(currentPacket, currentHeader); SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.INFO, "SMUX_DATA | MARS Session Id {0}, Current Session {1} completed receiving Data", args0: _lowerHandle?.ConnectionId, args1: _currentHeader?.sessionId); } if (_currentHeader.flags == (byte)SNISMUXFlags.SMUX_ACK) { try { currentSession.HandleAck(currentHeader.highwater); SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.INFO, "SMUX_ACK | MARS Session Id {0}, Current Session {1} handled ack", args0: _lowerHandle?.ConnectionId, args1: _currentHeader?.sessionId); } catch (Exception e) { SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.ERR, "SMUX_ACK | MARS Session Id {0}, Exception occurred: {2}", args0: _currentHeader?.sessionId, args1: e?.Message); SNICommon.ReportSNIError(SNIProviders.SMUX_PROV, SNICommon.InternalExceptionError, e); } #if DEBUG Debug.Assert(_currentPacket == currentPacket, "current and _current are not the same"); SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.INFO, "SMUX_ACK | MARS Session Id {0}, Current Packet {1} returned", args0: _lowerHandle?.ConnectionId, args1: currentPacket?._id); #endif ReturnPacket(currentPacket); currentPacket = null; _currentPacket = null; } lock (this) { if (packet.DataLeft == 0) { sniErrorCode = ReceiveAsync(ref packet); if (sniErrorCode == TdsEnums.SNI_SUCCESS_IO_PENDING) { return; } HandleReceiveError(packet); SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.ERR, "MARS Session Id {0}, packet.DataLeft 0, SNI error {2}", args0: _lowerHandle?.ConnectionId, args1: sniErrorCode); return; } } } } finally { SqlClientEventSource.Log.TrySNIScopeLeaveEvent(scopeID); } }