public ValueTask <int> ReadBytesAsync(ArraySegment <byte> buffer, IOBehavior ioBehavior) { return(ioBehavior == IOBehavior.Asynchronous ? new ValueTask <int>(DoReadBytesAsync(buffer)) : DoReadBytesSync(buffer)); ValueTask <int> DoReadBytesSync(ArraySegment <byte> buffer_) { if (RemainingTimeout == Constants.InfiniteTimeout) { return(new ValueTask <int>(m_socket.Receive(buffer_.Array, buffer_.Offset, buffer_.Count, SocketFlags.None))); } while (RemainingTimeout > 0) { var startTime = Environment.TickCount; if (m_socket.Poll(Math.Min(int.MaxValue / 1000, RemainingTimeout) * 1000, SelectMode.SelectRead)) { var bytesRead = m_socket.Receive(buffer_.Array, buffer_.Offset, buffer_.Count, SocketFlags.None); RemainingTimeout -= unchecked (Environment.TickCount - startTime); return(new ValueTask <int>(bytesRead)); } RemainingTimeout -= unchecked (Environment.TickCount - startTime); } return(ValueTaskExtensions.FromException <int>(MySqlException.CreateForTimeout())); } async Task <int> DoReadBytesAsync(ArraySegment <byte> buffer_) { var startTime = RemainingTimeout == Constants.InfiniteTimeout ? 0 : Environment.TickCount; var timerId = RemainingTimeout == Constants.InfiniteTimeout ? 0 : TimerQueue.Instance.Add(RemainingTimeout, m_closeSocket); m_socketAwaitable.EventArgs.SetBuffer(buffer_.Array, buffer_.Offset, buffer_.Count); int bytesRead; try { await m_socket.ReceiveAsync(m_socketAwaitable); bytesRead = m_socketAwaitable.EventArgs.BytesTransferred; } catch (SocketException ex) { if (RemainingTimeout != Constants.InfiniteTimeout) { RemainingTimeout -= unchecked (Environment.TickCount - startTime); if (!TimerQueue.Instance.Remove(timerId)) { throw MySqlException.CreateForTimeout(ex); } } throw; } if (RemainingTimeout != Constants.InfiniteTimeout) { RemainingTimeout -= unchecked (Environment.TickCount - startTime); if (!TimerQueue.Instance.Remove(timerId)) { throw MySqlException.CreateForTimeout(); } } return(bytesRead); } }
private ValueTask <ArraySegment <byte> > ReadBytesAsync(int count, ProtocolErrorBehavior protocolErrorBehavior, IOBehavior ioBehavior) { // satisfy the read from cache if possible if (m_remainingData.Count > 0) { int bytesToRead = Math.Min(m_remainingData.Count, count); var result = new ArraySegment <byte>(m_remainingData.Array, m_remainingData.Offset, bytesToRead); m_remainingData = m_remainingData.Slice(bytesToRead); return(new ValueTask <ArraySegment <byte> >(result)); } // read the compressed header (seven bytes) return(m_bufferedByteReader.ReadBytesAsync(m_byteHandler, 7, ioBehavior) .ContinueWith(headerReadBytes => { if (headerReadBytes.Count < 7) { return protocolErrorBehavior == ProtocolErrorBehavior.Ignore ? default(ValueTask <ArraySegment <byte> >) : ValueTaskExtensions.FromException <ArraySegment <byte> >(new EndOfStreamException("Wanted to read 7 bytes but only read {0} when reading compressed packet header".FormatInvariant(headerReadBytes.Count))); } var payloadLength = (int)SerializationUtility.ReadUInt32(headerReadBytes.Array, headerReadBytes.Offset, 3); int packetSequenceNumber = headerReadBytes.Array[headerReadBytes.Offset + 3]; var uncompressedLength = (int)SerializationUtility.ReadUInt32(headerReadBytes.Array, headerReadBytes.Offset + 4, 3); // verify the compressed packet sequence number var expectedSequenceNumber = GetNextCompressedSequenceNumber() % 256; if (packetSequenceNumber != expectedSequenceNumber) { if (protocolErrorBehavior == ProtocolErrorBehavior.Ignore) { return default(ValueTask <ArraySegment <byte> >); } var exception = new InvalidOperationException("Packet received out-of-order. Expected {0}; got {1}.".FormatInvariant(expectedSequenceNumber, packetSequenceNumber)); return ValueTaskExtensions.FromException <ArraySegment <byte> >(exception); } // MySQL protocol resets the uncompressed sequence number back to the sequence number of this compressed packet. // This isn't in the documentation, but the code explicitly notes that uncompressed packets are modified by compression: // - https://github.com/mysql/mysql-server/blob/c28e258157f39f25e044bb72e8bae1ff00989a3d/sql/net_serv.cc#L276 // - https://github.com/mysql/mysql-server/blob/c28e258157f39f25e044bb72e8bae1ff00989a3d/sql/net_serv.cc#L225-L227 if (!m_isContinuationPacket) { m_uncompressedSequenceNumber = packetSequenceNumber; } // except this doesn't happen when uncompressed packets need to be broken up across multiple compressed packets m_isContinuationPacket = payloadLength == ProtocolUtility.MaxPacketSize || uncompressedLength == ProtocolUtility.MaxPacketSize; return m_bufferedByteReader.ReadBytesAsync(m_byteHandler, payloadLength, ioBehavior) .ContinueWith(payloadReadBytes => { if (payloadReadBytes.Count < payloadLength) { return protocolErrorBehavior == ProtocolErrorBehavior.Ignore ? default(ValueTask <ArraySegment <byte> >) : ValueTaskExtensions.FromException <ArraySegment <byte> >(new EndOfStreamException("Wanted to read {0} bytes but only read {1} when reading compressed payload".FormatInvariant(payloadLength, payloadReadBytes.Count))); } if (uncompressedLength == 0) { // data is uncompressed m_remainingData = payloadReadBytes; } else { // check CMF (Compression Method and Flags) and FLG (Flags) bytes for expected values var cmf = payloadReadBytes.Array[payloadReadBytes.Offset]; var flg = payloadReadBytes.Array[payloadReadBytes.Offset + 1]; if (cmf != 0x78 || ((flg & 0x40) == 0x40) || ((cmf * 256 + flg) % 31 != 0)) { // CMF = 0x78: 32K Window Size + deflate compression // FLG & 0x40: has preset dictionary (not supported) // CMF*256+FLG is a multiple of 31: header checksum return protocolErrorBehavior == ProtocolErrorBehavior.Ignore ? default(ValueTask <ArraySegment <byte> >) : ValueTaskExtensions.FromException <ArraySegment <byte> >(new NotSupportedException("Unsupported zlib header: {0:X2}{1:X2}".FormatInvariant(cmf, flg))); } // zlib format (https://www.ietf.org/rfc/rfc1950.txt) is: [two header bytes] [deflate-compressed data] [four-byte checksum] // .NET implements the middle part with DeflateStream; need to handle header and checksum explicitly const int headerSize = 2; const int checksumSize = 4; var uncompressedData = new byte[uncompressedLength]; using (var compressedStream = new MemoryStream(payloadReadBytes.Array, payloadReadBytes.Offset + headerSize, payloadReadBytes.Count - headerSize - checksumSize)) using (var decompressingStream = new DeflateStream(compressedStream, CompressionMode.Decompress)) { var bytesRead = decompressingStream.Read(uncompressedData, 0, uncompressedLength); m_remainingData = new ArraySegment <byte>(uncompressedData, 0, bytesRead); var checksum = ComputeAdler32Checksum(uncompressedData, 0, bytesRead); int adlerStartOffset = payloadReadBytes.Offset + payloadReadBytes.Count - 4; if (payloadReadBytes.Array[adlerStartOffset + 0] != ((checksum >> 24) & 0xFF) || payloadReadBytes.Array[adlerStartOffset + 1] != ((checksum >> 16) & 0xFF) || payloadReadBytes.Array[adlerStartOffset + 2] != ((checksum >> 8) & 0xFF) || payloadReadBytes.Array[adlerStartOffset + 3] != (checksum & 0xFF)) { return protocolErrorBehavior == ProtocolErrorBehavior.Ignore ? default(ValueTask <ArraySegment <byte> >) : ValueTaskExtensions.FromException <ArraySegment <byte> >(new NotSupportedException("Invalid Adler-32 checksum of uncompressed data.")); } } } var result = m_remainingData.Slice(0, count); m_remainingData = m_remainingData.Slice(count); return new ValueTask <ArraySegment <byte> >(result); }); })); }
public ValueTask <int> ReadBytesAsync(ArraySegment <byte> buffer, IOBehavior ioBehavior) { return(ioBehavior == IOBehavior.Asynchronous ? new ValueTask <int>(DoReadBytesAsync(buffer)) : RemainingTimeout <= 0 ? ValueTaskExtensions.FromException <int>(MySqlException.CreateForTimeout()) : m_stream.CanTimeout ? DoReadBytesSync(buffer) : DoReadBytesSyncOverAsync(buffer)); ValueTask <int> DoReadBytesSync(ArraySegment <byte> buffer_) { m_stream.ReadTimeout = RemainingTimeout == Constants.InfiniteTimeout ? Timeout.Infinite : RemainingTimeout; var startTime = RemainingTimeout == Constants.InfiniteTimeout ? 0 : Environment.TickCount; int bytesRead; try { bytesRead = m_stream.Read(buffer_.Array, buffer_.Offset, buffer_.Count); } catch (Exception ex) { if (RemainingTimeout != Constants.InfiniteTimeout && ex is IOException ioException && ioException.InnerException is SocketException socketException && socketException.SocketErrorCode == SocketError.TimedOut) { return(ValueTaskExtensions.FromException <int>(MySqlException.CreateForTimeout(ex))); } return(ValueTaskExtensions.FromException <int>(ex)); } if (RemainingTimeout != Constants.InfiniteTimeout) { RemainingTimeout -= unchecked (Environment.TickCount - startTime); } return(new ValueTask <int>(bytesRead)); } ValueTask <int> DoReadBytesSyncOverAsync(ArraySegment <byte> buffer_) { try { // handle timeout by setting a timer to close the stream in the background return(new ValueTask <int>(DoReadBytesAsync(buffer_).GetAwaiter().GetResult())); } catch (Exception ex) { return(ValueTaskExtensions.FromException <int>(ex)); } } async Task <int> DoReadBytesAsync(ArraySegment <byte> buffer_) { var startTime = RemainingTimeout == Constants.InfiniteTimeout ? 0 : Environment.TickCount; var timerId = RemainingTimeout == Constants.InfiniteTimeout ? 0 : TimerQueue.Instance.Add(RemainingTimeout, m_closeStream); int bytesRead; try { bytesRead = await m_stream.ReadAsync(buffer_.Array, buffer_.Offset, buffer_.Count).ConfigureAwait(false); } catch (Exception ex) when(ex is ObjectDisposedException || ex is IOException) { if (RemainingTimeout != Constants.InfiniteTimeout) { RemainingTimeout -= unchecked (Environment.TickCount - startTime); if (!TimerQueue.Instance.Remove(timerId)) { throw MySqlException.CreateForTimeout(ex); } } throw; } if (RemainingTimeout != Constants.InfiniteTimeout) { RemainingTimeout -= unchecked (Environment.TickCount - startTime); if (!TimerQueue.Instance.Remove(timerId)) { throw MySqlException.CreateForTimeout(); } } return(bytesRead); } }
public ValueTask <int> ReadBytesAsync(ArraySegment <byte> buffer, IOBehavior ioBehavior) { return((ioBehavior == IOBehavior.Asynchronous) ? new ValueTask <int>(DoReadBytesAsync(buffer)) : DoReadBytesSync(buffer)); ValueTask <int> DoReadBytesSync(ArraySegment <byte> buffer_) { if (RemainingTimeout <= 0) { return(ValueTaskExtensions.FromException <int>(MySqlException.CreateForTimeout())); } m_stream.ReadTimeout = RemainingTimeout == Constants.InfiniteTimeout ? Timeout.Infinite : RemainingTimeout; var startTime = RemainingTimeout == Constants.InfiniteTimeout ? 0 : Environment.TickCount; int bytesRead; try { bytesRead = m_stream.Read(buffer_.Array, buffer_.Offset, buffer_.Count); } catch (Exception ex) { if (ex is IOException && RemainingTimeout != Constants.InfiniteTimeout) { return(ValueTaskExtensions.FromException <int>(MySqlException.CreateForTimeout(ex))); } return(ValueTaskExtensions.FromException <int>(ex)); } if (RemainingTimeout != Constants.InfiniteTimeout) { RemainingTimeout -= unchecked (Environment.TickCount - startTime); } return(new ValueTask <int>(bytesRead)); } async Task <int> DoReadBytesAsync(ArraySegment <byte> buffer_) { var startTime = RemainingTimeout == Constants.InfiniteTimeout ? 0 : Environment.TickCount; var timerId = RemainingTimeout == Constants.InfiniteTimeout ? 0 : TimerQueue.Instance.Add(RemainingTimeout, m_closeStream); int bytesRead; try { bytesRead = await m_stream.ReadAsync(buffer_.Array, buffer_.Offset, buffer_.Count).ConfigureAwait(false); } catch (ObjectDisposedException ex) { if (RemainingTimeout != Constants.InfiniteTimeout) { RemainingTimeout -= unchecked (Environment.TickCount - startTime); if (!TimerQueue.Instance.Remove(timerId)) { throw MySqlException.CreateForTimeout(ex); } } throw; } if (RemainingTimeout != Constants.InfiniteTimeout) { RemainingTimeout -= unchecked (Environment.TickCount - startTime); if (!TimerQueue.Instance.Remove(timerId)) { throw MySqlException.CreateForTimeout(); } } return(bytesRead); } }
private static ValueTask <Packet> CreatePacketFromPayload(ArraySegment <byte> payloadBytes, int payloadLength, int packetSequenceNumber, ProtocolErrorBehavior protocolErrorBehavior) => payloadBytes.Count >= payloadLength ? new ValueTask <Packet>(new Packet(packetSequenceNumber, payloadBytes)) : protocolErrorBehavior == ProtocolErrorBehavior.Throw ? ValueTaskExtensions.FromException <Packet>(new EndOfStreamException()) : default(ValueTask <Packet>);
private static ValueTask <Packet> CreatePacketFromPayload(ArraySegment <byte> payloadBytes, int payloadLength, ProtocolErrorBehavior protocolErrorBehavior) => payloadBytes.Count >= payloadLength ? new ValueTask <Packet>(new Packet(payloadBytes)) : protocolErrorBehavior == ProtocolErrorBehavior.Throw ? ValueTaskExtensions.FromException <Packet>(new EndOfStreamException("Expected to read {0} payload bytes but only received {1}.".FormatInvariant(payloadLength, payloadBytes.Count))) : default(ValueTask <Packet>);