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"); } }