private void ReadThread()
        {
            try
            {
                Thread.CurrentThread.Name = "HTTP2 Read";
                HTTPManager.Logger.Information("HTTP2Handler", "Reader thread up and running!");

                using (ReadOnlyBufferedStream bufferedStream = new ReadOnlyBufferedStream(this.conn.connector.Stream, 32 * 1024))
                {
                    while (this.isRunning)
                    {
                        // TODO:
                        //  1. Set the local window to a reasonable size
                        //  2. stop reading when the local window is about to be 0.
                        //  3.
                        HTTP2FrameHeaderAndPayload header = HTTP2FrameHelper.ReadHeader(bufferedStream);

                        if (HTTPManager.Logger.Level <= Logger.Loglevels.Information && header.Type != HTTP2FrameTypes.DATA /*&& header.Type != HTTP2FrameTypes.PING*/)
                        {
                            HTTPManager.Logger.Information("HTTP2Handler", "New frame received: " + header.ToString());
                        }

                        // Add the new frame to the queue. Processing it on the write thread gives us the advantage that
                        //  we don't have to deal with too much locking.
                        this.newFrames.Enqueue(header);

                        // ping write thread to process the new frame
                        this.newFrameSignal.Set();

                        switch (header.Type)
                        {
                        // Handle pongs on the read thread, so no additional latency is added to the rtt calculation.
                        case HTTP2FrameTypes.PING:
                            var pingFrame = HTTP2FrameHelper.ReadPingFrame(header);

                            if ((pingFrame.Flags & HTTP2PingFlags.ACK) != 0)
                            {
                                // it was an ack, payload must contain what we sent

                                var ticks = BufferHelper.ReadLong(pingFrame.OpaqueData, 0);

                                // the difference between the current time and the time when the ping message is sent
                                TimeSpan diff = TimeSpan.FromTicks(DateTime.UtcNow.Ticks - ticks);

                                // add it to the buffer
                                this.rtts.Add(diff.TotalMilliseconds);

                                // and calculate the new latency
                                this.Latency = CalculateLatency();

                                HTTPManager.Logger.Verbose("HTTP2Handler", string.Format("Latency: {0:F2}ms, RTT buffer: {1}", this.Latency, this.rtts.ToString()));
                            }
                            break;

                        case HTTP2FrameTypes.GOAWAY:
                            // Just exit from this thread. The processing thread will handle the frame too.
                            return;
                        }
                    }
                }
            }
            catch //(Exception ex)
            {
                //HTTPManager.Logger.Exception("HTTP2Handler", "", ex);

                this.isRunning = false;
                this.newFrameSignal.Set();
            }
            finally
            {
                // First thread closing notifies the ConnectionEventHelper
                if (Interlocked.Increment(ref this.threadExitCount) == 1)
                {
                    ConnectionEventHelper.EnqueueConnectionEvent(new ConnectionEventInfo(this.conn, HTTPConnectionStates.Closed));
                }

                HTTPManager.Logger.Information("HTTP2Handler", "Reader thread closing");
            }
        }