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|.