Beispiel #1
0
        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);
                });
            }));
        }
Beispiel #3
0
        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);
            }
        }
Beispiel #4
0
        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);
            }
        }
Beispiel #5
0
 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>);
Beispiel #6
0
 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>);