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.