gfe-relnote: Add and call Http3DebugVisitor methods.  Not protected.

This is so that Chromium can record NetLog events, see
https://crbug.com/1062700.

PiperOrigin-RevId: 302134482
Change-Id: I9c330ad2aa62115e3c24f068f011ada272148ad3
diff --git a/quic/core/http/quic_receive_control_stream.cc b/quic/core/http/quic_receive_control_stream.cc
index 7f8007a..f9fdef9 100644
--- a/quic/core/http/quic_receive_control_stream.cc
+++ b/quic/core/http/quic_receive_control_stream.cc
@@ -29,25 +29,41 @@
     stream_->OnUnrecoverableError(decoder->error(), decoder->error_detail());
   }
 
-  bool OnCancelPushFrame(const CancelPushFrame& /*frame*/) override {
+  bool OnCancelPushFrame(const CancelPushFrame& frame) override {
+    if (stream_->spdy_session()->debug_visitor()) {
+      stream_->spdy_session()->debug_visitor()->OnCancelPushFrameReceived(
+          frame);
+    }
+
     // TODO(b/151841240): Handle CANCEL_PUSH frames instead of ignoring them.
     return true;
   }
 
   bool OnMaxPushIdFrame(const MaxPushIdFrame& frame) override {
-    if (stream_->spdy_session()->perspective() == Perspective::IS_SERVER) {
-      stream_->spdy_session()->SetMaxAllowedPushId(frame.push_id);
-      return true;
+    if (stream_->spdy_session()->perspective() == Perspective::IS_CLIENT) {
+      OnWrongFrame("Max Push Id");
+      return false;
     }
-    OnWrongFrame("Max Push Id");
-    return false;
+
+    if (stream_->spdy_session()->debug_visitor()) {
+      stream_->spdy_session()->debug_visitor()->OnMaxPushIdFrameReceived(frame);
+    }
+
+    stream_->spdy_session()->SetMaxAllowedPushId(frame.push_id);
+    return true;
   }
 
   bool OnGoAwayFrame(const GoAwayFrame& frame) override {
+    // TODO(bnc): Check if SETTINGS frame has been received.
     if (stream_->spdy_session()->perspective() == Perspective::IS_SERVER) {
       OnWrongFrame("Go Away");
       return false;
     }
+
+    if (stream_->spdy_session()->debug_visitor()) {
+      stream_->spdy_session()->debug_visitor()->OnGoAwayFrameReceived(frame);
+    }
+
     stream_->spdy_session()->OnHttp3GoAway(frame.stream_id);
     return true;
   }
@@ -120,17 +136,32 @@
     return stream_->OnPriorityUpdateFrame(frame);
   }
 
-  bool OnUnknownFrameStart(uint64_t /* frame_type */,
+  bool OnUnknownFrameStart(uint64_t frame_type,
                            QuicByteCount /* header_length */) override {
+    if (stream_->spdy_session()->debug_visitor()) {
+      stream_->spdy_session()->debug_visitor()->OnUnknownFrameStart(
+          stream_->id(), frame_type);
+    }
+
     return stream_->OnUnknownFrameStart();
   }
 
-  bool OnUnknownFramePayload(quiche::QuicheStringPiece /* payload */) override {
+  bool OnUnknownFramePayload(quiche::QuicheStringPiece payload) override {
+    if (stream_->spdy_session()->debug_visitor()) {
+      stream_->spdy_session()->debug_visitor()->OnUnknownFramePayload(
+          stream_->id(), payload.length());
+    }
+
     // Ignore unknown frame types.
     return true;
   }
 
   bool OnUnknownFrameEnd() override {
+    if (stream_->spdy_session()->debug_visitor()) {
+      stream_->spdy_session()->debug_visitor()->OnUnknownFrameEnd(
+          stream_->id());
+    }
+
     // Ignore unknown frame types.
     return true;
   }
@@ -224,6 +255,10 @@
 
 bool QuicReceiveControlStream::OnPriorityUpdateFrame(
     const PriorityUpdateFrame& priority) {
+  if (spdy_session()->debug_visitor()) {
+    spdy_session()->debug_visitor()->OnPriorityUpdateFrameReceived(priority);
+  }
+
   // TODO(b/147306124): Use a proper structured headers parser instead.
   for (auto key_value :
        quiche::QuicheTextUtils::Split(priority.priority_field_value, ',')) {
diff --git a/quic/core/http/quic_receive_control_stream_test.cc b/quic/core/http/quic_receive_control_stream_test.cc
index 61d8229..77692a5 100644
--- a/quic/core/http/quic_receive_control_stream_test.cc
+++ b/quic/core/http/quic_receive_control_stream_test.cc
@@ -267,6 +267,9 @@
 }
 
 TEST_P(QuicReceiveControlStreamTest, ReceiveGoAwayFrame) {
+  StrictMock<MockHttp3DebugVisitor> debug_visitor;
+  session_.set_debug_visitor(&debug_visitor);
+
   GoAwayFrame goaway;
   goaway.stream_id = 0x00;
 
@@ -282,6 +285,8 @@
     EXPECT_CALL(
         *connection_,
         CloseConnection(QUIC_HTTP_FRAME_UNEXPECTED_ON_CONTROL_STREAM, _, _));
+  } else {
+    EXPECT_CALL(debug_visitor, OnGoAwayFrameReceived(goaway));
   }
 
   receive_control_stream_->OnStreamFrame(frame);
@@ -338,6 +343,35 @@
             NumBytesConsumed());
 }
 
+TEST_P(QuicReceiveControlStreamTest, ReceiveUnknownFrame) {
+  StrictMock<MockHttp3DebugVisitor> debug_visitor;
+  session_.set_debug_visitor(&debug_visitor);
+
+  const QuicStreamId id = receive_control_stream_->id();
+
+  // Receive SETTINGS frame.
+  SettingsFrame settings;
+  std::string settings_frame = EncodeSettings(settings);
+  EXPECT_CALL(debug_visitor, OnSettingsFrameReceived(settings));
+  receive_control_stream_->OnStreamFrame(QuicStreamFrame(id, /* fin = */ false,
+                                                         /* offset = */ 1,
+                                                         settings_frame));
+
+  // Receive unknown frame.
+  std::string unknown_frame = quiche::QuicheTextUtils::HexDecode(
+      "21"        // reserved frame type
+      "03"        // payload length
+      "666f6f");  // payload "foo"
+
+  EXPECT_CALL(debug_visitor, OnUnknownFrameStart(id, /* frame_type = */ 0x21));
+  EXPECT_CALL(debug_visitor,
+              OnUnknownFramePayload(id, /* payload_length = */ 3));
+  EXPECT_CALL(debug_visitor, OnUnknownFrameEnd(id));
+  receive_control_stream_->OnStreamFrame(
+      QuicStreamFrame(id, /* fin = */ false,
+                      /* offset = */ 1 + settings_frame.size(), unknown_frame));
+}
+
 TEST_P(QuicReceiveControlStreamTest, UnknownFrameBeforeSettings) {
   std::string unknown_frame = quiche::QuicheTextUtils::HexDecode(
       "21"        // reserved frame type
diff --git a/quic/core/http/quic_send_control_stream.cc b/quic/core/http/quic_send_control_stream.cc
index 01c03dd..0ea3d6a 100644
--- a/quic/core/http/quic_send_control_stream.cc
+++ b/quic/core/http/quic_send_control_stream.cc
@@ -20,16 +20,17 @@
 
 QuicSendControlStream::QuicSendControlStream(
     QuicStreamId id,
-    QuicSession* session,
+    QuicSpdySession* spdy_session,
     uint64_t qpack_maximum_dynamic_table_capacity,
     uint64_t qpack_maximum_blocked_streams,
     uint64_t max_inbound_header_list_size)
-    : QuicStream(id, session, /*is_static = */ true, WRITE_UNIDIRECTIONAL),
+    : QuicStream(id, spdy_session, /*is_static = */ true, WRITE_UNIDIRECTIONAL),
       settings_sent_(false),
       qpack_maximum_dynamic_table_capacity_(
           qpack_maximum_dynamic_table_capacity),
       qpack_maximum_blocked_streams_(qpack_maximum_blocked_streams),
-      max_inbound_header_list_size_(max_inbound_header_list_size) {}
+      max_inbound_header_list_size_(max_inbound_header_list_size),
+      spdy_session_(spdy_session) {}
 
 void QuicSendControlStream::OnStreamReset(const QuicRstStreamFrame& /*frame*/) {
   QUIC_BUG << "OnStreamReset() called for write unidirectional stream.";
@@ -80,9 +81,8 @@
       HttpEncoder::SerializeSettingsFrame(settings, &buffer);
   QUIC_DVLOG(1) << "Control stream " << id() << " is writing settings frame "
                 << settings;
-  QuicSpdySession* spdy_session = static_cast<QuicSpdySession*>(session());
-  if (spdy_session->debug_visitor() != nullptr) {
-    spdy_session->debug_visitor()->OnSettingsFrameSent(settings);
+  if (spdy_session_->debug_visitor()) {
+    spdy_session_->debug_visitor()->OnSettingsFrameSent(settings);
   }
   WriteOrBufferData(quiche::QuicheStringPiece(buffer.get(), frame_length),
                     /*fin = */ false, nullptr);
@@ -101,6 +101,11 @@
     const PriorityUpdateFrame& priority_update) {
   QuicConnection::ScopedPacketFlusher flusher(session()->connection());
   MaybeSendSettingsFrame();
+
+  if (spdy_session_->debug_visitor()) {
+    spdy_session_->debug_visitor()->OnPriorityUpdateFrameSent(priority_update);
+  }
+
   std::unique_ptr<char[]> buffer;
   QuicByteCount frame_length =
       HttpEncoder::SerializePriorityUpdateFrame(priority_update, &buffer);
@@ -112,10 +117,14 @@
 
 void QuicSendControlStream::SendMaxPushIdFrame(PushId max_push_id) {
   QuicConnection::ScopedPacketFlusher flusher(session()->connection());
-
   MaybeSendSettingsFrame();
+
   MaxPushIdFrame frame;
   frame.push_id = max_push_id;
+  if (spdy_session_->debug_visitor()) {
+    spdy_session_->debug_visitor()->OnMaxPushIdFrameSent(frame);
+  }
+
   std::unique_ptr<char[]> buffer;
   QuicByteCount frame_length =
       HttpEncoder::SerializeMaxPushIdFrame(frame, &buffer);
@@ -125,8 +134,8 @@
 
 void QuicSendControlStream::SendGoAway(QuicStreamId stream_id) {
   QuicConnection::ScopedPacketFlusher flusher(session()->connection());
-
   MaybeSendSettingsFrame();
+
   GoAwayFrame frame;
   // If the peer hasn't created any stream yet. Use stream id 0 to indicate no
   // request is accepted.
@@ -135,6 +144,10 @@
     stream_id = 0;
   }
   frame.stream_id = stream_id;
+  if (spdy_session_->debug_visitor()) {
+    spdy_session_->debug_visitor()->OnGoAwayFrameSent(stream_id);
+  }
+
   std::unique_ptr<char[]> buffer;
   QuicByteCount frame_length =
       HttpEncoder::SerializeGoAwayFrame(frame, &buffer);
diff --git a/quic/core/http/quic_send_control_stream.h b/quic/core/http/quic_send_control_stream.h
index 899abb9..3771cdd 100644
--- a/quic/core/http/quic_send_control_stream.h
+++ b/quic/core/http/quic_send_control_stream.h
@@ -13,16 +13,16 @@
 
 namespace quic {
 
-class QuicSession;
+class QuicSpdySession;
 
-// 3.2.1 Control Stream.
+// 6.2.1 Control Stream.
 // The send control stream is self initiated and is write only.
 class QUIC_EXPORT_PRIVATE QuicSendControlStream : public QuicStream {
  public:
   // |session| can't be nullptr, and the ownership is not passed. The stream can
   // only be accessed through the session.
   QuicSendControlStream(QuicStreamId id,
-                        QuicSession* session,
+                        QuicSpdySession* session,
                         uint64_t qpack_maximum_dynamic_table_capacity,
                         uint64_t qpack_maximum_blocked_streams,
                         uint64_t max_inbound_header_list_size);
@@ -65,6 +65,8 @@
   const uint64_t qpack_maximum_blocked_streams_;
   // SETTINGS_MAX_HEADER_LIST_SIZE value to send.
   const uint64_t max_inbound_header_list_size_;
+
+  QuicSpdySession* const spdy_session_;
 };
 
 }  // namespace quic
diff --git a/quic/core/http/quic_send_control_stream_test.cc b/quic/core/http/quic_send_control_stream_test.cc
index 8332cec..fb53b84 100644
--- a/quic/core/http/quic_send_control_stream_test.cc
+++ b/quic/core/http/quic_send_control_stream_test.cc
@@ -19,6 +19,7 @@
 namespace {
 
 using ::testing::_;
+using ::testing::AnyNumber;
 using ::testing::Invoke;
 using ::testing::StrictMock;
 
@@ -201,6 +202,22 @@
   send_control_stream_->OnStreamFrame(frame);
 }
 
+TEST_P(QuicSendControlStreamTest, SendGoAway) {
+  Initialize();
+
+  StrictMock<MockHttp3DebugVisitor> debug_visitor;
+  session_.set_debug_visitor(&debug_visitor);
+
+  QuicStreamId stream_id = 4;
+
+  EXPECT_CALL(session_, WritevData(send_control_stream_->id(), _, _, _, _, _))
+      .Times(AnyNumber());
+  EXPECT_CALL(debug_visitor, OnSettingsFrameSent(_));
+  EXPECT_CALL(debug_visitor, OnGoAwayFrameSent(stream_id));
+
+  send_control_stream_->SendGoAway(stream_id);
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace quic
diff --git a/quic/core/http/quic_spdy_session.cc b/quic/core/http/quic_spdy_session.cc
index 42a1dd5..713a8d0 100644
--- a/quic/core/http/quic_spdy_session.cc
+++ b/quic/core/http/quic_spdy_session.cc
@@ -672,13 +672,6 @@
     return;
   }
 
-  if (VersionUsesHttp3(transport_version()) &&
-      promised_stream_id > max_allowed_push_id()) {
-    QUIC_BUG
-        << "Server shouldn't send push id higher than client's MAX_PUSH_ID.";
-    return;
-  }
-
   if (!VersionUsesHttp3(transport_version())) {
     SpdyPushPromiseIR push_promise(original_stream_id, promised_stream_id,
                                    std::move(headers));
@@ -692,9 +685,21 @@
     return;
   }
 
+  if (promised_stream_id > max_allowed_push_id()) {
+    QUIC_BUG
+        << "Server shouldn't send push id higher than client's MAX_PUSH_ID.";
+    return;
+  }
+
   // Encode header list.
   std::string encoded_headers =
       qpack_encoder_->EncodeHeaderList(original_stream_id, headers, nullptr);
+
+  if (debug_visitor_) {
+    debug_visitor_->OnPushPromiseFrameSent(original_stream_id,
+                                           promised_stream_id, headers);
+  }
+
   PushPromiseFrame frame;
   frame.push_id = promised_stream_id;
   frame.headers = encoded_headers;
@@ -1149,6 +1154,9 @@
         max_inbound_header_list_size_);
     send_control_stream_ = send_control.get();
     ActivateStream(std::move(send_control));
+    if (debug_visitor_) {
+      debug_visitor_->OnControlStreamCreated(send_control_stream_->id());
+    }
   }
 
   if (!qpack_decoder_send_stream_ &&
@@ -1159,6 +1167,10 @@
     ActivateStream(std::move(decoder_send));
     qpack_decoder_->set_qpack_stream_sender_delegate(
         qpack_decoder_send_stream_);
+    if (debug_visitor_) {
+      debug_visitor_->OnQpackDecoderStreamCreated(
+          qpack_decoder_send_stream_->id());
+    }
   }
 
   if (!qpack_encoder_send_stream_ &&
@@ -1169,6 +1181,10 @@
     ActivateStream(std::move(encoder_send));
     qpack_encoder_->set_qpack_stream_sender_delegate(
         qpack_encoder_send_stream_);
+    if (debug_visitor_) {
+      debug_visitor_->OnQpackEncoderStreamCreated(
+          qpack_encoder_send_stream_->id());
+    }
   }
 }
 
diff --git a/quic/core/http/quic_spdy_session.h b/quic/core/http/quic_spdy_session.h
index eefb5ef..71a001d 100644
--- a/quic/core/http/quic_spdy_session.h
+++ b/quic/core/http/quic_spdy_session.h
@@ -60,20 +60,73 @@
 
   virtual ~Http3DebugVisitor();
 
+  // TODO(https://crbug.com/1062700): Remove default implementation of all
+  // methods after Chrome's QuicHttp3Logger has overrides.  This is to make sure
+  // QUICHE merge is not blocked on having to add those overrides, they can
+  // happen asynchronously.
+
+  // Creation of unidirectional streams.
+
+  // Called when locally-initiated control stream is created.
+  virtual void OnControlStreamCreated(QuicStreamId /*stream_id*/) {}
+  // Called when locally-initiated QPACK encoder stream is created.
+  virtual void OnQpackEncoderStreamCreated(QuicStreamId /*stream_id*/) {}
+  // Called when locally-initiated QPACK decoder stream is created.
+  virtual void OnQpackDecoderStreamCreated(QuicStreamId /*stream_id*/) {}
   // Called when peer's control stream type is received.
   virtual void OnPeerControlStreamCreated(QuicStreamId /*stream_id*/) = 0;
-
   // Called when peer's QPACK encoder stream type is received.
   virtual void OnPeerQpackEncoderStreamCreated(QuicStreamId /*stream_id*/) = 0;
-
   // Called when peer's QPACK decoder stream type is received.
   virtual void OnPeerQpackDecoderStreamCreated(QuicStreamId /*stream_id*/) = 0;
 
-  // Called when SETTINGS frame is received.
+  // Incoming HTTP/3 frames on the control stream.
+  virtual void OnCancelPushFrameReceived(CancelPushFrame /*frame*/) {}
   virtual void OnSettingsFrameReceived(const SettingsFrame& /*frame*/) = 0;
+  virtual void OnGoAwayFrameReceived(GoAwayFrame /*frame*/) {}
+  virtual void OnMaxPushIdFrameReceived(MaxPushIdFrame /*frame*/) {}
+  virtual void OnPriorityUpdateFrameReceived(PriorityUpdateFrame /*frame*/) {}
 
-  // Called when SETTINGS frame is sent.
+  // Incoming HTTP/3 frames on request or push streams.
+  virtual void OnDataFrameStart(QuicStreamId /*stream_id*/) {}
+  virtual void OnDataFramePayload(QuicStreamId /*stream_id*/,
+                                  QuicByteCount /*payload_fragment_length*/) {}
+  virtual void OnDataFrameEnd(QuicStreamId /*stream_id*/) {}
+  virtual void OnHeadersFrameReceived(QuicStreamId /*stream_id*/,
+                                      QuicByteCount /*payload_length*/) {}
+  virtual void OnHeadersDecoded(QuicStreamId /*stream_id*/,
+                                QuicHeaderList /*headers*/) {}
+  virtual void OnPushPromiseFrameReceived(QuicStreamId /*stream_id*/,
+                                          QuicStreamId /*push_id*/) {}
+  virtual void OnPushPromiseDecoded(QuicStreamId /*stream_id*/,
+                                    QuicStreamId /*push_id*/,
+                                    QuicHeaderList /*headers*/) {}
+
+  // Incoming HTTP/3 frames of unknown type on any stream.
+  virtual void OnUnknownFrameStart(QuicStreamId /*stream_id*/,
+                                   uint64_t /*frame_type*/) {}
+  virtual void OnUnknownFramePayload(
+      QuicStreamId /*stream_id*/,
+      QuicByteCount /*payload_fragment_length*/) {}
+  virtual void OnUnknownFrameEnd(QuicStreamId /*stream_id*/) {}
+
+  // Outgoing HTTP/3 frames on the control stream.
   virtual void OnSettingsFrameSent(const SettingsFrame& /*frame*/) = 0;
+  virtual void OnGoAwayFrameSent(QuicStreamId /*stream_id*/) {}
+  virtual void OnMaxPushIdFrameSent(MaxPushIdFrame /*frame*/) {}
+  virtual void OnPriorityUpdateFrameSent(PriorityUpdateFrame /*frame*/) {}
+
+  // Outgoing HTTP/3 frames on request or push streams.
+  virtual void OnDataFrameSent(QuicStreamId /*stream_id*/,
+                               QuicByteCount /*payload_length*/) {}
+  virtual void OnHeadersFrameSent(
+      QuicStreamId /*stream_id*/,
+      const spdy::SpdyHeaderBlock& /*header_block*/) {}
+  virtual void OnPushPromiseFrameSent(
+      QuicStreamId /*stream_id*/,
+      QuicStreamId
+      /*push_id*/,
+      const spdy::SpdyHeaderBlock& /*header_block*/) {}
 };
 
 // A QUIC session for HTTP.
diff --git a/quic/core/http/quic_spdy_session_test.cc b/quic/core/http/quic_spdy_session_test.cc
index ec91ff6..fb6ebfc 100644
--- a/quic/core/http/quic_spdy_session_test.cc
+++ b/quic/core/http/quic_spdy_session_test.cc
@@ -160,19 +160,6 @@
   MOCK_METHOD0(OnCanWrite, void());
 };
 
-class MockHttp3DebugVisitor : public Http3DebugVisitor {
- public:
-  MOCK_METHOD1(OnPeerControlStreamCreated, void(QuicStreamId));
-
-  MOCK_METHOD1(OnPeerQpackEncoderStreamCreated, void(QuicStreamId));
-
-  MOCK_METHOD1(OnPeerQpackDecoderStreamCreated, void(QuicStreamId));
-
-  MOCK_METHOD1(OnSettingsFrameReceived, void(const SettingsFrame&));
-
-  MOCK_METHOD1(OnSettingsFrameSent, void(const SettingsFrame&));
-};
-
 class TestStream : public QuicSpdyStream {
  public:
   TestStream(QuicStreamId id, QuicSpdySession* session, StreamType type)
@@ -1040,6 +1027,10 @@
   if (!VersionUsesHttp3(transport_version())) {
     return;
   }
+
+  StrictMock<MockHttp3DebugVisitor> debug_visitor;
+  session_.set_debug_visitor(&debug_visitor);
+
   connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
   MockPacketWriter* writer = static_cast<MockPacketWriter*>(
       QuicConnectionPeer::GetWriter(session_.connection()));
@@ -1048,9 +1039,12 @@
   if (connection_->version().HasHandshakeDone()) {
     EXPECT_CALL(*connection_, SendControlFrame(_));
   }
+
   CryptoHandshakeMessage message;
+  EXPECT_CALL(debug_visitor, OnSettingsFrameSent(_));
   session_.GetMutableCryptoStream()->OnHandshakeMessage(message);
 
+  EXPECT_CALL(debug_visitor, OnGoAwayFrameSent(_));
   session_.SendHttp3GoAway();
   EXPECT_TRUE(session_.http3_goaway_sent());
 
@@ -2227,6 +2221,9 @@
     return;
   }
 
+  StrictMock<MockHttp3DebugVisitor> debug_visitor;
+  session_.set_debug_visitor(&debug_visitor);
+
   // Create control stream.
   QuicStreamId receive_control_stream_id =
       GetNthClientInitiatedUnidirectionalStreamId(transport_version(), 3);
@@ -2235,6 +2232,8 @@
   QuicStreamOffset offset = 0;
   QuicStreamFrame data1(receive_control_stream_id, false, offset, stream_type);
   offset += stream_type.length();
+  EXPECT_CALL(debug_visitor,
+              OnPeerControlStreamCreated(receive_control_stream_id));
   session_.OnStreamFrame(data1);
   EXPECT_EQ(receive_control_stream_id,
             QuicSpdySessionPeer::GetReceiveControlStream(&session_)->id());
@@ -2244,6 +2243,7 @@
   QuicStreamFrame data2(receive_control_stream_id, false, offset,
                         serialized_settings);
   offset += serialized_settings.length();
+  EXPECT_CALL(debug_visitor, OnSettingsFrameReceived(_));
   session_.OnStreamFrame(data2);
 
   // PRIORITY_UPDATE frame for first request stream.
@@ -2262,6 +2262,7 @@
   TestStream* stream1 = session_.CreateIncomingStream(stream_id1);
   EXPECT_EQ(QuicStream::kDefaultUrgency,
             stream1->precedence().spdy3_priority());
+  EXPECT_CALL(debug_visitor, OnPriorityUpdateFrameReceived(priority_update1));
   session_.OnStreamFrame(data3);
   EXPECT_EQ(2u, stream1->precedence().spdy3_priority());
 
@@ -2279,6 +2280,7 @@
 
   // PRIORITY_UPDATE frame arrives before stream creation,
   // priority value is buffered.
+  EXPECT_CALL(debug_visitor, OnPriorityUpdateFrameReceived(priority_update2));
   session_.OnStreamFrame(stream_frame3);
   // Priority is applied upon stream construction.
   TestStream* stream2 = session_.CreateIncomingStream(stream_id2);
@@ -2420,16 +2422,21 @@
   if (!VersionUsesHttp3(transport_version())) {
     return;
   }
+
+  StrictMock<MockHttp3DebugVisitor> debug_visitor;
+  session_.set_debug_visitor(&debug_visitor);
+
   MockPacketWriter* writer = static_cast<MockPacketWriter*>(
       QuicConnectionPeer::GetWriter(session_.connection()));
   EXPECT_CALL(*writer, WritePacket(_, _, _, _, _))
       .WillRepeatedly(Return(WriteResult(WRITE_STATUS_OK, 0)));
 
+  EXPECT_CALL(debug_visitor, OnSettingsFrameSent(_));
   EXPECT_CALL(*connection_, SendControlFrame(_))
       .WillRepeatedly(Invoke(&ClearControlFrame));
   CryptoHandshakeMessage message;
   session_.GetMutableCryptoStream()->OnHandshakeMessage(message);
-  MockHttp3DebugVisitor debug_visitor;
+
   // Use an arbitrary stream id.
   QuicStreamId stream_id =
       GetNthClientInitiatedUnidirectionalStreamId(transport_version(), 3);
@@ -2437,12 +2444,11 @@
 
   QuicStreamFrame data1(stream_id, false, 0,
                         quiche::QuicheStringPiece(type, 1));
-  EXPECT_CALL(debug_visitor, OnPeerControlStreamCreated(stream_id)).Times(0);
+  EXPECT_CALL(debug_visitor, OnPeerControlStreamCreated(stream_id));
   session_.OnStreamFrame(data1);
   EXPECT_EQ(stream_id,
             QuicSpdySessionPeer::GetReceiveControlStream(&session_)->id());
 
-  session_.set_debug_visitor(&debug_visitor);
   SettingsFrame settings;
   settings.values[SETTINGS_QPACK_MAX_TABLE_CAPACITY] = 512;
   settings.values[SETTINGS_MAX_HEADER_LIST_SIZE] = 5;
@@ -2694,7 +2700,7 @@
     return;
   }
 
-  MockHttp3DebugVisitor debug_visitor;
+  StrictMock<MockHttp3DebugVisitor> debug_visitor;
   session_.set_debug_visitor(&debug_visitor);
 
   QuicStreamId id1 =
@@ -2818,6 +2824,9 @@
     return;
   }
 
+  StrictMock<MockHttp3DebugVisitor> debug_visitor;
+  session_.set_debug_visitor(&debug_visitor);
+
   // Create control stream.
   QuicStreamId receive_control_stream_id =
       GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 3);
@@ -2827,6 +2836,8 @@
   QuicStreamFrame data1(receive_control_stream_id, /* fin = */ false, offset,
                         stream_type);
   offset += stream_type.length();
+  EXPECT_CALL(debug_visitor,
+              OnPeerControlStreamCreated(receive_control_stream_id));
   session_.OnStreamFrame(data1);
   EXPECT_EQ(receive_control_stream_id,
             QuicSpdySessionPeer::GetReceiveControlStream(&session_)->id());
@@ -2837,6 +2848,7 @@
       HttpEncoder::SerializeCancelPushFrame(cancel_push, &buffer);
   QuicStreamFrame data2(receive_control_stream_id, /* fin = */ false, offset,
                         quiche::QuicheStringPiece(buffer.get(), frame_length));
+  EXPECT_CALL(debug_visitor, OnCancelPushFrameReceived(_));
   session_.OnStreamFrame(data2);
 }
 
diff --git a/quic/core/http/quic_spdy_stream.cc b/quic/core/http/quic_spdy_stream.cc
index 9d2772a..3a4c146 100644
--- a/quic/core/http/quic_spdy_stream.cc
+++ b/quic/core/http/quic_spdy_stream.cc
@@ -293,6 +293,10 @@
   }
   QuicConnection::ScopedPacketFlusher flusher(spdy_session_->connection());
 
+  if (spdy_session_->debug_visitor()) {
+    spdy_session_->debug_visitor()->OnDataFrameSent(id(), data.length());
+  }
+
   // Write frame header.
   std::unique_ptr<char[]> buffer;
   QuicByteCount header_length =
@@ -406,6 +410,11 @@
     return {0, false};
   }
 
+  if (spdy_session_->debug_visitor()) {
+    spdy_session_->debug_visitor()->OnDataFrameSent(id(),
+                                                    slices.total_length());
+  }
+
   QuicConnection::ScopedPacketFlusher flusher(spdy_session_->connection());
 
   // Write frame header.
@@ -546,13 +555,23 @@
       /* is_sent = */ false, headers.compressed_header_bytes(),
       headers.uncompressed_header_bytes());
 
-  if (spdy_session_->promised_stream_id() ==
-      QuicUtils::GetInvalidStreamId(session()->transport_version())) {
+  const QuicStreamId promised_stream_id = spdy_session()->promised_stream_id();
+  Http3DebugVisitor* const debug_visitor = spdy_session()->debug_visitor();
+  if (promised_stream_id ==
+      QuicUtils::GetInvalidStreamId(transport_version())) {
+    if (debug_visitor) {
+      debug_visitor->OnHeadersDecoded(id(), headers);
+    }
+
     const QuicByteCount frame_length = headers_decompressed_
                                            ? trailers_payload_length_
                                            : headers_payload_length_;
     OnStreamHeaderList(/* fin = */ false, frame_length, headers);
   } else {
+    if (debug_visitor) {
+      debug_visitor->OnPushPromiseDecoded(id(), promised_stream_id, headers);
+    }
+
     spdy_session_->OnHeaderList(headers);
   }
 
@@ -850,6 +869,10 @@
     return false;
   }
 
+  if (spdy_session_->debug_visitor()) {
+    spdy_session_->debug_visitor()->OnDataFrameStart(id());
+  }
+
   sequencer()->MarkConsumed(body_manager_.OnNonBody(header_length));
 
   return true;
@@ -858,6 +881,10 @@
 bool QuicSpdyStream::OnDataFramePayload(quiche::QuicheStringPiece payload) {
   DCHECK(VersionUsesHttp3(transport_version()));
 
+  if (spdy_session_->debug_visitor()) {
+    spdy_session_->debug_visitor()->OnDataFramePayload(id(), payload.length());
+  }
+
   body_manager_.OnBody(payload);
 
   return true;
@@ -865,6 +892,11 @@
 
 bool QuicSpdyStream::OnDataFrameEnd() {
   DCHECK(VersionUsesHttp3(transport_version()));
+
+  if (spdy_session_->debug_visitor()) {
+    spdy_session_->debug_visitor()->OnDataFrameEnd(id());
+  }
+
   QUIC_DVLOG(1) << ENDPOINT
                 << "Reaches the end of a data frame. Total bytes received are "
                 << body_manager_.total_body_bytes_received();
@@ -966,6 +998,18 @@
   DCHECK(VersionUsesHttp3(transport_version()));
   DCHECK(qpack_decoded_headers_accumulator_);
 
+  if (spdy_session_->debug_visitor()) {
+    if (spdy_session_->promised_stream_id() ==
+        QuicUtils::GetInvalidStreamId(transport_version())) {
+      spdy_session_->debug_visitor()->OnHeadersFrameReceived(
+          id(), headers_decompressed_ ? trailers_payload_length_
+                                      : headers_payload_length_);
+    } else {
+      spdy_session_->debug_visitor()->OnPushPromiseFrameReceived(
+          id(), spdy_session_->promised_stream_id());
+    }
+  }
+
   qpack_decoded_headers_accumulator_->EndHeaderBlock();
 
   // If decoding is complete or an error is detected, then
@@ -1018,6 +1062,10 @@
 
 bool QuicSpdyStream::OnUnknownFrameStart(uint64_t frame_type,
                                          QuicByteCount header_length) {
+  if (spdy_session_->debug_visitor()) {
+    spdy_session_->debug_visitor()->OnUnknownFrameStart(id(), frame_type);
+  }
+
   // Ignore unknown frames, but consume frame header.
   QUIC_DVLOG(1) << ENDPOINT << "Discarding " << header_length
                 << " byte long frame header of frame of unknown type "
@@ -1027,6 +1075,10 @@
 }
 
 bool QuicSpdyStream::OnUnknownFramePayload(quiche::QuicheStringPiece payload) {
+  if (spdy_session_->debug_visitor()) {
+    spdy_session_->debug_visitor()->OnUnknownFramePayload(id(), payload.size());
+  }
+
   // Ignore unknown frames, but consume frame payload.
   QUIC_DVLOG(1) << ENDPOINT << "Discarding " << payload.size()
                 << " bytes of payload of frame of unknown type.";
@@ -1035,6 +1087,10 @@
 }
 
 bool QuicSpdyStream::OnUnknownFrameEnd() {
+  if (spdy_session_->debug_visitor()) {
+    spdy_session_->debug_visitor()->OnUnknownFrameEnd(id());
+  }
+
   return true;
 }
 
@@ -1054,6 +1110,10 @@
       spdy_session_->qpack_encoder()->EncodeHeaderList(
           id(), header_block, &encoder_stream_sent_byte_count);
 
+  if (spdy_session_->debug_visitor()) {
+    spdy_session_->debug_visitor()->OnHeadersFrameSent(id(), header_block);
+  }
+
   // Write HEADERS frame.
   std::unique_ptr<char[]> headers_frame_header;
   const size_t headers_frame_header_length =
diff --git a/quic/core/http/quic_spdy_stream_test.cc b/quic/core/http/quic_spdy_stream_test.cc
index 98b7e85..09fb664 100644
--- a/quic/core/http/quic_spdy_stream_test.cc
+++ b/quic/core/http/quic_spdy_stream_test.cc
@@ -4,6 +4,7 @@
 
 #include "net/third_party/quiche/src/quic/core/http/quic_spdy_stream.h"
 
+#include <cstring>
 #include <memory>
 #include <string>
 #include <utility>
@@ -1421,6 +1422,8 @@
   }
 
   InitializeWithPerspective(kShouldProcessData, Perspective::IS_CLIENT);
+  StrictMock<MockHttp3DebugVisitor> debug_visitor;
+  session_->set_debug_visitor(&debug_visitor);
 
   // Four writes on the request stream: HEADERS frame header and payload both
   // for headers and trailers.
@@ -1435,12 +1438,14 @@
 
   // Write the initial headers, without a FIN.
   EXPECT_CALL(*stream_, WriteHeadersMock(false));
+  EXPECT_CALL(debug_visitor, OnHeadersFrameSent(stream_->id(), _));
   stream_->WriteHeaders(SpdyHeaderBlock(), /*fin=*/false, nullptr);
 
   // Writing trailers implicitly sends a FIN.
   SpdyHeaderBlock trailers;
   trailers["trailer key"] = "trailer value";
   EXPECT_CALL(*stream_, WriteHeadersMock(true));
+  EXPECT_CALL(debug_visitor, OnHeadersFrameSent(stream_->id(), _));
   stream_->WriteTrailers(std::move(trailers), nullptr);
   EXPECT_TRUE(stream_->fin_sent());
 }
@@ -1451,16 +1456,23 @@
   }
 
   InitializeWithPerspective(kShouldProcessData, Perspective::IS_CLIENT);
+  StrictMock<MockHttp3DebugVisitor> debug_visitor;
+  session_->set_debug_visitor(&debug_visitor);
 
   // Two writes on the request stream: HEADERS frame header and payload.
   EXPECT_CALL(*session_, WritevData(stream_->id(), _, _, _, _, _)).Times(2);
   EXPECT_CALL(*stream_, WriteHeadersMock(false));
+  EXPECT_CALL(debug_visitor, OnHeadersFrameSent(stream_->id(), _));
   stream_->WriteHeaders(SpdyHeaderBlock(), /*fin=*/false, nullptr);
 
   // PRIORITY_UPDATE frame on the control stream.
   auto send_control_stream =
       QuicSpdySessionPeer::GetSendControlStream(session_.get());
   EXPECT_CALL(*session_, WritevData(send_control_stream->id(), _, _, _, _, _));
+  PriorityUpdateFrame priority_update;
+  priority_update.prioritized_element_id = 0;
+  priority_update.priority_field_value = "u=0";
+  EXPECT_CALL(debug_visitor, OnPriorityUpdateFrameSent(priority_update));
   stream_->SetPriority(spdy::SpdyStreamPrecedence(kV3HighestPriority));
 }
 
@@ -2126,16 +2138,20 @@
   Initialize(kShouldProcessData);
   testing::InSequence s;
   session_->qpack_decoder()->OnSetDynamicTableCapacity(1024);
+  StrictMock<MockHttp3DebugVisitor> debug_visitor;
+  session_->set_debug_visitor(&debug_visitor);
 
   auto decoder_send_stream =
       QuicSpdySessionPeer::GetQpackDecoderSendStream(session_.get());
 
-  // The stream byte will be written in the first byte.
-  EXPECT_CALL(*session_, WritevData(decoder_send_stream->id(), _, _, _, _, _));
   // Deliver dynamic table entry to decoder.
   session_->qpack_decoder()->OnInsertWithoutNameReference("foo", "bar");
 
   // HEADERS frame referencing first dynamic table entry.
+  EXPECT_CALL(debug_visitor, OnHeadersFrameReceived(stream_->id(), _));
+  // Decoder stream type and header acknowledgement.
+  EXPECT_CALL(*session_, WritevData(decoder_send_stream->id(), _, _, _, _, _));
+  EXPECT_CALL(debug_visitor, OnHeadersDecoded(stream_->id(), _));
   std::string headers =
       HeadersFrame(quiche::QuicheTextUtils::HexDecode("020080"));
   stream_->OnStreamFrame(QuicStreamFrame(stream_->id(), false, 0, headers));
@@ -2149,15 +2165,22 @@
 
   // DATA frame.
   std::string data = DataFrame(kDataFramePayload);
+  EXPECT_CALL(debug_visitor, OnDataFrameStart(stream_->id()));
+  EXPECT_CALL(debug_visitor,
+              OnDataFramePayload(stream_->id(), strlen(kDataFramePayload)));
+  EXPECT_CALL(debug_visitor, OnDataFrameEnd(stream_->id()));
   stream_->OnStreamFrame(QuicStreamFrame(stream_->id(), false, /* offset = */
                                          headers.length(), data));
   EXPECT_EQ(kDataFramePayload, stream_->data());
 
-  EXPECT_CALL(*session_, WritevData(decoder_send_stream->id(), _, _, _, _, _));
   // Deliver second dynamic table entry to decoder.
   session_->qpack_decoder()->OnInsertWithoutNameReference("trailing", "foobar");
 
   // Trailing HEADERS frame referencing second dynamic table entry.
+  EXPECT_CALL(debug_visitor, OnHeadersFrameReceived(stream_->id(), _));
+  // Header acknowledgement.
+  EXPECT_CALL(*session_, WritevData(decoder_send_stream->id(), _, _, _, _, _));
+  EXPECT_CALL(debug_visitor, OnHeadersDecoded(stream_->id(), _));
   std::string trailers =
       HeadersFrame(quiche::QuicheTextUtils::HexDecode("030080"));
   stream_->OnStreamFrame(QuicStreamFrame(stream_->id(), true, /* offset = */
@@ -2181,10 +2204,13 @@
   Initialize(kShouldProcessData);
   testing::InSequence s;
   session_->qpack_decoder()->OnSetDynamicTableCapacity(1024);
+  StrictMock<MockHttp3DebugVisitor> debug_visitor;
+  session_->set_debug_visitor(&debug_visitor);
 
   // HEADERS frame referencing first dynamic table entry.
   std::string headers =
       HeadersFrame(quiche::QuicheTextUtils::HexDecode("020080"));
+  EXPECT_CALL(debug_visitor, OnHeadersFrameReceived(stream_->id(), _));
   stream_->OnStreamFrame(QuicStreamFrame(stream_->id(), false, 0, headers));
 
   // Decoding is blocked because dynamic table entry has not been received yet.
@@ -2193,8 +2219,9 @@
   auto decoder_send_stream =
       QuicSpdySessionPeer::GetQpackDecoderSendStream(session_.get());
 
-  // The stream byte will be written in the first byte.
+  // Decoder stream type and header acknowledgement.
   EXPECT_CALL(*session_, WritevData(decoder_send_stream->id(), _, _, _, _, _));
+  EXPECT_CALL(debug_visitor, OnHeadersDecoded(stream_->id(), _));
   // Deliver dynamic table entry to decoder.
   session_->qpack_decoder()->OnInsertWithoutNameReference("foo", "bar");
   EXPECT_TRUE(stream_->headers_decompressed());
@@ -2205,6 +2232,10 @@
 
   // DATA frame.
   std::string data = DataFrame(kDataFramePayload);
+  EXPECT_CALL(debug_visitor, OnDataFrameStart(stream_->id()));
+  EXPECT_CALL(debug_visitor,
+              OnDataFramePayload(stream_->id(), strlen(kDataFramePayload)));
+  EXPECT_CALL(debug_visitor, OnDataFrameEnd(stream_->id()));
   stream_->OnStreamFrame(QuicStreamFrame(stream_->id(), false, /* offset = */
                                          headers.length(), data));
   EXPECT_EQ(kDataFramePayload, stream_->data());
@@ -2212,6 +2243,7 @@
   // Trailing HEADERS frame referencing second dynamic table entry.
   std::string trailers =
       HeadersFrame(quiche::QuicheTextUtils::HexDecode("030080"));
+  EXPECT_CALL(debug_visitor, OnHeadersFrameReceived(stream_->id(), _));
   stream_->OnStreamFrame(QuicStreamFrame(stream_->id(), true, /* offset = */
                                          headers.length() + data.length(),
                                          trailers));
@@ -2220,6 +2252,7 @@
   EXPECT_FALSE(stream_->trailers_decompressed());
 
   EXPECT_CALL(*session_, WritevData(decoder_send_stream->id(), _, _, _, _, _));
+  EXPECT_CALL(debug_visitor, OnHeadersDecoded(stream_->id(), _));
   // Deliver second dynamic table entry to decoder.
   session_->qpack_decoder()->OnInsertWithoutNameReference("trailing", "foobar");
   EXPECT_TRUE(stream_->trailers_decompressed());
@@ -2463,6 +2496,25 @@
               ElementsAre(Pair("custom-key", "custom-value")));
 }
 
+TEST_P(QuicSpdyStreamIncrementalConsumptionTest, ReceiveUnknownFrame) {
+  if (!UsesHttp3()) {
+    return;
+  }
+
+  Initialize(kShouldProcessData);
+  StrictMock<MockHttp3DebugVisitor> debug_visitor;
+  session_->set_debug_visitor(&debug_visitor);
+
+  EXPECT_CALL(debug_visitor,
+              OnUnknownFrameStart(stream_->id(), /* frame_type = */ 0x21));
+  EXPECT_CALL(debug_visitor,
+              OnUnknownFramePayload(stream_->id(), /* payload_length = */ 3));
+  EXPECT_CALL(debug_visitor, OnUnknownFrameEnd(stream_->id()));
+
+  std::string unknown_frame = UnknownFrame(0x21, "foo");
+  OnStreamFrame(unknown_frame);
+}
+
 TEST_P(QuicSpdyStreamIncrementalConsumptionTest, UnknownFramesInterleaved) {
   if (!UsesHttp3()) {
     return;
@@ -2554,10 +2606,16 @@
     return;
   }
 
-  std::string headers = EncodeQpackHeaders({{"foo", "bar"}});
+  StrictMock<MockHttp3DebugVisitor> debug_visitor;
+  session_->set_debug_visitor(&debug_visitor);
 
+  SpdyHeaderBlock pushed_headers;
+  pushed_headers["foo"] = "bar";
+  std::string headers = EncodeQpackHeaders(pushed_headers);
+
+  const QuicStreamId push_id = 1;
   PushPromiseFrame push_promise;
-  push_promise.push_id = 0x01;
+  push_promise.push_id = push_id;
   push_promise.headers = headers;
   std::unique_ptr<char[]> buffer;
   uint64_t length = HttpEncoder::SerializePushPromiseFrameWithOnlyPushId(
@@ -2565,6 +2623,11 @@
   std::string data = std::string(buffer.get(), length) + headers;
   QuicStreamFrame frame(stream_->id(), false, 0, data);
 
+  EXPECT_CALL(debug_visitor,
+              OnPushPromiseFrameReceived(stream_->id(), push_id));
+  EXPECT_CALL(debug_visitor,
+              OnPushPromiseDecoded(stream_->id(), push_id,
+                                   AsHeaderList(pushed_headers)));
   EXPECT_CALL(*session_,
               OnPromiseHeaderList(stream_->id(), push_promise.push_id,
                                   headers.length(), _));
diff --git a/quic/test_tools/quic_test_utils.h b/quic/test_tools/quic_test_utils.h
index 5c4fa0a..224263f 100644
--- a/quic/test_tools/quic_test_utils.h
+++ b/quic/test_tools/quic_test_utils.h
@@ -17,6 +17,7 @@
 #include "net/third_party/quiche/src/quic/core/congestion_control/send_algorithm_interface.h"
 #include "net/third_party/quiche/src/quic/core/http/quic_client_push_promise_index.h"
 #include "net/third_party/quiche/src/quic/core/http/quic_server_session_base.h"
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_session.h"
 #include "net/third_party/quiche/src/quic/core/quic_connection.h"
 #include "net/third_party/quiche/src/quic/core/quic_framer.h"
 #include "net/third_party/quiche/src/quic/core/quic_packet_writer.h"
@@ -802,6 +803,48 @@
   std::unique_ptr<QuicCryptoStream> crypto_stream_;
 };
 
+class MockHttp3DebugVisitor : public Http3DebugVisitor {
+ public:
+  MOCK_METHOD1(OnControlStreamCreated, void(QuicStreamId));
+  MOCK_METHOD1(OnQpackEncoderStreamCreated, void(QuicStreamId));
+  MOCK_METHOD1(OnQpackDecoderStreamCreated, void(QuicStreamId));
+  MOCK_METHOD1(OnPeerControlStreamCreated, void(QuicStreamId));
+  MOCK_METHOD1(OnPeerQpackEncoderStreamCreated, void(QuicStreamId));
+  MOCK_METHOD1(OnPeerQpackDecoderStreamCreated, void(QuicStreamId));
+
+  MOCK_METHOD1(OnCancelPushFrameReceived, void(CancelPushFrame));
+  MOCK_METHOD1(OnSettingsFrameReceived, void(const SettingsFrame&));
+  MOCK_METHOD1(OnGoAwayFrameReceived, void(GoAwayFrame));
+  MOCK_METHOD1(OnMaxPushIdFrameReceived, void(MaxPushIdFrame));
+  MOCK_METHOD1(OnPriorityUpdateFrameReceived, void(PriorityUpdateFrame));
+
+  MOCK_METHOD1(OnDataFrameStart, void(QuicStreamId));
+  MOCK_METHOD2(OnDataFramePayload, void(QuicStreamId, QuicByteCount));
+  MOCK_METHOD1(OnDataFrameEnd, void(QuicStreamId));
+
+  MOCK_METHOD2(OnHeadersFrameReceived, void(QuicStreamId, QuicByteCount));
+  MOCK_METHOD2(OnHeadersDecoded, void(QuicStreamId, QuicHeaderList));
+
+  MOCK_METHOD2(OnPushPromiseFrameReceived, void(QuicStreamId, QuicStreamId));
+  MOCK_METHOD3(OnPushPromiseDecoded,
+               void(QuicStreamId, QuicStreamId, QuicHeaderList));
+
+  MOCK_METHOD2(OnUnknownFrameStart, void(QuicStreamId, uint64_t));
+  MOCK_METHOD2(OnUnknownFramePayload, void(QuicStreamId, QuicByteCount));
+  MOCK_METHOD1(OnUnknownFrameEnd, void(QuicStreamId));
+
+  MOCK_METHOD1(OnSettingsFrameSent, void(const SettingsFrame&));
+  MOCK_METHOD1(OnGoAwayFrameSent, void(QuicStreamId));
+  MOCK_METHOD1(OnMaxPushIdFrameSent, void(MaxPushIdFrame));
+  MOCK_METHOD1(OnPriorityUpdateFrameSent, void(PriorityUpdateFrame));
+
+  MOCK_METHOD2(OnDataFrameSent, void(QuicStreamId, QuicByteCount));
+  MOCK_METHOD2(OnHeadersFrameSent,
+               void(QuicStreamId, const spdy::SpdyHeaderBlock&));
+  MOCK_METHOD3(OnPushPromiseFrameSent,
+               void(QuicStreamId, QuicStreamId, const spdy::SpdyHeaderBlock&));
+};
+
 class TestQuicSpdyServerSession : public QuicServerSessionBase {
  public:
   // Takes ownership of |connection|.