WIP RFCv1 test
diff --git a/quic/core/crypto/crypto_utils.cc b/quic/core/crypto/crypto_utils.cc index f8eb06a..cb106ac 100644 --- a/quic/core/crypto/crypto_utils.cc +++ b/quic/core/crypto/crypto_utils.cc
@@ -135,6 +135,9 @@ const uint8_t kDraft29InitialSalt[] = {0xaf, 0xbf, 0xec, 0x28, 0x99, 0x93, 0xd2, 0x4c, 0x9e, 0x97, 0x86, 0xf1, 0x9c, 0x61, 0x11, 0xe0, 0x43, 0x90, 0xa8, 0x99}; +const uint8_t kRFCv1InitialSalt[] = {0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, + 0xb3, 0x4d, 0x17, 0x9a, 0xe6, 0xa4, 0xc8, + 0x0c, 0xad, 0xcc, 0xbb, 0x7f, 0x0a}; // Salts used by deployed versions of QUIC. When introducing a new version, // generate a new salt by running `openssl rand -hex 20`. @@ -155,9 +158,12 @@ const uint8_t* InitialSaltForVersion(const ParsedQuicVersion& version, size_t* out_len) { - static_assert(SupportedVersions().size() == 5u, + static_assert(SupportedVersions().size() == 6u, "Supported versions out of sync with initial encryption salts"); - if (version == ParsedQuicVersion::Draft29()) { + if (version == ParsedQuicVersion::RFCv1()) { + *out_len = ABSL_ARRAYSIZE(kRFCv1InitialSalt); + return kRFCv1InitialSalt; + } else if (version == ParsedQuicVersion::Draft29()) { *out_len = ABSL_ARRAYSIZE(kDraft29InitialSalt); return kDraft29InitialSalt; } else if (version == ParsedQuicVersion::T051()) { @@ -184,6 +190,11 @@ 0x6c, 0xb9, 0x6b, 0xe1}; const uint8_t kDraft29RetryIntegrityNonce[] = { 0xe5, 0x49, 0x30, 0xf9, 0x7f, 0x21, 0x36, 0xf0, 0x53, 0x0a, 0x8c, 0x1c}; +const uint8_t kRFCv1RetryIntegrityKey[] = {0xbe, 0x0c, 0x69, 0x0b, 0x9f, 0x66, + 0x57, 0x5a, 0x1d, 0x76, 0x6b, 0x54, + 0xe3, 0x68, 0xc8, 0x4e}; +const uint8_t kRFCv1RetryIntegrityNonce[] = { + 0x46, 0x15, 0x99, 0xd3, 0x5d, 0x63, 0x2b, 0xf2, 0x23, 0x98, 0x25, 0xbb}; // Keys used by Google versions of QUIC. When introducing a new version, // generate a new key by running `openssl rand -hex 16`. @@ -205,12 +216,20 @@ bool RetryIntegrityKeysForVersion(const ParsedQuicVersion& version, absl::string_view* key, absl::string_view* nonce) { - static_assert(SupportedVersions().size() == 5u, + static_assert(SupportedVersions().size() == 6u, "Supported versions out of sync with retry integrity keys"); if (!version.UsesTls()) { QUIC_BUG << "Attempted to get retry integrity keys for invalid version " << version; return false; + } else if (version == ParsedQuicVersion::RFCv1()) { + *key = absl::string_view( + reinterpret_cast<const char*>(kRFCv1RetryIntegrityKey), + ABSL_ARRAYSIZE(kRFCv1RetryIntegrityKey)); + *nonce = absl::string_view( + reinterpret_cast<const char*>(kRFCv1RetryIntegrityNonce), + ABSL_ARRAYSIZE(kRFCv1RetryIntegrityNonce)); + return true; } else if (version == ParsedQuicVersion::Draft29()) { *key = absl::string_view( reinterpret_cast<const char*>(kDraft29RetryIntegrityKey),
diff --git a/quic/core/http/http_decoder.cc b/quic/core/http/http_decoder.cc index 590f2fc..dc67302 100644 --- a/quic/core/http/http_decoder.cc +++ b/quic/core/http/http_decoder.cc
@@ -238,9 +238,20 @@ QUIC_CODE_COUNT_N(quic_new_priority_update_frame, 2, 2); continue_processing = visitor_->OnPriorityUpdateFrameStart(header_length); - break; + } else { + continue_processing = visitor_->OnUnknownFrameStart( + current_frame_type_, header_length, current_frame_length_); } - ABSL_FALLTHROUGH_INTENDED; + break; + case static_cast<uint64_t>(HttpFrameType::ACCEPT_CH): + if (GetQuicReloadableFlag(quic_parse_accept_ch_frame)) { + QUIC_RELOADABLE_FLAG_COUNT(quic_parse_accept_ch_frame); + continue_processing = visitor_->OnAcceptChFrameStart(header_length); + } else { + continue_processing = visitor_->OnUnknownFrameStart( + current_frame_type_, header_length, current_frame_length_); + } + break; default: continue_processing = visitor_->OnUnknownFrameStart( current_frame_type_, header_length, current_frame_length_); @@ -374,19 +385,23 @@ // TODO(bnc): Avoid buffering if the entire frame is present, and // instead parse directly out of |reader|. BufferFramePayload(reader); - break; + } else { + continue_processing = HandleUnknownFramePayload(reader); } - ABSL_FALLTHROUGH_INTENDED; + break; + } + case static_cast<uint64_t>(HttpFrameType::ACCEPT_CH): { + if (GetQuicReloadableFlag(quic_parse_accept_ch_frame)) { + // TODO(bnc): Avoid buffering if the entire frame is present, and + // instead parse directly out of |reader|. + BufferFramePayload(reader); + } else { + continue_processing = HandleUnknownFramePayload(reader); + } + break; } default: { - QuicByteCount bytes_to_read = std::min<QuicByteCount>( - remaining_frame_length_, reader->BytesRemaining()); - absl::string_view payload; - bool success = reader->ReadStringPiece(&payload, bytes_to_read); - DCHECK(success); - DCHECK(!payload.empty()); - continue_processing = visitor_->OnUnknownFramePayload(payload); - remaining_frame_length_ -= payload.length(); + continue_processing = HandleUnknownFramePayload(reader); break; } } @@ -492,14 +507,28 @@ return false; } continue_processing = visitor_->OnPriorityUpdateFrame(frame); - break; + } else { + continue_processing = visitor_->OnUnknownFrameEnd(); } - ABSL_FALLTHROUGH_INTENDED; - } - default: { - continue_processing = visitor_->OnUnknownFrameEnd(); break; } + case static_cast<uint64_t>(HttpFrameType::ACCEPT_CH): { + if (GetQuicReloadableFlag(quic_parse_accept_ch_frame)) { + // TODO(bnc): Avoid buffering if the entire frame is present, and + // instead parse directly out of |reader|. + AcceptChFrame frame; + QuicDataReader reader(buffer_.data(), current_frame_length_); + if (!ParseAcceptChFrame(&reader, &frame)) { + return false; + } + continue_processing = visitor_->OnAcceptChFrame(frame); + } else { + continue_processing = visitor_->OnUnknownFrameEnd(); + } + break; + } + default: + continue_processing = visitor_->OnUnknownFrameEnd(); } current_length_field_length_ = 0; @@ -508,6 +537,17 @@ return continue_processing; } +bool HttpDecoder::HandleUnknownFramePayload(QuicDataReader* reader) { + QuicByteCount bytes_to_read = std::min<QuicByteCount>( + remaining_frame_length_, reader->BytesRemaining()); + absl::string_view payload; + bool success = reader->ReadStringPiece(&payload, bytes_to_read); + DCHECK(success); + DCHECK(!payload.empty()); + remaining_frame_length_ -= payload.length(); + return visitor_->OnUnknownFramePayload(payload); +} + void HttpDecoder::DiscardFramePayload(QuicDataReader* reader) { QuicByteCount bytes_to_read = std::min<QuicByteCount>( remaining_frame_length_, reader->BytesRemaining()); @@ -647,6 +687,26 @@ return true; } +bool HttpDecoder::ParseAcceptChFrame(QuicDataReader* reader, + AcceptChFrame* frame) { + absl::string_view origin; + absl::string_view value; + while (!reader->IsDoneReading()) { + if (!reader->ReadStringPieceVarInt62(&origin)) { + RaiseError(QUIC_HTTP_FRAME_ERROR, "Unable to read ACCEPT_CH origin."); + return false; + } + if (!reader->ReadStringPieceVarInt62(&value)) { + RaiseError(QUIC_HTTP_FRAME_ERROR, "Unable to read ACCEPT_CH value."); + return false; + } + // Copy data. + frame->entries.push_back({std::string(origin.data(), origin.size()), + std::string(value.data(), value.size())}); + } + return true; +} + QuicByteCount HttpDecoder::MaxFrameLength(uint64_t frame_type) { switch (frame_type) { case static_cast<uint64_t>(HttpFrameType::CANCEL_PUSH): @@ -664,6 +724,9 @@ case static_cast<uint64_t>(HttpFrameType::PRIORITY_UPDATE_REQUEST_STREAM): // This limit is arbitrary. return 1024 * 1024; + case static_cast<uint64_t>(HttpFrameType::ACCEPT_CH): + // This limit is arbitrary. + return 1024 * 1024; default: // Other frames require no data buffering, so it's safe to have no limit. return std::numeric_limits<QuicByteCount>::max();
diff --git a/quic/core/http/http_decoder.h b/quic/core/http/http_decoder.h index 479f444..86f3f58 100644 --- a/quic/core/http/http_decoder.h +++ b/quic/core/http/http_decoder.h
@@ -102,6 +102,13 @@ // Called when a PRIORITY_UPDATE frame has been successfully parsed. virtual bool OnPriorityUpdateFrame(const PriorityUpdateFrame& frame) = 0; + // Called when an ACCEPT_CH frame has been received. + // |header_length| contains ACCEPT_CH frame length and payload length. + virtual bool OnAcceptChFrameStart(QuicByteCount header_length) = 0; + + // Called when an ACCEPT_CH frame has been successfully parsed. + virtual bool OnAcceptChFrame(const AcceptChFrame& frame) = 0; + // Called when a frame of unknown type |frame_type| has been received. // Frame type might be reserved, Visitor must make sure to ignore. // |header_length| and |payload_length| are the length of the frame header @@ -174,6 +181,11 @@ // had been parsed completely. Returns whether processing should continue. bool FinishParsing(); + // Read payload of unknown frame from |reader| and call + // Visitor::OnUnknownFramePayload(). Returns true decoding should continue, + // false if it should be paused. + bool HandleUnknownFramePayload(QuicDataReader* reader); + // Discards any remaining frame payload from |reader|. void DiscardFramePayload(QuicDataReader* reader); @@ -207,6 +219,9 @@ bool ParseNewPriorityUpdateFrame(QuicDataReader* reader, PriorityUpdateFrame* frame); + // Parses the payload of an ACCEPT_CH frame from |reader| into |frame|. + bool ParseAcceptChFrame(QuicDataReader* reader, AcceptChFrame* frame); + // Returns the max frame size of a given |frame_type|. QuicByteCount MaxFrameLength(uint64_t frame_type);
diff --git a/quic/core/http/http_decoder_test.cc b/quic/core/http/http_decoder_test.cc index 68f9d7f..68248ea 100644 --- a/quic/core/http/http_decoder_test.cc +++ b/quic/core/http/http_decoder_test.cc
@@ -21,6 +21,7 @@ #include "net/third_party/quiche/src/common/platform/api/quiche_text_utils.h" using ::testing::_; +using ::testing::AnyNumber; using ::testing::Eq; using ::testing::InSequence; using ::testing::Return; @@ -104,6 +105,12 @@ (override)); MOCK_METHOD(bool, + OnAcceptChFrameStart, + (QuicByteCount header_length), + (override)); + MOCK_METHOD(bool, OnAcceptChFrame, (const AcceptChFrame& frame), (override)); + + MOCK_METHOD(bool, OnUnknownFrameStart, (uint64_t frame_type, QuicByteCount header_length, @@ -138,6 +145,8 @@ ON_CALL(visitor_, OnPriorityUpdateFrameStart(_)) .WillByDefault(Return(true)); ON_CALL(visitor_, OnPriorityUpdateFrame(_)).WillByDefault(Return(true)); + ON_CALL(visitor_, OnAcceptChFrameStart(_)).WillByDefault(Return(true)); + ON_CALL(visitor_, OnAcceptChFrame(_)).WillByDefault(Return(true)); ON_CALL(visitor_, OnUnknownFrameStart(_, _, _)).WillByDefault(Return(true)); ON_CALL(visitor_, OnUnknownFramePayload(_)).WillByDefault(Return(true)); ON_CALL(visitor_, OnUnknownFrameEnd()).WillByDefault(Return(true)); @@ -1192,6 +1201,153 @@ } } +TEST_F(HttpDecoderTest, AcceptChFrame) { + if (!GetQuicReloadableFlag(quic_parse_accept_ch_frame)) { + return; + } + + InSequence s; + std::string input1 = absl::HexStringToBytes( + "4089" // type (ACCEPT_CH) + "00"); // length + + AcceptChFrame accept_ch1; + + // Visitor pauses processing. + EXPECT_CALL(visitor_, OnAcceptChFrameStart(3)).WillOnce(Return(false)); + absl::string_view remaining_input(input1); + QuicByteCount processed_bytes = + ProcessInputWithGarbageAppended(remaining_input); + EXPECT_EQ(3u, processed_bytes); + remaining_input = remaining_input.substr(processed_bytes); + + EXPECT_CALL(visitor_, OnAcceptChFrame(accept_ch1)).WillOnce(Return(false)); + processed_bytes = ProcessInputWithGarbageAppended(remaining_input); + EXPECT_EQ(remaining_input.size(), processed_bytes); + EXPECT_THAT(decoder_.error(), IsQuicNoError()); + EXPECT_EQ("", decoder_.error_detail()); + + // Process the full frame. + EXPECT_CALL(visitor_, OnAcceptChFrameStart(3)); + EXPECT_CALL(visitor_, OnAcceptChFrame(accept_ch1)); + EXPECT_EQ(input1.size(), ProcessInput(input1)); + EXPECT_THAT(decoder_.error(), IsQuicNoError()); + EXPECT_EQ("", decoder_.error_detail()); + + // Process the frame incrementally. + EXPECT_CALL(visitor_, OnAcceptChFrameStart(3)); + EXPECT_CALL(visitor_, OnAcceptChFrame(accept_ch1)); + ProcessInputCharByChar(input1); + EXPECT_THAT(decoder_.error(), IsQuicNoError()); + EXPECT_EQ("", decoder_.error_detail()); + + std::string input2 = absl::HexStringToBytes( + "4089" // type (ACCEPT_CH) + "08" // length + "03" // length of origin + "666f6f" // origin "foo" + "03" // length of value + "626172"); // value "bar" + + AcceptChFrame accept_ch2; + accept_ch2.entries.push_back({"foo", "bar"}); + + // Visitor pauses processing. + EXPECT_CALL(visitor_, OnAcceptChFrameStart(3)).WillOnce(Return(false)); + remaining_input = input2; + processed_bytes = ProcessInputWithGarbageAppended(remaining_input); + EXPECT_EQ(3u, processed_bytes); + remaining_input = remaining_input.substr(processed_bytes); + + EXPECT_CALL(visitor_, OnAcceptChFrame(accept_ch2)).WillOnce(Return(false)); + processed_bytes = ProcessInputWithGarbageAppended(remaining_input); + EXPECT_EQ(remaining_input.size(), processed_bytes); + EXPECT_THAT(decoder_.error(), IsQuicNoError()); + EXPECT_EQ("", decoder_.error_detail()); + + // Process the full frame. + EXPECT_CALL(visitor_, OnAcceptChFrameStart(3)); + EXPECT_CALL(visitor_, OnAcceptChFrame(accept_ch2)); + EXPECT_EQ(input2.size(), ProcessInput(input2)); + EXPECT_THAT(decoder_.error(), IsQuicNoError()); + EXPECT_EQ("", decoder_.error_detail()); + + // Process the frame incrementally. + EXPECT_CALL(visitor_, OnAcceptChFrameStart(3)); + EXPECT_CALL(visitor_, OnAcceptChFrame(accept_ch2)); + ProcessInputCharByChar(input2); + EXPECT_THAT(decoder_.error(), IsQuicNoError()); + EXPECT_EQ("", decoder_.error_detail()); +} + +TEST_F(HttpDecoderTest, CorruptAcceptChFrame) { + if (!GetQuicReloadableFlag(quic_parse_accept_ch_frame)) { + // TODO(bnc): merge this test into HttpDecoderTest.CorruptFrame when + // deprecating flag. + return; + } + + EXPECT_CALL(visitor_, OnAcceptChFrameStart(_)).Times(AnyNumber()); + + struct { + const char* const input; + const char* const error_message; + } kTestData[] = {{"\x40\x89" // type (ACCEPT_CH) + "\x01" // length + "\x40", // first byte of two-byte varint origin length + "Unable to read ACCEPT_CH origin."}, + {"\x40\x89" // type (ACCEPT_CH) + "\x01" // length + "\x05", // valid origin length but no origin string + "Unable to read ACCEPT_CH origin."}, + {"\x40\x89" // type (ACCEPT_CH) + "\x04" // length + "\x05" // valid origin length + "foo", // payload ends before origin ends + "Unable to read ACCEPT_CH origin."}, + {"\x40\x89" // type (ACCEPT_CH) + "\x04" // length + "\x03" // valid origin length + "foo", // payload ends at end of origin: no value + "Unable to read ACCEPT_CH value."}, + {"\x40\x89" // type (ACCEPT_CH) + "\x05" // length + "\x03" // valid origin length + "foo" // payload ends at end of origin: no value + "\x40", // first byte of two-byte varint value length + "Unable to read ACCEPT_CH value."}, + {"\x40\x89" // type (ACCEPT_CH) + "\x08" // length + "\x03" // valid origin length + "foo" // origin + "\x05" // valid value length + "bar", // payload ends before value ends + "Unable to read ACCEPT_CH value."}}; + + for (const auto& test_data : kTestData) { + { + HttpDecoder decoder(&visitor_); + EXPECT_CALL(visitor_, OnError(&decoder)); + + absl::string_view input(test_data.input); + decoder.ProcessInput(input.data(), input.size()); + EXPECT_THAT(decoder.error(), IsError(QUIC_HTTP_FRAME_ERROR)); + EXPECT_EQ(test_data.error_message, decoder.error_detail()); + } + { + HttpDecoder decoder(&visitor_); + EXPECT_CALL(visitor_, OnError(&decoder)); + + absl::string_view input(test_data.input); + for (auto c : input) { + decoder.ProcessInput(&c, 1); + } + EXPECT_THAT(decoder.error(), IsError(QUIC_HTTP_FRAME_ERROR)); + EXPECT_EQ(test_data.error_message, decoder.error_detail()); + } + } +} + TEST_F(HttpDecoderTest, DecodeSettings) { std::string input = absl::HexStringToBytes( "04" // type (SETTINGS)
diff --git a/quic/core/http/quic_receive_control_stream.cc b/quic/core/http/quic_receive_control_stream.cc index cc6857f..17c588d 100644 --- a/quic/core/http/quic_receive_control_stream.cc +++ b/quic/core/http/quic_receive_control_stream.cc
@@ -252,6 +252,34 @@ return true; } +bool QuicReceiveControlStream::OnAcceptChFrameStart( + QuicByteCount /* header_length */) { + if (!settings_frame_received_) { + stream_delegate()->OnStreamError( + QUIC_HTTP_MISSING_SETTINGS_FRAME, + "ACCEPT_CH frame received before SETTINGS."); + return false; + } + + if (spdy_session()->perspective() == Perspective::IS_SERVER) { + OnWrongFrame("ACCEPT_CH"); + return false; + } + + return true; +} + +bool QuicReceiveControlStream::OnAcceptChFrame(const AcceptChFrame& frame) { + DCHECK_EQ(Perspective::IS_CLIENT, spdy_session()->perspective()); + + if (spdy_session()->debug_visitor()) { + spdy_session()->debug_visitor()->OnAcceptChFrameReceived(frame); + } + + spdy_session()->OnAcceptChFrame(frame); + return true; +} + bool QuicReceiveControlStream::OnUnknownFrameStart( uint64_t frame_type, QuicByteCount /*header_length*/,
diff --git a/quic/core/http/quic_receive_control_stream.h b/quic/core/http/quic_receive_control_stream.h index 2654bb8..728aa31 100644 --- a/quic/core/http/quic_receive_control_stream.h +++ b/quic/core/http/quic_receive_control_stream.h
@@ -56,6 +56,8 @@ bool OnPushPromiseFrameEnd() override; bool OnPriorityUpdateFrameStart(QuicByteCount header_length) override; bool OnPriorityUpdateFrame(const PriorityUpdateFrame& frame) override; + bool OnAcceptChFrameStart(QuicByteCount header_length) override; + bool OnAcceptChFrame(const AcceptChFrame& frame) override; bool OnUnknownFrameStart(uint64_t frame_type, QuicByteCount header_length, QuicByteCount payload_length) override;
diff --git a/quic/core/http/quic_receive_control_stream_test.cc b/quic/core/http/quic_receive_control_stream_test.cc index 51948e6..949a0f4 100644 --- a/quic/core/http/quic_receive_control_stream_test.cc +++ b/quic/core/http/quic_receive_control_stream_test.cc
@@ -396,6 +396,70 @@ /* offset = */ 1, cancel_push_frame)); } +TEST_P(QuicReceiveControlStreamTest, AcceptChFrameBeforeSettings) { + std::string accept_ch_frame = absl::HexStringToBytes( + "4089" // type (ACCEPT_CH) + "00"); // length + + EXPECT_CALL(*connection_, + CloseConnection(QUIC_HTTP_MISSING_SETTINGS_FRAME, + GetQuicReloadableFlag(quic_parse_accept_ch_frame) + ? "ACCEPT_CH frame received before SETTINGS." + : "Unknown frame received before SETTINGS.", + _)) + .WillOnce( + Invoke(connection_, &MockQuicConnection::ReallyCloseConnection)); + EXPECT_CALL(*connection_, SendConnectionClosePacket(_, _)); + EXPECT_CALL(session_, OnConnectionClosed(_, _)); + + receive_control_stream_->OnStreamFrame( + QuicStreamFrame(receive_control_stream_->id(), /* fin = */ false, + /* offset = */ 1, accept_ch_frame)); +} + +TEST_P(QuicReceiveControlStreamTest, ReceiveAcceptChFrame) { + StrictMock<MockHttp3DebugVisitor> debug_visitor; + session_.set_debug_visitor(&debug_visitor); + + const QuicStreamId id = receive_control_stream_->id(); + QuicStreamOffset offset = 1; + + // 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, settings_frame)); + offset += settings_frame.length(); + + // Receive ACCEPT_CH frame. + std::string accept_ch_frame = absl::HexStringToBytes( + "4089" // type (ACCEPT_CH) + "00"); // length + + if (GetQuicReloadableFlag(quic_parse_accept_ch_frame)) { + if (perspective() == Perspective::IS_CLIENT) { + EXPECT_CALL(debug_visitor, OnAcceptChFrameReceived(_)); + } else { + EXPECT_CALL( + *connection_, + CloseConnection(QUIC_HTTP_FRAME_UNEXPECTED_ON_CONTROL_STREAM, + "ACCEPT_CH frame received on control stream", _)) + .WillOnce( + Invoke(connection_, &MockQuicConnection::ReallyCloseConnection)); + EXPECT_CALL(*connection_, SendConnectionClosePacket(_, _)); + EXPECT_CALL(session_, OnConnectionClosed(_, _)); + } + } else { + EXPECT_CALL(debug_visitor, + OnUnknownFrameReceived(id, /* frame_type = */ 0x89, + /* payload_length = */ 0)); + } + + receive_control_stream_->OnStreamFrame( + QuicStreamFrame(id, /* fin = */ false, offset, accept_ch_frame)); +} + TEST_P(QuicReceiveControlStreamTest, UnknownFrameBeforeSettings) { std::string unknown_frame = absl::HexStringToBytes( "21" // reserved frame type
diff --git a/quic/core/http/quic_spdy_session.h b/quic/core/http/quic_spdy_session.h index 4ab4cfd..a90befa 100644 --- a/quic/core/http/quic_spdy_session.h +++ b/quic/core/http/quic_spdy_session.h
@@ -90,6 +90,7 @@ virtual void OnMaxPushIdFrameReceived(const MaxPushIdFrame& /*frame*/) {} virtual void OnPriorityUpdateFrameReceived( const PriorityUpdateFrame& /*frame*/) {} + virtual void OnAcceptChFrameReceived(const AcceptChFrame& /*frame*/) {} // Incoming HTTP/3 frames on request or push streams. virtual void OnDataFrameReceived(QuicStreamId /*stream_id*/, @@ -196,6 +197,10 @@ // stream. Returns false and closes connection if |push_id| is invalid. bool OnPriorityUpdateForPushStream(QuicStreamId push_id, int urgency); + // Called when an HTTP/3 ACCEPT_CH frame has been received. + // This method will only be called for client sessions. + virtual void OnAcceptChFrame(const AcceptChFrame& /*frame*/) {} + // Sends contents of |iov| to h2_deframer_, returns number of bytes processed. size_t ProcessHeaderData(const struct iovec& iov);
diff --git a/quic/core/http/quic_spdy_session_test.cc b/quic/core/http/quic_spdy_session_test.cc index 3606949..e4b5dc0 100644 --- a/quic/core/http/quic_spdy_session_test.cc +++ b/quic/core/http/quic_spdy_session_test.cc
@@ -351,6 +351,8 @@ GetEncryptionLevelToSendApplicationData()); } + MOCK_METHOD(void, OnAcceptChFrame, (const AcceptChFrame&), (override)); + using QuicSession::closed_streams; using QuicSession::ShouldKeepConnectionAlive; using QuicSpdySession::ProcessPendingStream; @@ -3260,6 +3262,57 @@ session_.OnSettingsFrame(frame); } +TEST_P(QuicSpdySessionTestClient, ReceiveAcceptChFrame) { + if (!VersionUsesHttp3(transport_version())) { + return; + } + + if (!GetQuicReloadableFlag(quic_parse_accept_ch_frame)) { + return; + } + + StrictMock<MockHttp3DebugVisitor> debug_visitor; + session_.set_debug_visitor(&debug_visitor); + + // Create control stream. + QuicStreamId receive_control_stream_id = + GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 3); + char type[] = {kControlStream}; + absl::string_view stream_type(type, 1); + QuicStreamOffset offset = 0; + 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()); + + // First frame has to be SETTINGS. + std::string serialized_settings = EncodeSettings({}); + QuicStreamFrame data2(receive_control_stream_id, /* fin = */ false, offset, + serialized_settings); + offset += serialized_settings.length(); + EXPECT_CALL(debug_visitor, OnSettingsFrameReceived(_)); + + session_.OnStreamFrame(data2); + + // Receive ACCEPT_CH frame. + AcceptChFrame accept_ch; + accept_ch.entries.push_back({"foo", "bar"}); + std::unique_ptr<char[]> buffer; + auto frame_length = HttpEncoder::SerializeAcceptChFrame(accept_ch, &buffer); + QuicStreamFrame data3(receive_control_stream_id, /* fin = */ false, offset, + absl::string_view(buffer.get(), frame_length)); + + EXPECT_CALL(debug_visitor, OnAcceptChFrameReceived(accept_ch)); + EXPECT_CALL(session_, OnAcceptChFrame(accept_ch)); + + session_.OnStreamFrame(data3); +} + } // namespace } // namespace test } // namespace quic
diff --git a/quic/core/http/quic_spdy_stream.cc b/quic/core/http/quic_spdy_stream.cc index 62a81ae..8489954 100644 --- a/quic/core/http/quic_spdy_stream.cc +++ b/quic/core/http/quic_spdy_stream.cc
@@ -154,6 +154,16 @@ return false; } + bool OnAcceptChFrameStart(QuicByteCount /*header_length*/) override { + CloseConnectionOnWrongFrame("ACCEPT_CH"); + return false; + } + + bool OnAcceptChFrame(const AcceptChFrame& /*frame*/) override { + CloseConnectionOnWrongFrame("ACCEPT_CH"); + return false; + } + bool OnUnknownFrameStart(uint64_t frame_type, QuicByteCount header_length, QuicByteCount payload_length) override {
diff --git a/quic/core/quic_connection_test.cc b/quic/core/quic_connection_test.cc index 5d26ab0..fb58ae8 100644 --- a/quic/core/quic_connection_test.cc +++ b/quic/core/quic_connection_test.cc
@@ -9821,6 +9821,10 @@ } // These values come from draft-ietf-quic-tls Appendix A.4. + char retry_packet_rfcv1[] = { + 0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0xf0, 0x67, 0xa5, 0x50, 0x2a, + 0x42, 0x62, 0xb5, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x04, 0xa2, 0x65, 0xba, + 0x2e, 0xff, 0x4d, 0x82, 0x90, 0x58, 0xfb, 0x3f, 0x0f, 0x24, 0x96, 0xba}; char retry_packet29[] = { 0xff, 0xff, 0x00, 0x00, 0x1d, 0x00, 0x08, 0xf0, 0x67, 0xa5, 0x50, 0x2a, 0x42, 0x62, 0xb5, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0xd1, 0x69, 0x26, 0xd8, @@ -9828,7 +9832,10 @@ char* retry_packet; size_t retry_packet_length; - if (version() == ParsedQuicVersion::Draft29()) { + if (version() == ParsedQuicVersion::RFCv1()) { + retry_packet = retry_packet_rfcv1; + retry_packet_length = ABSL_ARRAYSIZE(retry_packet_rfcv1); + } else if (version() == ParsedQuicVersion::Draft29()) { retry_packet = retry_packet29; retry_packet_length = ABSL_ARRAYSIZE(retry_packet29); } else {
diff --git a/quic/core/quic_dispatcher_test.cc b/quic/core/quic_dispatcher_test.cc index d65159f..5810c42 100644 --- a/quic/core/quic_dispatcher_test.cc +++ b/quic/core/quic_dispatcher_test.cc
@@ -1285,7 +1285,7 @@ received_packet44); } -static_assert(quic::SupportedVersions().size() == 5u, +static_assert(quic::SupportedVersions().size() == 6u, "Please add new RejectDeprecatedVersion tests above this assert " "when deprecating versions");
diff --git a/quic/core/quic_flags_list.h b/quic/core/quic_flags_list.h index 28082aa..fcacf5d 100644 --- a/quic/core/quic_flags_list.h +++ b/quic/core/quic_flags_list.h
@@ -37,6 +37,7 @@ QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_enable_mtu_discovery_at_server, false) QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_enable_server_on_wire_ping, false) QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_enable_token_based_address_validation, false) +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_enable_version_rfcv1, false) QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_encrypted_control_frames, false) QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_encrypted_goaway, false) QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_extract_x509_subject_using_certificate_view, true) @@ -44,6 +45,7 @@ QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_goaway_with_max_stream_id, false) QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_granular_qpack_error_codes, true) QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_new_priority_update_frame, true) +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_parse_accept_ch_frame, true) QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_pass_path_response_to_validator, false) QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_require_handshake_confirmation, false) QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_round_up_tiny_bandwidth, true)
diff --git a/quic/core/quic_trace_visitor_test.cc b/quic/core/quic_trace_visitor_test.cc index 07bc4f2..9a63d45 100644 --- a/quic/core/quic_trace_visitor_test.cc +++ b/quic/core/quic_trace_visitor_test.cc
@@ -81,7 +81,6 @@ TEST_F(QuicTraceVisitorTest, Version) { std::string version = trace_.protocol_version(); ASSERT_EQ(4u, version.size()); - EXPECT_NE(0, version[0]); } // Check that basic metadata about sent packets is recorded.
diff --git a/quic/core/quic_version_manager.cc b/quic/core/quic_version_manager.cc index 946e152..c80b95b 100644 --- a/quic/core/quic_version_manager.cc +++ b/quic/core/quic_version_manager.cc
@@ -15,14 +15,15 @@ QuicVersionManager::QuicVersionManager( ParsedQuicVersionVector supported_versions) - : disable_version_draft_29_( + : enable_version_rfcv1_(GetQuicReloadableFlag(quic_enable_version_rfcv1)), + disable_version_draft_29_( GetQuicReloadableFlag(quic_disable_version_draft_29)), disable_version_t051_(GetQuicReloadableFlag(quic_disable_version_t051)), disable_version_q050_(GetQuicReloadableFlag(quic_disable_version_q050)), disable_version_q046_(GetQuicReloadableFlag(quic_disable_version_q046)), disable_version_q043_(GetQuicReloadableFlag(quic_disable_version_q043)), allowed_supported_versions_(std::move(supported_versions)) { - static_assert(SupportedVersions().size() == 5u, + static_assert(SupportedVersions().size() == 6u, "Supported versions out of sync"); RefilterSupportedVersions(); } @@ -46,9 +47,11 @@ } void QuicVersionManager::MaybeRefilterSupportedVersions() { - static_assert(SupportedVersions().size() == 5u, + static_assert(SupportedVersions().size() == 6u, "Supported versions out of sync"); - if (disable_version_draft_29_ != + if (enable_version_rfcv1_ != + GetQuicReloadableFlag(quic_enable_version_rfcv1) || + disable_version_draft_29_ != GetQuicReloadableFlag(quic_disable_version_draft_29) || disable_version_t051_ != GetQuicReloadableFlag(quic_disable_version_t051) || @@ -58,6 +61,7 @@ GetQuicReloadableFlag(quic_disable_version_q046) || disable_version_q043_ != GetQuicReloadableFlag(quic_disable_version_q043)) { + enable_version_rfcv1_ = GetQuicReloadableFlag(quic_enable_version_rfcv1); disable_version_draft_29_ = GetQuicReloadableFlag(quic_disable_version_draft_29); disable_version_t051_ = GetQuicReloadableFlag(quic_disable_version_t051);
diff --git a/quic/core/quic_version_manager.h b/quic/core/quic_version_manager.h index d38019c..b5712c6 100644 --- a/quic/core/quic_version_manager.h +++ b/quic/core/quic_version_manager.h
@@ -50,6 +50,8 @@ private: // Cached value of reloadable flags. + // quic_enable_version_rfcv1 flag + bool enable_version_rfcv1_; // quic_disable_version_draft_29 flag bool disable_version_draft_29_; // quic_disable_version_t051 flag
diff --git a/quic/core/quic_version_manager_test.cc b/quic/core/quic_version_manager_test.cc index b1a67ee..56af975 100644 --- a/quic/core/quic_version_manager_test.cc +++ b/quic/core/quic_version_manager_test.cc
@@ -18,11 +18,12 @@ class QuicVersionManagerTest : public QuicTest {}; TEST_F(QuicVersionManagerTest, QuicVersionManager) { - static_assert(SupportedVersions().size() == 5u, + static_assert(SupportedVersions().size() == 6u, "Supported versions out of sync"); for (const ParsedQuicVersion& version : AllSupportedVersions()) { QuicEnableVersion(version); } + QuicDisableVersion(ParsedQuicVersion::RFCv1()); QuicDisableVersion(ParsedQuicVersion::Draft29()); QuicDisableVersion(ParsedQuicVersion::T051()); QuicVersionManager manager(AllSupportedVersions());
diff --git a/quic/core/quic_versions.cc b/quic/core/quic_versions.cc index 27d56c9..46a092e 100644 --- a/quic/core/quic_versions.cc +++ b/quic/core/quic_versions.cc
@@ -42,11 +42,13 @@ } void SetVersionFlag(const ParsedQuicVersion& version, bool should_enable) { - static_assert(SupportedVersions().size() == 5u, + static_assert(SupportedVersions().size() == 6u, "Supported versions out of sync"); const bool enable = should_enable; const bool disable = !should_enable; - if (version == ParsedQuicVersion::Draft29()) { + if (version == ParsedQuicVersion::RFCv1()) { + SetQuicReloadableFlag(quic_enable_version_rfcv1, enable); + } else if (version == ParsedQuicVersion::Draft29()) { SetQuicReloadableFlag(quic_disable_version_draft_29, disable); } else if (version == ParsedQuicVersion::T051()) { SetQuicReloadableFlag(quic_disable_version_t051, disable); @@ -167,6 +169,11 @@ return VersionHasIetfQuicFrames(transport_version); } +bool ParsedQuicVersion::UsesLegacyTlsExtension() const { + DCHECK(IsKnown()); + return transport_version <= QUIC_VERSION_IETF_DRAFT_29; +} + bool ParsedQuicVersion::UsesTls() const { DCHECK(IsKnown()); return handshake_protocol == PROTOCOL_TLS1_3; @@ -208,9 +215,11 @@ } QuicVersionLabel CreateQuicVersionLabel(ParsedQuicVersion parsed_version) { - static_assert(SupportedVersions().size() == 5u, + static_assert(SupportedVersions().size() == 6u, "Supported versions out of sync"); - if (parsed_version == ParsedQuicVersion::Draft29()) { + if (parsed_version == ParsedQuicVersion::RFCv1()) { + return MakeVersionLabel(0x00, 0x00, 0x00, 0x01); + } else if (parsed_version == ParsedQuicVersion::Draft29()) { return MakeVersionLabel(0xff, 0x00, 0x00, 29); } else if (parsed_version == ParsedQuicVersion::T051()) { return MakeVersionLabel('T', '0', '5', '1'); @@ -380,7 +389,11 @@ ParsedQuicVersionVector filtered_versions; filtered_versions.reserve(versions.size()); for (const ParsedQuicVersion& version : versions) { - if (version == ParsedQuicVersion::Draft29()) { + if (version == ParsedQuicVersion::RFCv1()) { + if (GetQuicReloadableFlag(quic_enable_version_rfcv1)) { + filtered_versions.push_back(version); + } + } else if (version == ParsedQuicVersion::Draft29()) { if (!GetQuicReloadableFlag(quic_disable_version_draft_29)) { filtered_versions.push_back(version); } @@ -455,6 +468,7 @@ RETURN_STRING_LITERAL(QUIC_VERSION_50); RETURN_STRING_LITERAL(QUIC_VERSION_51); RETURN_STRING_LITERAL(QUIC_VERSION_IETF_DRAFT_29); + RETURN_STRING_LITERAL(QUIC_VERSION_IETF_RFC_V1); RETURN_STRING_LITERAL(QUIC_VERSION_UNSUPPORTED); RETURN_STRING_LITERAL(QUIC_VERSION_RESERVED_FOR_NEGOTIATION); } @@ -473,12 +487,14 @@ } std::string ParsedQuicVersionToString(ParsedQuicVersion version) { - static_assert(SupportedVersions().size() == 5u, + static_assert(SupportedVersions().size() == 6u, "Supported versions out of sync"); if (version == UnsupportedQuicVersion()) { return "0"; - } - if (version == ParsedQuicVersion::Draft29()) { + } else if (version == ParsedQuicVersion::RFCv1()) { + DCHECK(version.UsesHttp3()); + return "RFCv1"; + } else if (version == ParsedQuicVersion::Draft29()) { DCHECK(version.UsesHttp3()); return "draft29"; } @@ -567,7 +583,9 @@ } std::string AlpnForVersion(ParsedQuicVersion parsed_version) { - if (parsed_version == ParsedQuicVersion::Draft29()) { + if (parsed_version == ParsedQuicVersion::RFCv1()) { + return "h3"; + } else if (parsed_version == ParsedQuicVersion::Draft29()) { return "h3-29"; } return "h3-" + ParsedQuicVersionToString(parsed_version);
diff --git a/quic/core/quic_versions.h b/quic/core/quic_versions.h index 3257cc5..9bb58a5 100644 --- a/quic/core/quic_versions.h +++ b/quic/core/quic_versions.h
@@ -123,6 +123,7 @@ // Number 71 used to represent draft-ietf-quic-transport-27. // Number 72 used to represent draft-ietf-quic-transport-28. QUIC_VERSION_IETF_DRAFT_29 = 73, // draft-ietf-quic-transport-29. + QUIC_VERSION_IETF_RFC_V1 = 80, // Not-yet-published RFC. // Version 99 was a dumping ground for IETF QUIC changes which were not yet // yet ready for production between 2018-02 and 2020-02. @@ -170,6 +171,7 @@ QuicTransportVersion transport_version) { bool transport_version_is_valid = false; constexpr QuicTransportVersion valid_transport_versions[] = { + QUIC_VERSION_IETF_RFC_V1, QUIC_VERSION_IETF_DRAFT_29, QUIC_VERSION_51, QUIC_VERSION_50, @@ -194,7 +196,8 @@ return transport_version != QUIC_VERSION_UNSUPPORTED && transport_version != QUIC_VERSION_RESERVED_FOR_NEGOTIATION && transport_version != QUIC_VERSION_51 && - transport_version != QUIC_VERSION_IETF_DRAFT_29; + transport_version != QUIC_VERSION_IETF_DRAFT_29 && + transport_version != QUIC_VERSION_IETF_RFC_V1; case PROTOCOL_TLS1_3: return transport_version != QUIC_VERSION_UNSUPPORTED && transport_version != QUIC_VERSION_50 && @@ -243,6 +246,10 @@ transport_version != other.transport_version; } + static constexpr ParsedQuicVersion RFCv1() { + return ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_IETF_RFC_V1); + } + static constexpr ParsedQuicVersion Draft29() { return ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_IETF_DRAFT_29); } @@ -351,6 +358,9 @@ // frames or not. bool HasIetfQuicFrames() const; + // Returns whether this version uses the legacy TLS extension codepoint. + bool UsesLegacyTlsExtension() const; + // Returns whether this version uses PROTOCOL_TLS1_3. bool UsesTls() const; @@ -389,11 +399,11 @@ return {PROTOCOL_TLS1_3, PROTOCOL_QUIC_CRYPTO}; } -constexpr std::array<ParsedQuicVersion, 5> SupportedVersions() { +constexpr std::array<ParsedQuicVersion, 6> SupportedVersions() { return { - ParsedQuicVersion::Draft29(), ParsedQuicVersion::T051(), - ParsedQuicVersion::Q050(), ParsedQuicVersion::Q046(), - ParsedQuicVersion::Q043(), + ParsedQuicVersion::RFCv1(), ParsedQuicVersion::Draft29(), + ParsedQuicVersion::T051(), ParsedQuicVersion::Q050(), + ParsedQuicVersion::Q046(), ParsedQuicVersion::Q043(), }; }
diff --git a/quic/core/quic_versions_test.cc b/quic/core/quic_versions_test.cc index d8af34f..e3bd906 100644 --- a/quic/core/quic_versions_test.cc +++ b/quic/core/quic_versions_test.cc
@@ -371,23 +371,25 @@ // yet a typo was made in doing the #defines and it was caught // only in some test far removed from here... Better safe than sorry. TEST_F(QuicVersionsTest, CheckTransportVersionNumbersForTypos) { - static_assert(SupportedVersions().size() == 5u, + static_assert(SupportedVersions().size() == 6u, "Supported versions out of sync"); EXPECT_EQ(QUIC_VERSION_43, 43); EXPECT_EQ(QUIC_VERSION_46, 46); EXPECT_EQ(QUIC_VERSION_50, 50); EXPECT_EQ(QUIC_VERSION_51, 51); EXPECT_EQ(QUIC_VERSION_IETF_DRAFT_29, 73); + EXPECT_EQ(QUIC_VERSION_IETF_RFC_V1, 80); } TEST_F(QuicVersionsTest, AlpnForVersion) { - static_assert(SupportedVersions().size() == 5u, + static_assert(SupportedVersions().size() == 6u, "Supported versions out of sync"); EXPECT_EQ("h3-Q043", AlpnForVersion(ParsedQuicVersion::Q043())); EXPECT_EQ("h3-Q046", AlpnForVersion(ParsedQuicVersion::Q046())); EXPECT_EQ("h3-Q050", AlpnForVersion(ParsedQuicVersion::Q050())); EXPECT_EQ("h3-T051", AlpnForVersion(ParsedQuicVersion::T051())); EXPECT_EQ("h3-29", AlpnForVersion(ParsedQuicVersion::Draft29())); + EXPECT_EQ("h3", AlpnForVersion(ParsedQuicVersion::RFCv1())); } TEST_F(QuicVersionsTest, QuicVersionEnabling) {
diff --git a/quic/core/tls_chlo_extractor.cc b/quic/core/tls_chlo_extractor.cc index 27f44d3..62a8636 100644 --- a/quic/core/tls_chlo_extractor.cc +++ b/quic/core/tls_chlo_extractor.cc
@@ -348,6 +348,13 @@ const int rv = SSL_set_ex_data(ssl_.get(), ex_data_index, this); CHECK_EQ(rv, 1) << "Internal allocation failure in SSL_set_ex_data"; SSL_set_accept_state(ssl_.get()); + + // Make sure we use the right TLS extension codepoint. + int use_legacy_extension = 0; + if (framer_->version().UsesLegacyTlsExtension()) { + use_legacy_extension = 1; + } + SSL_set_quic_use_legacy_codepoint(ssl_.get(), use_legacy_extension); } // Called by other methods to record any unrecoverable failures they experience.
diff --git a/quic/core/tls_client_handshaker.cc b/quic/core/tls_client_handshaker.cc index 7e7c5fe..95be1fa 100644 --- a/quic/core/tls_client_handshaker.cc +++ b/quic/core/tls_client_handshaker.cc
@@ -62,6 +62,13 @@ return false; } + // Make sure we use the right TLS extension codepoint. + int use_legacy_extension = 0; + if (session()->version().UsesLegacyTlsExtension()) { + use_legacy_extension = 1; + } + SSL_set_quic_use_legacy_codepoint(ssl(), use_legacy_extension); + // Set the SNI to send, if any. SSL_set_connect_state(ssl()); if (QUIC_DLOG_INFO_IS_ON() &&
diff --git a/quic/core/tls_server_handshaker.cc b/quic/core/tls_server_handshaker.cc index 0ae78a3..87b4213 100644 --- a/quic/core/tls_server_handshaker.cc +++ b/quic/core/tls_server_handshaker.cc
@@ -100,6 +100,13 @@ // Configure the SSL to be a server. SSL_set_accept_state(ssl()); + // Make sure we use the right TLS extension codepoint. + int use_legacy_extension = 0; + if (session->version().UsesLegacyTlsExtension()) { + use_legacy_extension = 1; + } + SSL_set_quic_use_legacy_codepoint(ssl(), use_legacy_extension); + if (GetQuicFlag(FLAGS_quic_disable_server_tls_resumption)) { SSL_set_options(ssl(), SSL_OP_NO_TICKET); } @@ -281,12 +288,17 @@ const uint8_t* client_params_bytes; size_t params_bytes_len; if (use_early_select_cert_) { + // Make sure we use the right TLS extension codepoint. + uint16_t extension_type = TLSEXT_TYPE_quic_transport_parameters; + if (session()->version().UsesLegacyTlsExtension()) { + extension_type = TLSEXT_TYPE_quic_transport_parameters_legacy; + } // When using early select cert callback, SSL_get_peer_quic_transport_params // can not be used to retrieve the client's transport parameters, but we can // use SSL_early_callback_ctx_extension_get to do that. - if (!SSL_early_callback_ctx_extension_get( - client_hello, TLSEXT_TYPE_quic_transport_parameters, - &client_params_bytes, ¶ms_bytes_len)) { + if (!SSL_early_callback_ctx_extension_get(client_hello, extension_type, + &client_params_bytes, + ¶ms_bytes_len)) { params_bytes_len = 0; } } else {
diff --git a/quic/test_tools/quic_test_utils.h b/quic/test_tools/quic_test_utils.h index 6fbcc17..50afd2d 100644 --- a/quic/test_tools/quic_test_utils.h +++ b/quic/test_tools/quic_test_utils.h
@@ -1110,6 +1110,10 @@ OnPriorityUpdateFrameReceived, (const PriorityUpdateFrame&), (override)); + MOCK_METHOD(void, + OnAcceptChFrameReceived, + (const AcceptChFrame&), + (override)); MOCK_METHOD(void, OnDataFrameReceived,
diff --git a/spdy/core/spdy_protocol.cc b/spdy/core/spdy_protocol.cc index 2116025..b1d9f56 100644 --- a/spdy/core/spdy_protocol.cc +++ b/spdy/core/spdy_protocol.cc
@@ -60,7 +60,31 @@ } bool IsDefinedFrameType(uint8_t frame_type_field) { - return frame_type_field <= SerializeFrameType(SpdyFrameType::MAX_FRAME_TYPE); + switch (static_cast<SpdyFrameType>(frame_type_field)) { + case SpdyFrameType::DATA: + return true; + case SpdyFrameType::HEADERS: + return true; + case SpdyFrameType::PRIORITY: + return true; + case SpdyFrameType::RST_STREAM: + return true; + case SpdyFrameType::SETTINGS: + return true; + case SpdyFrameType::PUSH_PROMISE: + return true; + case SpdyFrameType::PING: + return true; + case SpdyFrameType::GOAWAY: + return true; + case SpdyFrameType::WINDOW_UPDATE: + return true; + case SpdyFrameType::CONTINUATION: + return true; + case SpdyFrameType::ALTSVC: + return true; + } + return false; } SpdyFrameType ParseFrameType(uint8_t frame_type_field) {
diff --git a/spdy/core/spdy_protocol.h b/spdy/core/spdy_protocol.h index 175db51..39b3daa 100644 --- a/spdy/core/spdy_protocol.h +++ b/spdy/core/spdy_protocol.h
@@ -99,7 +99,6 @@ CONTINUATION = 0x09, // ALTSVC is a public extension. ALTSVC = 0x0a, - MAX_FRAME_TYPE = ALTSVC, }; // Flags on data packets.