diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-01-20 13:40:20 +0100 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-01-22 12:41:23 +0000 |
commit | 7961cea6d1041e3e454dae6a1da660b453efd238 (patch) | |
tree | c0eeb4a9ff9ba32986289c1653d9608e53ccb444 /chromium/net/third_party | |
parent | b7034d0803538058e5c9d904ef03cf5eab34f6ef (diff) | |
download | qtwebengine-chromium-7961cea6d1041e3e454dae6a1da660b453efd238.tar.gz |
BASELINE: Update Chromium to 78.0.3904.130
Change-Id: If185e0c0061b3437531c97c9c8c78f239352a68b
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/net/third_party')
450 files changed, 23822 insertions, 6802 deletions
diff --git a/chromium/net/third_party/quiche/src/http2/decoder/decode_http2_structures_test.cc b/chromium/net/third_party/quiche/src/http2/decoder/decode_http2_structures_test.cc index 91b3ef78bb9..48b79bd5582 100644 --- a/chromium/net/third_party/quiche/src/http2/decoder/decode_http2_structures_test.cc +++ b/chromium/net/third_party/quiche/src/http2/decoder/decode_http2_structures_test.cc @@ -9,13 +9,14 @@ #include <stddef.h> +#include <string> + #include "testing/gtest/include/gtest/gtest.h" #include "net/third_party/quiche/src/http2/decoder/decode_buffer.h" #include "net/third_party/quiche/src/http2/decoder/decode_status.h" #include "net/third_party/quiche/src/http2/http2_constants.h" #include "net/third_party/quiche/src/http2/http2_structures_test_util.h" #include "net/third_party/quiche/src/http2/platform/api/http2_logging.h" -#include "net/third_party/quiche/src/http2/platform/api/http2_string.h" #include "net/third_party/quiche/src/http2/platform/api/http2_string_piece.h" #include "net/third_party/quiche/src/http2/test_tools/http2_random.h" #include "net/third_party/quiche/src/http2/tools/http2_frame_builder.h" @@ -32,7 +33,7 @@ Http2StringPiece ToStringPiece(T (&data)[N]) { } template <class S> -Http2String SerializeStructure(const S& s) { +std::string SerializeStructure(const S& s) { Http2FrameBuilder fb; fb.Append(s); EXPECT_EQ(S::EncodedSize(), fb.size()); @@ -70,7 +71,7 @@ class StructureDecoderTest : public ::testing::Test { // Encode the structure |in_s| into bytes, then decode the bytes // and validate that the decoder produced the same field values. void EncodeThenDecode(const S& in_s) { - Http2String bytes = SerializeStructure(in_s); + std::string bytes = SerializeStructure(in_s); EXPECT_EQ(S::EncodedSize(), bytes.size()); DecodeLeadingStructure(&in_s, bytes); } diff --git a/chromium/net/third_party/quiche/src/http2/decoder/http2_frame_decoder_test.cc b/chromium/net/third_party/quiche/src/http2/decoder/http2_frame_decoder_test.cc index 50e75edf454..7ed67f39674 100644 --- a/chromium/net/third_party/quiche/src/http2/decoder/http2_frame_decoder_test.cc +++ b/chromium/net/third_party/quiche/src/http2/decoder/http2_frame_decoder_test.cc @@ -6,13 +6,13 @@ // Tests of Http2FrameDecoder. +#include <string> #include <vector> #include "testing/gtest/include/gtest/gtest.h" #include "net/third_party/quiche/src/http2/http2_constants.h" #include "net/third_party/quiche/src/http2/platform/api/http2_logging.h" #include "net/third_party/quiche/src/http2/platform/api/http2_reconstruct_object.h" -#include "net/third_party/quiche/src/http2/platform/api/http2_string.h" #include "net/third_party/quiche/src/http2/platform/api/http2_string_piece.h" #include "net/third_party/quiche/src/http2/platform/api/http2_test_helpers.h" #include "net/third_party/quiche/src/http2/test_tools/frame_parts.h" @@ -84,7 +84,7 @@ class Http2FrameDecoderTest : public RandomDecoderTest { // The decoder will discard the remaining bytes, but not go beyond that, // which these conditions verify. size_t extra = 10; - Http2String junk(remaining + extra, '0'); + std::string junk(remaining + extra, '0'); DecodeBuffer tmp(junk); EXPECT_EQ(DecodeStatus::kDecodeDone, decoder_.DecodeFrame(&tmp)); EXPECT_EQ(remaining, tmp.Offset()); @@ -147,8 +147,8 @@ class Http2FrameDecoderTest : public RandomDecoderTest { VERIFY_GT(slow_decode_count_, 0u); // Repeat with more input; it should stop without reading that input. - Http2String next_frame = Random().RandString(10); - Http2String input(payload.data(), payload.size()); + std::string next_frame = Random().RandString(10); + std::string input(payload.data(), payload.size()); input += next_frame; ResetDecodeSpeedCounters(); diff --git a/chromium/net/third_party/quiche/src/http2/decoder/http2_structure_decoder_test.cc b/chromium/net/third_party/quiche/src/http2/decoder/http2_structure_decoder_test.cc index 7b9afc59234..254510919d3 100644 --- a/chromium/net/third_party/quiche/src/http2/decoder/http2_structure_decoder_test.cc +++ b/chromium/net/third_party/quiche/src/http2/decoder/http2_structure_decoder_test.cc @@ -21,6 +21,7 @@ #include <stddef.h> #include <cstdint> +#include <string> #include "testing/gtest/include/gtest/gtest.h" #include "net/third_party/quiche/src/http2/decoder/decode_buffer.h" @@ -29,7 +30,6 @@ #include "net/third_party/quiche/src/http2/http2_structures_test_util.h" #include "net/third_party/quiche/src/http2/platform/api/http2_logging.h" #include "net/third_party/quiche/src/http2/platform/api/http2_reconstruct_object.h" -#include "net/third_party/quiche/src/http2/platform/api/http2_string.h" #include "net/third_party/quiche/src/http2/platform/api/http2_string_piece.h" #include "net/third_party/quiche/src/http2/platform/api/http2_test_helpers.h" #include "net/third_party/quiche/src/http2/tools/http2_frame_builder.h" @@ -159,7 +159,7 @@ class Http2StructureDecoderTest : public RandomDecoderTest { // Encode the structure |in_s| into bytes, then decode the bytes // and validate that the decoder produced the same field values. AssertionResult EncodeThenDecode(const S& in_s) { - Http2String bytes = SerializeStructure(in_s); + std::string bytes = SerializeStructure(in_s); VERIFY_EQ(S::EncodedSize(), bytes.size()); VERIFY_AND_RETURN_SUCCESS(DecodeLeadingStructure(&in_s, bytes)); } diff --git a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/altsvc_payload_decoder_test.cc b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/altsvc_payload_decoder_test.cc index 6a4bee53b14..354fdae0621 100644 --- a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/altsvc_payload_decoder_test.cc +++ b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/altsvc_payload_decoder_test.cc @@ -6,13 +6,14 @@ #include <stddef.h> +#include <string> + #include "testing/gtest/include/gtest/gtest.h" #include "net/third_party/quiche/src/http2/decoder/http2_frame_decoder_listener.h" #include "net/third_party/quiche/src/http2/decoder/payload_decoders/payload_decoder_base_test_util.h" #include "net/third_party/quiche/src/http2/http2_constants.h" #include "net/third_party/quiche/src/http2/http2_structures_test_util.h" #include "net/third_party/quiche/src/http2/platform/api/http2_logging.h" -#include "net/third_party/quiche/src/http2/platform/api/http2_string.h" #include "net/third_party/quiche/src/http2/test_tools/frame_parts.h" #include "net/third_party/quiche/src/http2/test_tools/frame_parts_collector.h" #include "net/third_party/quiche/src/http2/test_tools/http2_random.h" @@ -103,8 +104,8 @@ INSTANTIATE_TEST_SUITE_P(VariousOriginAndValueLengths, ::testing::Values(0, 1, 3, 65537))); TEST_P(AltSvcPayloadLengthTests, ValidOriginAndValueLength) { - Http2String origin = Random().RandString(origin_length_); - Http2String value = Random().RandString(value_length_); + std::string origin = Random().RandString(origin_length_); + std::string value = Random().RandString(value_length_); Http2FrameBuilder fb; fb.Append(Http2AltSvcFields{origin_length_}); fb.Append(origin); diff --git a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/continuation_payload_decoder_test.cc b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/continuation_payload_decoder_test.cc index 35479a15082..125e16fc7c9 100644 --- a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/continuation_payload_decoder_test.cc +++ b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/continuation_payload_decoder_test.cc @@ -6,6 +6,7 @@ #include <stddef.h> +#include <string> #include <type_traits> #include "testing/gtest/include/gtest/gtest.h" @@ -14,7 +15,6 @@ #include "net/third_party/quiche/src/http2/http2_constants.h" #include "net/third_party/quiche/src/http2/http2_structures.h" #include "net/third_party/quiche/src/http2/platform/api/http2_logging.h" -#include "net/third_party/quiche/src/http2/platform/api/http2_string.h" #include "net/third_party/quiche/src/http2/test_tools/frame_parts.h" #include "net/third_party/quiche/src/http2/test_tools/frame_parts_collector.h" #include "net/third_party/quiche/src/http2/tools/random_decoder_test.h" @@ -73,7 +73,7 @@ INSTANTIATE_TEST_SUITE_P(VariousLengths, ::testing::Values(0, 1, 2, 3, 4, 5, 6)); TEST_P(ContinuationPayloadDecoderTest, ValidLength) { - Http2String hpack_payload = Random().RandString(length_); + std::string hpack_payload = Random().RandString(length_); Http2FrameHeader frame_header(length_, Http2FrameType::CONTINUATION, RandFlags(), RandStreamId()); set_frame_header(frame_header); diff --git a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/data_payload_decoder_test.cc b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/data_payload_decoder_test.cc index 1cae7141ef5..f1dc017c9da 100644 --- a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/data_payload_decoder_test.cc +++ b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/data_payload_decoder_test.cc @@ -6,6 +6,8 @@ #include <stddef.h> +#include <string> + #include "testing/gtest/include/gtest/gtest.h" #include "net/third_party/quiche/src/http2/decoder/http2_frame_decoder_listener.h" #include "net/third_party/quiche/src/http2/decoder/payload_decoders/payload_decoder_base_test_util.h" @@ -13,7 +15,6 @@ #include "net/third_party/quiche/src/http2/http2_structures.h" #include "net/third_party/quiche/src/http2/http2_structures_test_util.h" #include "net/third_party/quiche/src/http2/platform/api/http2_logging.h" -#include "net/third_party/quiche/src/http2/platform/api/http2_string.h" #include "net/third_party/quiche/src/http2/platform/api/http2_test_helpers.h" #include "net/third_party/quiche/src/http2/test_tools/frame_parts.h" #include "net/third_party/quiche/src/http2/test_tools/frame_parts_collector.h" @@ -84,7 +85,7 @@ class DataPayloadDecoderTest Reset(); uint8_t flags = RandFlags(); - Http2String data_payload = Random().RandString(data_size); + std::string data_payload = Random().RandString(data_size); frame_builder_.Append(data_payload); MaybeAppendTrailingPadding(); diff --git a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/goaway_payload_decoder_test.cc b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/goaway_payload_decoder_test.cc index 5ea533a4703..b2d96fb2d71 100644 --- a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/goaway_payload_decoder_test.cc +++ b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/goaway_payload_decoder_test.cc @@ -6,13 +6,14 @@ #include <stddef.h> +#include <string> + #include "testing/gtest/include/gtest/gtest.h" #include "net/third_party/quiche/src/http2/decoder/http2_frame_decoder_listener.h" #include "net/third_party/quiche/src/http2/decoder/payload_decoders/payload_decoder_base_test_util.h" #include "net/third_party/quiche/src/http2/http2_constants.h" #include "net/third_party/quiche/src/http2/http2_structures_test_util.h" #include "net/third_party/quiche/src/http2/platform/api/http2_logging.h" -#include "net/third_party/quiche/src/http2/platform/api/http2_string.h" #include "net/third_party/quiche/src/http2/test_tools/frame_parts.h" #include "net/third_party/quiche/src/http2/test_tools/frame_parts_collector.h" #include "net/third_party/quiche/src/http2/test_tools/http2_random.h" @@ -92,7 +93,7 @@ INSTANTIATE_TEST_SUITE_P(VariousLengths, TEST_P(GoAwayOpaqueDataLengthTests, ValidLength) { Http2GoAwayFields goaway; Randomize(&goaway, RandomPtr()); - Http2String opaque_data = Random().RandString(length_); + std::string opaque_data = Random().RandString(length_); Http2FrameBuilder fb; fb.Append(goaway); fb.Append(opaque_data); diff --git a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/headers_payload_decoder_test.cc b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/headers_payload_decoder_test.cc index a7a90bfd21f..4e0f2e307bb 100644 --- a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/headers_payload_decoder_test.cc +++ b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/headers_payload_decoder_test.cc @@ -6,13 +6,14 @@ #include <stddef.h> +#include <string> + #include "testing/gtest/include/gtest/gtest.h" #include "net/third_party/quiche/src/http2/decoder/http2_frame_decoder_listener.h" #include "net/third_party/quiche/src/http2/decoder/payload_decoders/payload_decoder_base_test_util.h" #include "net/third_party/quiche/src/http2/http2_constants.h" #include "net/third_party/quiche/src/http2/http2_structures_test_util.h" #include "net/third_party/quiche/src/http2/platform/api/http2_logging.h" -#include "net/third_party/quiche/src/http2/platform/api/http2_string.h" #include "net/third_party/quiche/src/http2/test_tools/frame_parts.h" #include "net/third_party/quiche/src/http2/test_tools/frame_parts_collector.h" #include "net/third_party/quiche/src/http2/test_tools/http2_random.h" @@ -116,7 +117,7 @@ TEST_P(HeadersPayloadDecoderTest, VariousHpackPayloadSizes) { frame_builder_.Append(priority); } - Http2String hpack_payload = Random().RandString(hpack_size); + std::string hpack_payload = Random().RandString(hpack_size); frame_builder_.Append(hpack_payload); MaybeAppendTrailingPadding(); diff --git a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/payload_decoder_base_test_util.h b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/payload_decoder_base_test_util.h index 66dd88a48c0..5de0d1b88c1 100644 --- a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/payload_decoder_base_test_util.h +++ b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/payload_decoder_base_test_util.h @@ -9,6 +9,8 @@ #include <stddef.h> +#include <string> + #include "testing/gtest/include/gtest/gtest.h" #include "net/third_party/quiche/src/http2/decoder/decode_buffer.h" #include "net/third_party/quiche/src/http2/decoder/decode_status.h" @@ -19,7 +21,6 @@ #include "net/third_party/quiche/src/http2/http2_structures.h" #include "net/third_party/quiche/src/http2/platform/api/http2_logging.h" #include "net/third_party/quiche/src/http2/platform/api/http2_reconstruct_object.h" -#include "net/third_party/quiche/src/http2/platform/api/http2_string.h" #include "net/third_party/quiche/src/http2/platform/api/http2_string_piece.h" #include "net/third_party/quiche/src/http2/test_tools/frame_parts.h" #include "net/third_party/quiche/src/http2/tools/http2_frame_builder.h" @@ -429,7 +430,7 @@ class AbstractPaddablePayloadDecoderTest } HTTP2_VLOG(1) << "payload_length=" << payload_length; - Http2String payload = fb.buffer().substr(0, payload_length); + std::string payload = fb.buffer().substr(0, payload_length); // The missing length is the amount we cut off the end, unless // payload_length is zero, in which case the decoder knows only that 1 diff --git a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/push_promise_payload_decoder_test.cc b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/push_promise_payload_decoder_test.cc index 64dd935b44e..0561aa4b96b 100644 --- a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/push_promise_payload_decoder_test.cc +++ b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/push_promise_payload_decoder_test.cc @@ -6,13 +6,14 @@ #include <stddef.h> +#include <string> + #include "testing/gtest/include/gtest/gtest.h" #include "net/third_party/quiche/src/http2/decoder/http2_frame_decoder_listener.h" #include "net/third_party/quiche/src/http2/decoder/payload_decoders/payload_decoder_base_test_util.h" #include "net/third_party/quiche/src/http2/http2_constants.h" #include "net/third_party/quiche/src/http2/http2_structures_test_util.h" #include "net/third_party/quiche/src/http2/platform/api/http2_logging.h" -#include "net/third_party/quiche/src/http2/platform/api/http2_string.h" #include "net/third_party/quiche/src/http2/test_tools/frame_parts.h" #include "net/third_party/quiche/src/http2/test_tools/frame_parts_collector.h" #include "net/third_party/quiche/src/http2/test_tools/http2_random.h" @@ -98,7 +99,7 @@ TEST_P(PushPromisePayloadDecoderTest, VariousHpackPayloadSizes) { HTTP2_LOG(INFO) << "########### hpack_size = " << hpack_size << " ###########"; Reset(); - Http2String hpack_payload = Random().RandString(hpack_size); + std::string hpack_payload = Random().RandString(hpack_size); Http2PushPromiseFields push_promise{RandStreamId()}; frame_builder_.Append(push_promise); frame_builder_.Append(hpack_payload); diff --git a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/unknown_payload_decoder_test.cc b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/unknown_payload_decoder_test.cc index 018bd82e046..10759329c0f 100644 --- a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/unknown_payload_decoder_test.cc +++ b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/unknown_payload_decoder_test.cc @@ -6,6 +6,7 @@ #include <stddef.h> +#include <string> #include <type_traits> #include "testing/gtest/include/gtest/gtest.h" @@ -14,7 +15,6 @@ #include "net/third_party/quiche/src/http2/http2_constants.h" #include "net/third_party/quiche/src/http2/http2_structures.h" #include "net/third_party/quiche/src/http2/platform/api/http2_logging.h" -#include "net/third_party/quiche/src/http2/platform/api/http2_string.h" #include "net/third_party/quiche/src/http2/test_tools/frame_parts.h" #include "net/third_party/quiche/src/http2/test_tools/frame_parts_collector.h" #include "net/third_party/quiche/src/http2/test_tools/http2_random.h" @@ -83,7 +83,7 @@ INSTANTIATE_TEST_SUITE_P(VariousLengths, ::testing::Values(0, 1, 2, 3, 255, 256)); TEST_P(UnknownPayloadDecoderTest, ValidLength) { - Http2String unknown_payload = Random().RandString(length_); + std::string unknown_payload = Random().RandString(length_); Http2FrameHeader frame_header(length_, g_unknown_frame_type, Random().Rand8(), RandStreamId()); set_frame_header(frame_header); diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_block_collector.cc b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_block_collector.cc index 1137be53d85..3490ca7d740 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_block_collector.cc +++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_block_collector.cc @@ -77,14 +77,14 @@ void HpackBlockCollector::ExpectNameIndexAndLiteralValue( HpackEntryType type, size_t index, bool value_huffman, - const Http2String& value) { + const std::string& value) { entries_.push_back(HpackEntryCollector(type, index, value_huffman, value)); } void HpackBlockCollector::ExpectLiteralNameAndValue(HpackEntryType type, bool name_huffman, - const Http2String& name, + const std::string& name, bool value_huffman, - const Http2String& value) { + const std::string& value) { entries_.push_back( HpackEntryCollector(type, name_huffman, name, value_huffman, value)); } diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_block_collector.h b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_block_collector.h index 6ad14057c24..0d8f8113d70 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_block_collector.h +++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_block_collector.h @@ -15,6 +15,7 @@ #include <stddef.h> +#include <string> #include <vector> #include "testing/gtest/include/gtest/gtest.h" @@ -22,7 +23,6 @@ #include "net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_decoder_listener.h" #include "net/third_party/quiche/src/http2/hpack/http2_hpack_constants.h" #include "net/third_party/quiche/src/http2/hpack/tools/hpack_block_builder.h" -#include "net/third_party/quiche/src/http2/platform/api/http2_string.h" #include "net/third_party/quiche/src/http2/platform/api/http2_string_piece.h" #include "net/third_party/quiche/src/http2/test_tools/http2_random.h" @@ -66,14 +66,14 @@ class HpackBlockCollector : public HpackEntryDecoderListener { void ExpectNameIndexAndLiteralValue(HpackEntryType type, size_t index, bool value_huffman, - const Http2String& value); + const std::string& value); // Add an HPACK entry for a header entry with a literal name and value. void ExpectLiteralNameAndValue(HpackEntryType type, bool name_huffman, - const Http2String& name, + const std::string& name, bool value_huffman, - const Http2String& value); + const std::string& value); // Shuffle the entries, in support of generating an HPACK block of entries // in some random order. diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_block_decoder.cc b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_block_decoder.cc index 656f8e9866e..bb86597e812 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_block_decoder.cc +++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_block_decoder.cc @@ -48,7 +48,7 @@ DecodeStatus HpackBlockDecoder::Decode(DecodeBuffer* db) { return DecodeStatus::kDecodeDone; } -Http2String HpackBlockDecoder::DebugString() const { +std::string HpackBlockDecoder::DebugString() const { return Http2StrCat("HpackBlockDecoder(", entry_decoder_.DebugString(), ", listener@", Http2Hex(reinterpret_cast<intptr_t>(listener_)), diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_block_decoder.h b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_block_decoder.h index a17664f8ded..fb44c3f1617 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_block_decoder.h +++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_block_decoder.h @@ -10,13 +10,14 @@ // or dynamic table support, so table indices remain indices at this level. // Reports the entries to an HpackEntryDecoderListener. +#include <string> + #include "net/third_party/quiche/src/http2/decoder/decode_buffer.h" #include "net/third_party/quiche/src/http2/decoder/decode_status.h" #include "net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_decoder.h" #include "net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_decoder_listener.h" #include "net/third_party/quiche/src/http2/platform/api/http2_export.h" #include "net/third_party/quiche/src/http2/platform/api/http2_logging.h" -#include "net/third_party/quiche/src/http2/platform/api/http2_string.h" namespace http2 { @@ -48,7 +49,7 @@ class HTTP2_EXPORT_PRIVATE HpackBlockDecoder { // first byte of a new HPACK entry)? bool before_entry() const { return before_entry_; } - Http2String DebugString() const; + std::string DebugString() const; private: HpackEntryDecoder entry_decoder_; diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_block_decoder_test.cc b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_block_decoder_test.cc index fce45b2a316..4988b5cde37 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_block_decoder_test.cc +++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_block_decoder_test.cc @@ -7,6 +7,7 @@ // Tests of HpackBlockDecoder. #include <cstdint> +#include <string> #include "testing/gtest/include/gtest/gtest.h" #include "net/third_party/quiche/src/http2/decoder/decode_buffer.h" @@ -67,14 +68,14 @@ class HpackBlockDecoderTest : public RandomDecoderTest { AssertionResult DecodeHpackExampleAndValidateSeveralWays( Http2StringPiece hpack_example, Validator validator) { - Http2String input = HpackExampleToStringOrDie(hpack_example); + std::string input = HpackExampleToStringOrDie(hpack_example); DecodeBuffer db(input); return DecodeAndValidateSeveralWays(&db, validator); } uint8_t Rand8() { return Random().Rand8(); } - Http2String Rand8String() { return Random().RandString(Rand8()); } + std::string Rand8String() { return Random().RandString(Rand8()); } HpackBlockCollector collector_; HpackEntryDecoderVLoggingListener listener_; @@ -157,7 +158,7 @@ TEST_F(HpackBlockDecoderTest, SpecExample_C_2_4) { } // http://httpwg.org/specs/rfc7541.html#rfc.section.C.3.1 TEST_F(HpackBlockDecoderTest, SpecExample_C_3_1) { - Http2String example = R"( + std::string example = R"( 82 | == Indexed - Add == | idx = 2 | -> :method: GET @@ -191,7 +192,7 @@ TEST_F(HpackBlockDecoderTest, SpecExample_C_3_1) { // http://httpwg.org/specs/rfc7541.html#rfc.section.C.5.1 TEST_F(HpackBlockDecoderTest, SpecExample_C_5_1) { - Http2String example = R"( + std::string example = R"( 48 | == Literal indexed == | Indexed name (idx = 8) | :status diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_string_buffer.cc b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_string_buffer.cc index 76431a6ffd0..6a200085cb7 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_string_buffer.cc +++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_string_buffer.cc @@ -9,7 +9,6 @@ #include "net/third_party/quiche/src/http2/platform/api/http2_bug_tracker.h" #include "net/third_party/quiche/src/http2/platform/api/http2_estimate_memory_usage.h" #include "net/third_party/quiche/src/http2/platform/api/http2_logging.h" -#include "net/third_party/quiche/src/http2/platform/api/http2_string.h" namespace http2 { @@ -168,7 +167,7 @@ void HpackDecoderStringBuffer::BufferStringIfUnbuffered() { << state_ << ", backing=" << backing_; if (state_ != State::RESET && backing_ == Backing::UNBUFFERED) { HTTP2_DVLOG(2) - << "HpackDecoderStringBuffer buffering Http2String of length " + << "HpackDecoderStringBuffer buffering std::string of length " << value_.size(); buffer_.assign(value_.data(), value_.size()); if (state_ == State::COMPLETE) { @@ -194,7 +193,7 @@ Http2StringPiece HpackDecoderStringBuffer::str() const { return value_; } -Http2String HpackDecoderStringBuffer::ReleaseString() { +std::string HpackDecoderStringBuffer::ReleaseString() { HTTP2_DVLOG(3) << "HpackDecoderStringBuffer::ReleaseString"; DCHECK_EQ(state_, State::COMPLETE); DCHECK_EQ(backing_, Backing::BUFFERED); @@ -203,7 +202,7 @@ Http2String HpackDecoderStringBuffer::ReleaseString() { if (backing_ == Backing::BUFFERED) { return std::move(buffer_); } else { - return Http2String(value_); + return std::string(value_); } } return ""; diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_string_buffer.h b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_string_buffer.h index 8a810b2d9d3..d8dce6c150d 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_string_buffer.h +++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_string_buffer.h @@ -12,10 +12,10 @@ #include <stddef.h> #include <ostream> +#include <string> #include "net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_decoder.h" #include "net/third_party/quiche/src/http2/platform/api/http2_export.h" -#include "net/third_party/quiche/src/http2/platform/api/http2_string.h" #include "net/third_party/quiche/src/http2/platform/api/http2_string_piece.h" namespace http2 { @@ -58,7 +58,7 @@ class HTTP2_EXPORT_PRIVATE HpackDecoderStringBuffer { // unless the string has been buffered (to avoid forcing a potentially // unnecessary copy). ReleaseString() also resets the instance so that it can // be used to collect another string. - Http2String ReleaseString(); + std::string ReleaseString(); State state_for_testing() const { return state_; } Backing backing_for_testing() const { return backing_; } @@ -70,7 +70,7 @@ class HTTP2_EXPORT_PRIVATE HpackDecoderStringBuffer { private: // Storage for the string being buffered, if buffering is necessary // (e.g. if Huffman encoded, buffer_ is storage for the decoded string). - Http2String buffer_; + std::string buffer_; // The Http2StringPiece to be returned by HpackDecoderStringBuffer::str(). If // a string has been collected, but not buffered, value_ points to that diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_string_buffer_test.cc b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_string_buffer_test.cc index 11b0d9773bb..ec807c84b66 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_string_buffer_test.cc +++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_string_buffer_test.cc @@ -11,7 +11,6 @@ #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "net/third_party/quiche/src/http2/platform/api/http2_logging.h" -#include "net/third_party/quiche/src/http2/platform/api/http2_string.h" #include "net/third_party/quiche/src/http2/platform/api/http2_string_utils.h" #include "net/third_party/quiche/src/http2/platform/api/http2_test_helpers.h" @@ -33,11 +32,11 @@ class HpackDecoderStringBufferTest : public ::testing::Test { // We want to know that HTTP2_LOG(x) << buf_ will work in production should // that be needed, so we test that it outputs the expected values. - AssertionResult VerifyLogHasSubstrs(std::initializer_list<Http2String> strs) { + AssertionResult VerifyLogHasSubstrs(std::initializer_list<std::string> strs) { HTTP2_VLOG(1) << buf_; std::ostringstream ss; buf_.OutputDebugStringTo(ss); - Http2String dbg_str(ss.str()); + std::string dbg_str(ss.str()); for (const auto& expected : strs) { VERIFY_THAT(dbg_str, HasSubstr(expected)); } @@ -153,7 +152,7 @@ TEST_F(HpackDecoderStringBufferTest, PlainSplit) { } TEST_F(HpackDecoderStringBufferTest, HuffmanWhole) { - Http2String encoded = Http2HexDecode("f1e3c2e5f23a6ba0ab90f4ff"); + std::string encoded = Http2HexDecode("f1e3c2e5f23a6ba0ab90f4ff"); Http2StringPiece decoded("www.example.com"); EXPECT_EQ(state(), State::RESET); @@ -172,15 +171,15 @@ TEST_F(HpackDecoderStringBufferTest, HuffmanWhole) { EXPECT_TRUE(VerifyLogHasSubstrs( {"{state=COMPLETE", "backing=BUFFERED", "buffer: www.example.com}"})); - Http2String s = buf_.ReleaseString(); + std::string s = buf_.ReleaseString(); EXPECT_EQ(s, decoded); EXPECT_EQ(state(), State::RESET); } TEST_F(HpackDecoderStringBufferTest, HuffmanSplit) { - Http2String encoded = Http2HexDecode("f1e3c2e5f23a6ba0ab90f4ff"); - Http2String part1 = encoded.substr(0, 5); - Http2String part2 = encoded.substr(5); + std::string encoded = Http2HexDecode("f1e3c2e5f23a6ba0ab90f4ff"); + std::string part1 = encoded.substr(0, 5); + std::string part2 = encoded.substr(5); Http2StringPiece decoded("www.example.com"); EXPECT_EQ(state(), State::RESET); @@ -217,7 +216,7 @@ TEST_F(HpackDecoderStringBufferTest, HuffmanSplit) { TEST_F(HpackDecoderStringBufferTest, InvalidHuffmanOnData) { // Explicitly encode the End-of-String symbol, a no-no. - Http2String encoded = Http2HexDecode("ffffffff"); + std::string encoded = Http2HexDecode("ffffffff"); buf_.OnStart(/*huffman_encoded*/ true, encoded.size()); EXPECT_EQ(state(), State::COLLECTING); @@ -231,7 +230,7 @@ TEST_F(HpackDecoderStringBufferTest, InvalidHuffmanOnData) { TEST_F(HpackDecoderStringBufferTest, InvalidHuffmanOnEnd) { // Last byte of string doesn't end with prefix of End-of-String symbol. - Http2String encoded = Http2HexDecode("00"); + std::string encoded = Http2HexDecode("00"); buf_.OnStart(/*huffman_encoded*/ true, encoded.size()); EXPECT_EQ(state(), State::COLLECTING); diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_tables_test.cc b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_tables_test.cc index 592ce563544..db4ada111fd 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_tables_test.cc +++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_tables_test.cc @@ -5,6 +5,7 @@ #include "net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_tables.h" #include <algorithm> +#include <string> #include <tuple> #include <vector> @@ -12,7 +13,6 @@ #include "testing/gtest/include/gtest/gtest.h" #include "net/third_party/quiche/src/http2/hpack/http2_hpack_constants.h" #include "net/third_party/quiche/src/http2/platform/api/http2_logging.h" -#include "net/third_party/quiche/src/http2/platform/api/http2_string.h" #include "net/third_party/quiche/src/http2/platform/api/http2_test_helpers.h" #include "net/third_party/quiche/src/http2/test_tools/http2_random.h" #include "net/third_party/quiche/src/http2/tools/random_util.h" @@ -96,7 +96,7 @@ TEST_F(HpackDecoderStaticTableTest, StaticTableContents) { EXPECT_TRUE(VerifyStaticTableContents()); } -size_t Size(const Http2String& name, const Http2String& value) { +size_t Size(const std::string& name, const std::string& value) { return name.size() + value.size() + 32; } @@ -105,11 +105,11 @@ size_t Size(const Http2String& name, const Http2String& value) { // dynamic table containing FakeHpackEntry instances. We can thus compare the // contents of the actual table with those in fake_dynamic_table_. -typedef std::tuple<Http2String, Http2String, size_t> FakeHpackEntry; -const Http2String& Name(const FakeHpackEntry& entry) { +typedef std::tuple<std::string, std::string, size_t> FakeHpackEntry; +const std::string& Name(const FakeHpackEntry& entry) { return std::get<0>(entry); } -const Http2String& Value(const FakeHpackEntry& entry) { +const std::string& Value(const FakeHpackEntry& entry) { return std::get<1>(entry); } size_t Size(const FakeHpackEntry& entry) { @@ -133,7 +133,7 @@ class HpackDecoderTablesTest : public HpackDecoderStaticTableTest { } // Insert the name and value into fake_dynamic_table_. - void FakeInsert(const Http2String& name, const Http2String& value) { + void FakeInsert(const std::string& name, const std::string& value) { FakeHpackEntry entry(name, value, Size(name, value)); fake_dynamic_table_.insert(fake_dynamic_table_.begin(), entry); } @@ -204,7 +204,7 @@ class HpackDecoderTablesTest : public HpackDecoderStaticTableTest { // Insert an entry into the dynamic table, confirming that trimming of entries // occurs if the total size is greater than the limit, and that older entries // move up by 1 index. - AssertionResult Insert(const Http2String& name, const Http2String& value) { + AssertionResult Insert(const std::string& name, const std::string& value) { size_t old_count = num_dynamic_entries(); if (tables_.Insert(HpackString(name), HpackString(value))) { VERIFY_GT(current_dynamic_size(), 0u); @@ -251,9 +251,9 @@ TEST_F(HpackDecoderTablesTest, RandomDynamicTable) { for (size_t limit : table_sizes) { ASSERT_TRUE(DynamicTableSizeUpdate(limit)); for (int insert_count = 0; insert_count < 100; ++insert_count) { - Http2String name = + std::string name = GenerateHttp2HeaderName(random_.UniformInRange(2, 40), RandomPtr()); - Http2String value = + std::string value = GenerateWebSafeString(random_.UniformInRange(2, 600), RandomPtr()); ASSERT_TRUE(Insert(name, value)); } diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_test.cc b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_test.cc index bf2cee52c8d..9734bdd71a8 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_test.cc +++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_test.cc @@ -6,6 +6,7 @@ // Tests of HpackDecoder. +#include <string> #include <tuple> #include <utility> #include <vector> @@ -22,7 +23,6 @@ #include "net/third_party/quiche/src/http2/hpack/tools/hpack_example.h" #include "net/third_party/quiche/src/http2/http2_constants.h" #include "net/third_party/quiche/src/http2/platform/api/http2_logging.h" -#include "net/third_party/quiche/src/http2/platform/api/http2_string.h" #include "net/third_party/quiche/src/http2/platform/api/http2_test_helpers.h" #include "net/third_party/quiche/src/http2/test_tools/http2_random.h" #include "net/third_party/quiche/src/http2/tools/random_util.h" @@ -57,7 +57,7 @@ class HpackDecoderPeer { namespace { -typedef std::tuple<HpackEntryType, Http2String, Http2String> HpackHeaderEntry; +typedef std::tuple<HpackEntryType, std::string, std::string> HpackHeaderEntry; typedef std::vector<HpackHeaderEntry> HpackHeaderEntries; // TODO(jamessynge): Create a ...test_utils.h file with the mock listener @@ -121,7 +121,7 @@ class HpackDecoderTest : public ::testing::TestWithParam<bool>, // error_message may be used in a GOAWAY frame as the Opaque Data. void OnHeaderErrorDetected(Http2StringPiece error_message) override { ASSERT_TRUE(saw_start_); - error_messages_.push_back(Http2String(error_message)); + error_messages_.push_back(std::string(error_message)); // No further callbacks should be made at this point, so replace 'this' as // the listener with mock_listener_, which is a strict mock, so will // generate an error for any calls. @@ -219,7 +219,7 @@ class HpackDecoderTest : public ::testing::TestWithParam<bool>, HpackDecoder decoder_; testing::StrictMock<MockHpackDecoderListener> mock_listener_; HpackHeaderEntries header_entries_; - std::vector<Http2String> error_messages_; + std::vector<std::string> error_messages_; bool fragment_the_hpack_block_; bool saw_start_ = false; bool saw_end_ = false; @@ -232,7 +232,7 @@ INSTANTIATE_TEST_SUITE_P(AllWays, HpackDecoderTest, ::testing::Bool()); // http://httpwg.org/specs/rfc7541.html#rfc.section.C.3 TEST_P(HpackDecoderTest, C3_RequestExamples) { // C.3.1 First Request - Http2String hpack_block = HpackExampleToStringOrDie(R"( + std::string hpack_block = HpackExampleToStringOrDie(R"( 82 | == Indexed - Add == | idx = 2 | -> :method: GET @@ -367,7 +367,7 @@ TEST_P(HpackDecoderTest, C3_RequestExamples) { // http://httpwg.org/specs/rfc7541.html#rfc.section.C.4 TEST_P(HpackDecoderTest, C4_RequestExamplesWithHuffmanEncoding) { // C.4.1 First Request - Http2String hpack_block = HpackExampleToStringOrDie(R"( + std::string hpack_block = HpackExampleToStringOrDie(R"( 82 | == Indexed - Add == | idx = 2 | -> :method: GET @@ -526,7 +526,7 @@ TEST_P(HpackDecoderTest, C5_ResponseExamples) { // date: Mon, 21 Oct 2013 20:13:21 GMT // location: https://www.example.com - Http2String hpack_block = HpackExampleToStringOrDie(R"( + std::string hpack_block = HpackExampleToStringOrDie(R"( 48 | == Literal indexed == | Indexed name (idx = 8) | :status @@ -751,7 +751,7 @@ TEST_P(HpackDecoderTest, C6_ResponseExamplesWithHuffmanEncoding) { // cache-control: private // date: Mon, 21 Oct 2013 20:13:21 GMT // location: https://www.example.com - Http2String hpack_block = HpackExampleToStringOrDie(R"( + std::string hpack_block = HpackExampleToStringOrDie(R"( 48 | == Literal indexed == | Indexed name (idx = 8) | :status diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_collector.cc b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_collector.cc index 327075df7ee..7706bd6c644 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_collector.cc +++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_collector.cc @@ -35,7 +35,7 @@ HpackEntryCollector::HpackEntryCollector(HpackEntryType type, HpackEntryCollector::HpackEntryCollector(HpackEntryType type, size_t index, bool value_huffman, - const Http2String& value) + const std::string& value) : header_type_(type), index_(index), value_(value, value_huffman), @@ -43,9 +43,9 @@ HpackEntryCollector::HpackEntryCollector(HpackEntryType type, ended_(true) {} HpackEntryCollector::HpackEntryCollector(HpackEntryType type, bool name_huffman, - const Http2String& name, + const std::string& name, bool value_huffman, - const Http2String& value) + const std::string& value) : header_type_(type), index_(0), name_(name, name_huffman), @@ -232,8 +232,8 @@ void HpackEntryCollector::AppendToHpackBlockBuilder( } } -Http2String HpackEntryCollector::ToString() const { - Http2String result("Type="); +std::string HpackEntryCollector::ToString() const { + std::string result("Type="); switch (header_type_) { case HpackEntryType::kIndexedHeader: result += "IndexedHeader"; diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_collector.h b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_collector.h index c2c5fe5830d..6b27b7b7f81 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_collector.h +++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_collector.h @@ -13,13 +13,13 @@ #include <stddef.h> #include <iosfwd> +#include <string> #include "testing/gtest/include/gtest/gtest.h" #include "net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_decoder_listener.h" #include "net/third_party/quiche/src/http2/hpack/decoder/hpack_string_collector.h" #include "net/third_party/quiche/src/http2/hpack/http2_hpack_constants.h" #include "net/third_party/quiche/src/http2/hpack/tools/hpack_block_builder.h" -#include "net/third_party/quiche/src/http2/platform/api/http2_string.h" #include "net/third_party/quiche/src/http2/platform/api/http2_string_piece.h" namespace http2 { @@ -37,12 +37,12 @@ class HpackEntryCollector : public HpackEntryDecoderListener { HpackEntryCollector(HpackEntryType type, size_t index, bool value_huffman, - const Http2String& value); + const std::string& value); HpackEntryCollector(HpackEntryType type, bool name_huffman, - const Http2String& name, + const std::string& name, bool value_huffman, - const Http2String& value); + const std::string& value); ~HpackEntryCollector() override; @@ -122,7 +122,7 @@ class HpackEntryCollector : public HpackEntryDecoderListener { void AppendToHpackBlockBuilder(HpackBlockBuilder* hbb) const; // Returns a debug string. - Http2String ToString() const; + std::string ToString() const; private: void Init(HpackEntryType type, size_t maybe_index); diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_decoder.cc b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_decoder.cc index 5e1bd8c7176..97af6f9c53c 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_decoder.cc +++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_decoder.cc @@ -237,7 +237,7 @@ void HpackEntryDecoder::OutputDebugString(std::ostream& out) const { << ", " << string_decoder_ << ")"; } -Http2String HpackEntryDecoder::DebugString() const { +std::string HpackEntryDecoder::DebugString() const { std::stringstream s; s << *this; return s.str(); diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_decoder.h b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_decoder.h index 5858c1e82e6..1848fb6813c 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_decoder.h +++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_decoder.h @@ -10,6 +10,8 @@ // must provide a non-empty decode buffer. Continue with calls to Resume() if // Start, and any subsequent calls to Resume, returns kDecodeInProgress. +#include <string> + #include "net/third_party/quiche/src/http2/decoder/decode_buffer.h" #include "net/third_party/quiche/src/http2/decoder/decode_status.h" #include "net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_decoder_listener.h" @@ -18,7 +20,6 @@ #include "net/third_party/quiche/src/http2/hpack/http2_hpack_constants.h" #include "net/third_party/quiche/src/http2/platform/api/http2_export.h" #include "net/third_party/quiche/src/http2/platform/api/http2_logging.h" -#include "net/third_party/quiche/src/http2/platform/api/http2_string.h" namespace http2 { @@ -62,7 +63,7 @@ class HTTP2_EXPORT_PRIVATE HpackEntryDecoder { // in decoding the entry type and its varint. DecodeStatus Resume(DecodeBuffer* db, HpackEntryDecoderListener* listener); - Http2String DebugString() const; + std::string DebugString() const; void OutputDebugString(std::ostream& out) const; private: diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_decoder_test.cc b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_decoder_test.cc index b447e829bbc..d4113f8004e 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_decoder_test.cc +++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_decoder_test.cc @@ -167,7 +167,7 @@ TEST_P(HpackLiteralEntryDecoderTest, RandNameIndexAndLiteralValue) { for (int n = 0; n < 10; n++) { const uint32_t ndx = 1 + Random().Rand8(); const bool value_is_huffman_encoded = (n % 2) == 0; - const Http2String value = Random().RandString(Random().Rand8()); + const std::string value = Random().RandString(Random().Rand8()); HpackBlockBuilder hbb; hbb.AppendNameIndexAndLiteralValue(entry_type_, ndx, value_is_huffman_encoded, value); @@ -186,10 +186,10 @@ TEST_P(HpackLiteralEntryDecoderTest, RandLiteralNameAndValue) { for (int n = 0; n < 10; n++) { const bool name_is_huffman_encoded = (n & 1) == 0; const int name_len = 1 + Random().Rand8(); - const Http2String name = Random().RandString(name_len); + const std::string name = Random().RandString(name_len); const bool value_is_huffman_encoded = (n & 2) == 0; const int value_len = Random().Skewed(10); - const Http2String value = Random().RandString(value_len); + const std::string value = Random().RandString(value_len); HpackBlockBuilder hbb; hbb.AppendLiteralNameAndValue(entry_type_, name_is_huffman_encoded, name, value_is_huffman_encoded, value); diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_type_decoder.cc b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_type_decoder.cc index 7519f5a07b8..ce91978fefb 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_type_decoder.cc +++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_type_decoder.cc @@ -10,7 +10,7 @@ namespace http2 { -Http2String HpackEntryTypeDecoder::DebugString() const { +std::string HpackEntryTypeDecoder::DebugString() const { return Http2StrCat( "HpackEntryTypeDecoder(varint_decoder=", varint_decoder_.DebugString(), ", entry_type=", entry_type_, ")"); diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_type_decoder.h b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_type_decoder.h index 092a8440f90..bf13cf94bc6 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_type_decoder.h +++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_type_decoder.h @@ -11,6 +11,7 @@ // zero, or is the new size limit of the dynamic table. #include <cstdint> +#include <string> #include "net/third_party/quiche/src/http2/decoder/decode_buffer.h" #include "net/third_party/quiche/src/http2/decoder/decode_status.h" @@ -18,7 +19,6 @@ #include "net/third_party/quiche/src/http2/hpack/varint/hpack_varint_decoder.h" #include "net/third_party/quiche/src/http2/platform/api/http2_export.h" #include "net/third_party/quiche/src/http2/platform/api/http2_logging.h" -#include "net/third_party/quiche/src/http2/platform/api/http2_string.h" namespace http2 { @@ -40,7 +40,7 @@ class HTTP2_EXPORT_PRIVATE HpackEntryTypeDecoder { // preceding call to Start or Resume returned kDecodeDone. uint64_t varint() const { return varint_decoder_.value(); } - Http2String DebugString() const; + std::string DebugString() const; private: HpackVarintDecoder varint_decoder_; diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_string_collector.cc b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_string_collector.cc index 247ce9c2a6a..e903755dc72 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_string_collector.cc +++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_string_collector.cc @@ -36,7 +36,7 @@ HpackStringCollector::HpackStringCollector() { Clear(); } -HpackStringCollector::HpackStringCollector(const Http2String& str, bool huffman) +HpackStringCollector::HpackStringCollector(const std::string& str, bool huffman) : s(str), len(str.size()), huffman_encoded(huffman), state(kEnded) {} void HpackStringCollector::Clear() { @@ -89,7 +89,7 @@ void HpackStringCollector::OnStringEnd() { return ::testing::AssertionSuccess(); } -Http2String HpackStringCollector::ToString() const { +std::string HpackStringCollector::ToString() const { std::stringstream ss; ss << *this; return ss.str(); diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_string_collector.h b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_string_collector.h index 76be13b743c..d56abee231e 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_string_collector.h +++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_string_collector.h @@ -10,10 +10,10 @@ #include <stddef.h> #include <iosfwd> +#include <string> #include "testing/gtest/include/gtest/gtest.h" #include "net/third_party/quiche/src/http2/hpack/decoder/hpack_string_decoder_listener.h" -#include "net/third_party/quiche/src/http2/platform/api/http2_string.h" #include "net/third_party/quiche/src/http2/platform/api/http2_string_piece.h" namespace http2 { @@ -29,7 +29,7 @@ struct HpackStringCollector : public HpackStringDecoderListener { }; HpackStringCollector(); - HpackStringCollector(const Http2String& str, bool huffman); + HpackStringCollector(const std::string& str, bool huffman); void Clear(); bool IsClear() const; @@ -43,9 +43,9 @@ struct HpackStringCollector : public HpackStringDecoderListener { ::testing::AssertionResult Collected(Http2StringPiece str, bool is_huffman_encoded) const; - Http2String ToString() const; + std::string ToString() const; - Http2String s; + std::string s; size_t len; bool huffman_encoded; CollectorState state; diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_string_decoder.cc b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_string_decoder.cc index 0b9eb596835..f74e536f8c1 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_string_decoder.cc +++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_string_decoder.cc @@ -8,7 +8,7 @@ namespace http2 { -Http2String HpackStringDecoder::DebugString() const { +std::string HpackStringDecoder::DebugString() const { return Http2StrCat("HpackStringDecoder(state=", StateToString(state_), ", length=", length_decoder_.DebugString(), ", remaining=", remaining_, @@ -16,7 +16,7 @@ Http2String HpackStringDecoder::DebugString() const { } // static -Http2String HpackStringDecoder::StateToString(StringDecoderState v) { +std::string HpackStringDecoder::StateToString(StringDecoderState v) { switch (v) { case kStartDecodingLength: return "kStartDecodingLength"; diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_string_decoder.h b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_string_decoder.h index e30b36dc0bd..0cd17423524 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_string_decoder.h +++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_string_decoder.h @@ -13,6 +13,7 @@ #include <algorithm> #include <cstdint> +#include <string> #include "net/third_party/quiche/src/http2/decoder/decode_buffer.h" #include "net/third_party/quiche/src/http2/decoder/decode_status.h" @@ -20,7 +21,6 @@ #include "net/third_party/quiche/src/http2/platform/api/http2_export.h" #include "net/third_party/quiche/src/http2/platform/api/http2_logging.h" #include "net/third_party/quiche/src/http2/platform/api/http2_macros.h" -#include "net/third_party/quiche/src/http2/platform/api/http2_string.h" namespace http2 { @@ -113,10 +113,10 @@ class HTTP2_EXPORT_PRIVATE HpackStringDecoder { } } - Http2String DebugString() const; + std::string DebugString() const; private: - static Http2String StateToString(StringDecoderState v); + static std::string StateToString(StringDecoderState v); // Returns true if the length is fully decoded and the listener wants the // decoding to continue, false otherwise; status is set to the status from diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_string_decoder_test.cc b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_string_decoder_test.cc index 53469998d14..ceeebcae48a 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_string_decoder_test.cc +++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_string_decoder_test.cc @@ -48,10 +48,10 @@ class HpackStringDecoderTest : public RandomDecoderTest { return collector_.Collected(s, huffman_encoded); } - // expected_str is a Http2String rather than a const Http2String& or + // expected_str is a std::string rather than a const std::string& or // Http2StringPiece so that the lambda makes a copy of the string, and thus // the string to be passed to Collected outlives the call to MakeValidator. - Validator MakeValidator(const Http2String& expected_str, + Validator MakeValidator(const std::string& expected_str, bool expected_huffman) { return [expected_str, expected_huffman, this]( @@ -119,8 +119,8 @@ TEST_F(HpackStringDecoderTest, DecodeShortString) { } TEST_F(HpackStringDecoderTest, DecodeLongStrings) { - Http2String name = Random().RandString(1024); - Http2String value = Random().RandString(65536); + std::string name = Random().RandString(1024); + std::string value = Random().RandString(65536); HpackBlockBuilder hbb; hbb.AppendString(false, name); diff --git a/chromium/net/third_party/quiche/src/http2/hpack/hpack_string.cc b/chromium/net/third_party/quiche/src/http2/hpack/hpack_string.cc index 957cffa62d7..85ba812f7f3 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/hpack_string.cc +++ b/chromium/net/third_party/quiche/src/http2/hpack/hpack_string.cc @@ -12,8 +12,8 @@ namespace http2 { HpackString::HpackString(const char* data) : str_(data) {} -HpackString::HpackString(Http2StringPiece str) : str_(Http2String(str)) {} -HpackString::HpackString(Http2String str) : str_(std::move(str)) {} +HpackString::HpackString(Http2StringPiece str) : str_(std::string(str)) {} +HpackString::HpackString(std::string str) : str_(std::move(str)) {} HpackString::HpackString(const HpackString& other) = default; HpackString::~HpackString() = default; @@ -59,7 +59,7 @@ HpackStringPair::~HpackStringPair() { HTTP2_DVLOG(3) << DebugString() << " dtor"; } -Http2String HpackStringPair::DebugString() const { +std::string HpackStringPair::DebugString() const { return Http2StrCat("HpackStringPair(name=", name.ToString(), ", value=", value.ToString(), ")"); } diff --git a/chromium/net/third_party/quiche/src/http2/hpack/hpack_string.h b/chromium/net/third_party/quiche/src/http2/hpack/hpack_string.h index b1c499c95be..e1535f7c114 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/hpack_string.h +++ b/chromium/net/third_party/quiche/src/http2/hpack/hpack_string.h @@ -13,9 +13,9 @@ #include <stddef.h> #include <iosfwd> +#include <string> #include "net/third_party/quiche/src/http2/platform/api/http2_export.h" -#include "net/third_party/quiche/src/http2/platform/api/http2_string.h" #include "net/third_party/quiche/src/http2/platform/api/http2_string_piece.h" namespace http2 { @@ -24,7 +24,7 @@ class HTTP2_EXPORT_PRIVATE HpackString { public: explicit HpackString(const char* data); explicit HpackString(Http2StringPiece str); - explicit HpackString(Http2String str); + explicit HpackString(std::string str); HpackString(const HpackString& other); // Not sure yet whether this move ctor is required/sensible. @@ -33,7 +33,7 @@ class HTTP2_EXPORT_PRIVATE HpackString { ~HpackString(); size_t size() const { return str_.size(); } - const Http2String& ToString() const { return str_; } + const std::string& ToString() const { return str_; } Http2StringPiece ToStringPiece() const; bool operator==(const HpackString& other) const; @@ -41,7 +41,7 @@ class HTTP2_EXPORT_PRIVATE HpackString { bool operator==(Http2StringPiece str) const; private: - Http2String str_; + std::string str_; }; HTTP2_EXPORT_PRIVATE bool operator==(Http2StringPiece a, const HpackString& b); @@ -61,7 +61,7 @@ struct HTTP2_EXPORT_PRIVATE HpackStringPair { // http://httpwg.org/specs/rfc7541.html#calculating.table.size size_t size() const { return 32 + name.size() + value.size(); } - Http2String DebugString() const; + std::string DebugString() const; const HpackString name; const HpackString value; diff --git a/chromium/net/third_party/quiche/src/http2/hpack/hpack_string_test.cc b/chromium/net/third_party/quiche/src/http2/hpack/hpack_string_test.cc index 898ef8d1fd1..9230ecc0e14 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/hpack_string_test.cc +++ b/chromium/net/third_party/quiche/src/http2/hpack/hpack_string_test.cc @@ -26,7 +26,7 @@ const char kStr1[] = "S1 - some string to be copied into yet another string."; class HpackStringTest : public ::testing::Test { protected: AssertionResult VerifyNotEqual(HpackString* actual, - const Http2String& not_expected_str) { + const std::string& not_expected_str) { const char* not_expected_ptr = not_expected_str.c_str(); Http2StringPiece not_expected_sp(not_expected_str); @@ -52,7 +52,7 @@ class HpackStringTest : public ::testing::Test { } AssertionResult VerifyEqual(HpackString* actual, - const Http2String& expected_str) { + const std::string& expected_str) { VERIFY_EQ(actual->size(), expected_str.size()); const char* expected_ptr = expected_str.c_str(); @@ -103,12 +103,12 @@ TEST_F(HpackStringTest, StringPieceConstructor) { } TEST_F(HpackStringTest, MoveStringConstructor) { - Http2String str0(kStr0); + std::string str0(kStr0); HpackString hs0(str0); EXPECT_TRUE(VerifyEqual(&hs0, kStr0)); EXPECT_TRUE(VerifyNotEqual(&hs0, kStr1)); - Http2String str1(kStr1); + std::string str1(kStr1); HpackString hs1(str1); EXPECT_TRUE(VerifyEqual(&hs1, kStr1)); EXPECT_TRUE(VerifyNotEqual(&hs1, kStr0)); diff --git a/chromium/net/third_party/quiche/src/http2/hpack/http2_hpack_constants.cc b/chromium/net/third_party/quiche/src/http2/hpack/http2_hpack_constants.cc index f258ab9bcbd..d697db03d37 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/http2_hpack_constants.cc +++ b/chromium/net/third_party/quiche/src/http2/hpack/http2_hpack_constants.cc @@ -8,7 +8,7 @@ namespace http2 { -Http2String HpackEntryTypeToString(HpackEntryType v) { +std::string HpackEntryTypeToString(HpackEntryType v) { switch (v) { case HpackEntryType::kIndexedHeader: return "kIndexedHeader"; diff --git a/chromium/net/third_party/quiche/src/http2/hpack/http2_hpack_constants.h b/chromium/net/third_party/quiche/src/http2/hpack/http2_hpack_constants.h index de0d685595d..c9737749a7c 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/http2_hpack_constants.h +++ b/chromium/net/third_party/quiche/src/http2/hpack/http2_hpack_constants.h @@ -11,9 +11,9 @@ // https://http2.github.io/http2-spec/compression.html#rfc.section.6 #include <ostream> +#include <string> #include "net/third_party/quiche/src/http2/platform/api/http2_export.h" -#include "net/third_party/quiche/src/http2/platform/api/http2_string.h" namespace http2 { @@ -52,7 +52,7 @@ enum class HpackEntryType { }; // Returns the name of the enum member. -HTTP2_EXPORT_PRIVATE Http2String HpackEntryTypeToString(HpackEntryType v); +HTTP2_EXPORT_PRIVATE std::string HpackEntryTypeToString(HpackEntryType v); // Inserts the name of the enum member into |out|. HTTP2_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& out, diff --git a/chromium/net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_decoder.cc b/chromium/net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_decoder.cc index e74ca143403..71ce8558159 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_decoder.cc +++ b/chromium/net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_decoder.cc @@ -403,7 +403,7 @@ bool HuffmanBitBuffer::InputProperlyTerminated() const { return false; } -Http2String HuffmanBitBuffer::DebugString() const { +std::string HuffmanBitBuffer::DebugString() const { std::stringstream ss; ss << "{accumulator: " << HuffmanAccumulatorBitSet(accumulator_) << "; count: " << count_ << "}"; @@ -414,7 +414,7 @@ HpackHuffmanDecoder::HpackHuffmanDecoder() = default; HpackHuffmanDecoder::~HpackHuffmanDecoder() = default; -bool HpackHuffmanDecoder::Decode(Http2StringPiece input, Http2String* output) { +bool HpackHuffmanDecoder::Decode(Http2StringPiece input, std::string* output) { HTTP2_DVLOG(1) << "HpackHuffmanDecoder::Decode"; // Fill bit_buffer_ from input. @@ -480,7 +480,7 @@ bool HpackHuffmanDecoder::Decode(Http2StringPiece input, Http2String* output) { } } -Http2String HpackHuffmanDecoder::DebugString() const { +std::string HpackHuffmanDecoder::DebugString() const { return bit_buffer_.DebugString(); } diff --git a/chromium/net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_decoder.h b/chromium/net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_decoder.h index 8e511d62ae9..065fe853524 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_decoder.h +++ b/chromium/net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_decoder.h @@ -16,9 +16,9 @@ #include <cstdint> #include <iosfwd> +#include <string> #include "net/third_party/quiche/src/http2/platform/api/http2_export.h" -#include "net/third_party/quiche/src/http2/platform/api/http2_string.h" #include "net/third_party/quiche/src/http2/platform/api/http2_string_piece.h" namespace http2 { @@ -74,7 +74,7 @@ class HTTP2_EXPORT_PRIVATE HuffmanBitBuffer { // of them 1. Otherwise returns false. bool InputProperlyTerminated() const; - Http2String DebugString() const; + std::string DebugString() const; private: HuffmanAccumulator accumulator_; @@ -109,7 +109,7 @@ class HTTP2_EXPORT_PRIVATE HpackHuffmanDecoder { // will contain the leading bits of the code for that symbol, but not the // final bits of that code. // Note that output should be empty, but that it is not cleared by Decode(). - bool Decode(Http2StringPiece input, Http2String* output); + bool Decode(Http2StringPiece input, std::string* output); // Is what remains in the bit_buffer_ valid at the end of an encoded string? // Call after passing the the final portion of a Huffman string to Decode, @@ -118,7 +118,7 @@ class HTTP2_EXPORT_PRIVATE HpackHuffmanDecoder { return bit_buffer_.InputProperlyTerminated(); } - Http2String DebugString() const; + std::string DebugString() const; private: HuffmanBitBuffer bit_buffer_; diff --git a/chromium/net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_decoder_test.cc b/chromium/net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_decoder_test.cc index 30b586fd2a6..f1bed3ce6d4 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_decoder_test.cc +++ b/chromium/net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_decoder_test.cc @@ -32,7 +32,7 @@ TEST(HuffmanBitBufferTest, Reset) { } TEST(HuffmanBitBufferTest, AppendBytesAligned) { - Http2String s; + std::string s; s.push_back('\x11'); s.push_back('\x22'); s.push_back('\x33'); @@ -81,7 +81,7 @@ TEST(HuffmanBitBufferTest, AppendBytesAligned) { } TEST(HuffmanBitBufferTest, ConsumeBits) { - Http2String s; + std::string s; s.push_back('\x11'); s.push_back('\x22'); s.push_back('\x33'); @@ -103,7 +103,7 @@ TEST(HuffmanBitBufferTest, ConsumeBits) { } TEST(HuffmanBitBufferTest, AppendBytesUnaligned) { - Http2String s; + std::string s; s.push_back('\x11'); s.push_back('\x22'); s.push_back('\x33'); @@ -180,14 +180,14 @@ class HpackHuffmanDecoderTest : public RandomDecoderTest { } HpackHuffmanDecoder decoder_; - Http2String output_buffer_; + std::string output_buffer_; size_t input_bytes_seen_; size_t input_bytes_expected_; }; TEST_F(HpackHuffmanDecoderTest, SpecRequestExamples) { HpackHuffmanDecoder decoder; - Http2String test_table[] = { + std::string test_table[] = { Http2HexDecode("f1e3c2e5f23a6ba0ab90f4ff"), "www.example.com", Http2HexDecode("a8eb10649cbf"), @@ -198,9 +198,9 @@ TEST_F(HpackHuffmanDecoderTest, SpecRequestExamples) { "custom-value", }; for (size_t i = 0; i != HTTP2_ARRAYSIZE(test_table); i += 2) { - const Http2String& huffman_encoded(test_table[i]); - const Http2String& plain_string(test_table[i + 1]); - Http2String buffer; + const std::string& huffman_encoded(test_table[i]); + const std::string& plain_string(test_table[i + 1]); + std::string buffer; decoder.Reset(); EXPECT_TRUE(decoder.Decode(huffman_encoded, &buffer)) << decoder; EXPECT_TRUE(decoder.InputProperlyTerminated()) << decoder; @@ -211,7 +211,7 @@ TEST_F(HpackHuffmanDecoderTest, SpecRequestExamples) { TEST_F(HpackHuffmanDecoderTest, SpecResponseExamples) { HpackHuffmanDecoder decoder; // clang-format off - Http2String test_table[] = { + std::string test_table[] = { Http2HexDecode("6402"), "302", Http2HexDecode("aec3771a4b"), @@ -229,9 +229,9 @@ TEST_F(HpackHuffmanDecoderTest, SpecResponseExamples) { }; // clang-format on for (size_t i = 0; i != HTTP2_ARRAYSIZE(test_table); i += 2) { - const Http2String& huffman_encoded(test_table[i]); - const Http2String& plain_string(test_table[i + 1]); - Http2String buffer; + const std::string& huffman_encoded(test_table[i]); + const std::string& plain_string(test_table[i + 1]); + std::string buffer; decoder.Reset(); EXPECT_TRUE(decoder.Decode(huffman_encoded, &buffer)) << decoder; EXPECT_TRUE(decoder.InputProperlyTerminated()) << decoder; diff --git a/chromium/net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_encoder.cc b/chromium/net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_encoder.cc index a92a4c0ed7c..8a5dee9698e 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_encoder.cc +++ b/chromium/net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_encoder.cc @@ -61,7 +61,7 @@ size_t BoundedHuffmanSize(Http2StringPiece plain) { return (bits + 7) / 8; } -void HuffmanEncode(Http2StringPiece plain, Http2String* huffman) { +void HuffmanEncode(Http2StringPiece plain, std::string* huffman) { DCHECK(huffman != nullptr); huffman->clear(); // Note that this doesn't release memory. uint64_t bit_buffer = 0; // High-bit is next bit to output. Not clear if that diff --git a/chromium/net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_encoder.h b/chromium/net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_encoder.h index bf6b1b23b34..247aaff6ba7 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_encoder.h +++ b/chromium/net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_encoder.h @@ -9,9 +9,9 @@ // table. #include <cstddef> // For size_t +#include <string> #include "net/third_party/quiche/src/http2/platform/api/http2_export.h" -#include "net/third_party/quiche/src/http2/platform/api/http2_string.h" #include "net/third_party/quiche/src/http2/platform/api/http2_string_piece.h" namespace http2 { @@ -33,7 +33,7 @@ HTTP2_EXPORT_PRIVATE size_t BoundedHuffmanSize(Http2StringPiece plain); // the beginning of this function. This allows reusing the same string object // across multiple invocations. HTTP2_EXPORT_PRIVATE void HuffmanEncode(Http2StringPiece plain, - Http2String* huffman); + std::string* huffman); } // namespace http2 diff --git a/chromium/net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_encoder_test.cc b/chromium/net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_encoder_test.cc index 2a8999fd3a8..24d3cf12f19 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_encoder_test.cc +++ b/chromium/net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_encoder_test.cc @@ -12,7 +12,7 @@ namespace http2 { namespace { TEST(HuffmanEncoderTest, SpecRequestExamples) { - Http2String test_table[] = { + std::string test_table[] = { Http2HexDecode("f1e3c2e5f23a6ba0ab90f4ff"), "www.example.com", Http2HexDecode("a8eb10649cbf"), @@ -23,11 +23,11 @@ TEST(HuffmanEncoderTest, SpecRequestExamples) { "custom-value", }; for (size_t i = 0; i != HTTP2_ARRAYSIZE(test_table); i += 2) { - const Http2String& huffman_encoded(test_table[i]); - const Http2String& plain_string(test_table[i + 1]); + const std::string& huffman_encoded(test_table[i]); + const std::string& plain_string(test_table[i + 1]); EXPECT_EQ(ExactHuffmanSize(plain_string), huffman_encoded.size()); EXPECT_EQ(BoundedHuffmanSize(plain_string), huffman_encoded.size()); - Http2String buffer; + std::string buffer; buffer.reserve(); HuffmanEncode(plain_string, &buffer); EXPECT_EQ(buffer, huffman_encoded) << "Error encoding " << plain_string; @@ -36,7 +36,7 @@ TEST(HuffmanEncoderTest, SpecRequestExamples) { TEST(HuffmanEncoderTest, SpecResponseExamples) { // clang-format off - Http2String test_table[] = { + std::string test_table[] = { Http2HexDecode("6402"), "302", Http2HexDecode("aec3771a4b"), @@ -54,11 +54,11 @@ TEST(HuffmanEncoderTest, SpecResponseExamples) { }; // clang-format on for (size_t i = 0; i != HTTP2_ARRAYSIZE(test_table); i += 2) { - const Http2String& huffman_encoded(test_table[i]); - const Http2String& plain_string(test_table[i + 1]); + const std::string& huffman_encoded(test_table[i]); + const std::string& plain_string(test_table[i + 1]); EXPECT_EQ(ExactHuffmanSize(plain_string), huffman_encoded.size()); EXPECT_EQ(BoundedHuffmanSize(plain_string), huffman_encoded.size()); - Http2String buffer; + std::string buffer; buffer.reserve(huffman_encoded.size()); const size_t capacity = buffer.capacity(); HuffmanEncode(plain_string, &buffer); @@ -68,14 +68,14 @@ TEST(HuffmanEncoderTest, SpecResponseExamples) { } TEST(HuffmanEncoderTest, EncodedSizeAgreesWithEncodeString) { - Http2String test_table[] = { + std::string test_table[] = { "", "Mon, 21 Oct 2013 20:13:21 GMT", "https://www.example.com", "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1", - Http2String(1, '\0'), - Http2String("foo\0bar", 7), - Http2String(256, '\0'), + std::string(1, '\0'), + std::string("foo\0bar", 7), + std::string(256, '\0'), }; // Modify last |test_table| entry to cover all codes. for (size_t i = 0; i != 256; ++i) { @@ -83,8 +83,8 @@ TEST(HuffmanEncoderTest, EncodedSizeAgreesWithEncodeString) { } for (size_t i = 0; i != HTTP2_ARRAYSIZE(test_table); ++i) { - const Http2String& plain_string = test_table[i]; - Http2String huffman_encoded; + const std::string& plain_string = test_table[i]; + std::string huffman_encoded; HuffmanEncode(plain_string, &huffman_encoded); EXPECT_EQ(huffman_encoded.size(), ExactHuffmanSize(plain_string)); EXPECT_LE(BoundedHuffmanSize(plain_string), plain_string.size()); diff --git a/chromium/net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_transcoder_test.cc b/chromium/net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_transcoder_test.cc index 2791d6a14a7..9781094dfb8 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_transcoder_test.cc +++ b/chromium/net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_transcoder_test.cc @@ -11,7 +11,6 @@ #include "net/third_party/quiche/src/http2/decoder/decode_status.h" #include "net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_decoder.h" #include "net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_encoder.h" -#include "net/third_party/quiche/src/http2/platform/api/http2_string.h" #include "net/third_party/quiche/src/http2/platform/api/http2_string_piece.h" #include "net/third_party/quiche/src/http2/platform/api/http2_string_utils.h" #include "net/third_party/quiche/src/http2/tools/random_decoder_test.h" @@ -24,8 +23,8 @@ namespace http2 { namespace test { namespace { -Http2String GenAsciiNonControlSet() { - Http2String s; +std::string GenAsciiNonControlSet() { + std::string s; const char space = ' '; // First character after the control characters: 0x20 const char del = 127; // First character after the non-control characters. for (char c = space; c < del; ++c) { @@ -74,7 +73,7 @@ class HpackHuffmanTranscoderTest : public RandomDecoderTest { AssertionResult TranscodeAndValidateSeveralWays( Http2StringPiece plain, Http2StringPiece expected_huffman) { - Http2String encoded; + std::string encoded; HuffmanEncode(plain, &encoded); if (expected_huffman.size() > 0 || plain.empty()) { VERIFY_EQ(encoded, expected_huffman); @@ -95,22 +94,22 @@ class HpackHuffmanTranscoderTest : public RandomDecoderTest { return TranscodeAndValidateSeveralWays(plain, ""); } - Http2String RandomAsciiNonControlString(int length) { + std::string RandomAsciiNonControlString(int length) { return Random().RandStringWithAlphabet(length, ascii_non_control_set_); } - Http2String RandomBytes(int length) { return Random().RandString(length); } + std::string RandomBytes(int length) { return Random().RandString(length); } - const Http2String ascii_non_control_set_; + const std::string ascii_non_control_set_; HpackHuffmanDecoder decoder_; - Http2String output_buffer_; + std::string output_buffer_; size_t input_bytes_seen_; size_t input_bytes_expected_; }; TEST_F(HpackHuffmanTranscoderTest, RoundTripRandomAsciiNonControlString) { for (size_t length = 0; length != 20; length++) { - const Http2String s = RandomAsciiNonControlString(length); + const std::string s = RandomAsciiNonControlString(length); ASSERT_TRUE(TranscodeAndValidateSeveralWays(s)) << "Unable to decode:\n\n" << Http2HexDump(s) << "\n\noutput_buffer_:\n" @@ -120,7 +119,7 @@ TEST_F(HpackHuffmanTranscoderTest, RoundTripRandomAsciiNonControlString) { TEST_F(HpackHuffmanTranscoderTest, RoundTripRandomBytes) { for (size_t length = 0; length != 20; length++) { - const Http2String s = RandomBytes(length); + const std::string s = RandomBytes(length); ASSERT_TRUE(TranscodeAndValidateSeveralWays(s)) << "Unable to decode:\n\n" << Http2HexDump(s) << "\n\noutput_buffer_:\n" @@ -145,7 +144,7 @@ INSTANTIATE_TEST_SUITE_P(HpackHuffmanTranscoderAdjacentCharTest, // Test c_ adjacent to every other character, both before and after. TEST_P(HpackHuffmanTranscoderAdjacentCharTest, RoundTripAdjacentChar) { - Http2String s; + std::string s; for (int a = 0; a < 256; ++a) { s.push_back(static_cast<char>(a)); s.push_back(c_); @@ -162,7 +161,7 @@ class HpackHuffmanTranscoderRepeatedCharTest HpackHuffmanTranscoderRepeatedCharTest() : c_(static_cast<char>(::testing::get<0>(GetParam()))), length_(::testing::get<1>(GetParam())) {} - Http2String MakeString() { return Http2String(length_, c_); } + std::string MakeString() { return std::string(length_, c_); } private: const char c_; diff --git a/chromium/net/third_party/quiche/src/http2/hpack/tools/hpack_block_builder.h b/chromium/net/third_party/quiche/src/http2/hpack/tools/hpack_block_builder.h index 560953cc341..59c3805bcb6 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/tools/hpack_block_builder.h +++ b/chromium/net/third_party/quiche/src/http2/hpack/tools/hpack_block_builder.h @@ -18,10 +18,10 @@ #include <stddef.h> #include <cstdint> +#include <string> #include "testing/gtest/include/gtest/gtest.h" #include "net/third_party/quiche/src/http2/hpack/http2_hpack_constants.h" -#include "net/third_party/quiche/src/http2/platform/api/http2_string.h" #include "net/third_party/quiche/src/http2/platform/api/http2_string_piece.h" namespace http2 { @@ -35,7 +35,7 @@ class HpackBlockBuilder { ~HpackBlockBuilder() {} size_t size() const { return buffer_.size(); } - const Http2String& buffer() const { return buffer_; } + const std::string& buffer() const { return buffer_; } //---------------------------------------------------------------------------- // Methods for appending a valid HPACK entry. @@ -87,7 +87,7 @@ class HpackBlockBuilder { void AppendString(bool is_huffman_encoded, Http2StringPiece str); private: - Http2String buffer_; + std::string buffer_; }; } // namespace test diff --git a/chromium/net/third_party/quiche/src/http2/hpack/tools/hpack_block_builder_test.cc b/chromium/net/third_party/quiche/src/http2/hpack/tools/hpack_block_builder_test.cc index a7b80647979..bba363f2d2a 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/tools/hpack_block_builder_test.cc +++ b/chromium/net/third_party/quiche/src/http2/hpack/tools/hpack_block_builder_test.cc @@ -96,7 +96,7 @@ TEST(HpackBlockBuilderTest, ExamplesFromSpecC3) { // 0x0000: 8286 8441 0f77 7777 2e65 7861 6d70 6c65 ...A.www.example // 0x0010: 2e63 6f6d .com - const Http2String expected = + const std::string expected = Http2HexDecode("828684410f7777772e6578616d706c652e636f6d"); EXPECT_EQ(expected, b.buffer()); } @@ -127,7 +127,7 @@ TEST(HpackBlockBuilderTest, ExamplesFromSpecC4) { // 0x0000: 8286 8441 8cf1 e3c2 e5f2 3a6b a0ab 90f4 ...A......:k.... // 0x0010: ff . - const Http2String expected = + const std::string expected = Http2HexDecode("828684418cf1e3c2e5f23a6ba0ab90f4ff"); EXPECT_EQ(expected, b.buffer()); } diff --git a/chromium/net/third_party/quiche/src/http2/hpack/tools/hpack_example.cc b/chromium/net/third_party/quiche/src/http2/hpack/tools/hpack_example.cc index d7b8c7c6739..52d84f9747e 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/tools/hpack_example.cc +++ b/chromium/net/third_party/quiche/src/http2/hpack/tools/hpack_example.cc @@ -14,7 +14,7 @@ namespace http2 { namespace test { namespace { -void HpackExampleToStringOrDie(Http2StringPiece example, Http2String* output) { +void HpackExampleToStringOrDie(Http2StringPiece example, std::string* output) { while (!example.empty()) { const char c0 = example[0]; if (isxdigit(c0)) { @@ -48,8 +48,8 @@ void HpackExampleToStringOrDie(Http2StringPiece example, Http2String* output) { } // namespace -Http2String HpackExampleToStringOrDie(Http2StringPiece example) { - Http2String output; +std::string HpackExampleToStringOrDie(Http2StringPiece example) { + std::string output; HpackExampleToStringOrDie(example, &output); return output; } diff --git a/chromium/net/third_party/quiche/src/http2/hpack/tools/hpack_example.h b/chromium/net/third_party/quiche/src/http2/hpack/tools/hpack_example.h index ddb22a32534..0371e17abbe 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/tools/hpack_example.h +++ b/chromium/net/third_party/quiche/src/http2/hpack/tools/hpack_example.h @@ -5,7 +5,8 @@ #ifndef QUICHE_HTTP2_HPACK_TOOLS_HPACK_EXAMPLE_H_ #define QUICHE_HTTP2_HPACK_TOOLS_HPACK_EXAMPLE_H_ -#include "net/third_party/quiche/src/http2/platform/api/http2_string.h" +#include <string> + #include "net/third_party/quiche/src/http2/platform/api/http2_string_piece.h" // Parses HPACK examples in the format seen in the HPACK specification, @@ -23,7 +24,7 @@ namespace http2 { namespace test { -Http2String HpackExampleToStringOrDie(Http2StringPiece example); +std::string HpackExampleToStringOrDie(Http2StringPiece example); } // namespace test } // namespace http2 diff --git a/chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_decoder.cc b/chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_decoder.cc index 9741a23cbdd..e7e2c9c19ca 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_decoder.cc +++ b/chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_decoder.cc @@ -120,7 +120,7 @@ void HpackVarintDecoder::set_value(uint64_t v) { value_ = v; } -Http2String HpackVarintDecoder::DebugString() const { +std::string HpackVarintDecoder::DebugString() const { return Http2StrCat("HpackVarintDecoder(value=", value_, ", offset=", offset_, ")"); } diff --git a/chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_decoder.h b/chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_decoder.h index 855ced449ab..45764468b77 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_decoder.h +++ b/chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_decoder.h @@ -29,12 +29,12 @@ #include <cstdint> #include <limits> +#include <string> #include "net/third_party/quiche/src/http2/decoder/decode_buffer.h" #include "net/third_party/quiche/src/http2/decoder/decode_status.h" #include "net/third_party/quiche/src/http2/platform/api/http2_export.h" #include "net/third_party/quiche/src/http2/platform/api/http2_logging.h" -#include "net/third_party/quiche/src/http2/platform/api/http2_string.h" namespace http2 { @@ -83,7 +83,7 @@ class HTTP2_EXPORT_PRIVATE HpackVarintDecoder { // All the public methods below are for supporting assertions and tests. - Http2String DebugString() const; + std::string DebugString() const; // For benchmarking, these methods ensure the decoder // is NOT inlined into the caller. diff --git a/chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_decoder_test.cc b/chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_decoder_test.cc index 16101262df8..07cb51b87b4 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_decoder_test.cc +++ b/chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_decoder_test.cc @@ -69,7 +69,7 @@ class HpackVarintDecoderTest : public RandomDecoderTest, prefix_length_ = prefix_length; // Copy |data| so that it can be modified. - Http2String data_copy(data); + std::string data_copy(data); // Bits of the first byte not part of the prefix should be ignored. uint8_t high_bits_mask = 0b11111111 << prefix_length_; @@ -101,7 +101,7 @@ class HpackVarintDecoderTest : public RandomDecoderTest, // Bits of the first byte not part of the prefix. const uint8_t high_bits_; // Extra bytes appended to the input. - const Http2String suffix_; + const std::string suffix_; HpackVarintDecoder decoder_; uint8_t prefix_length_; diff --git a/chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_encoder.cc b/chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_encoder.cc index 63cf2897839..11ebf6ae748 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_encoder.cc +++ b/chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_encoder.cc @@ -14,7 +14,7 @@ namespace http2 { void HpackVarintEncoder::Encode(uint8_t high_bits, uint8_t prefix_length, uint64_t varint, - Http2String* output) { + std::string* output) { DCHECK_LE(1u, prefix_length); DCHECK_LE(prefix_length, 8u); diff --git a/chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_encoder.h b/chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_encoder.h index 745222e9566..014380d76f4 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_encoder.h +++ b/chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_encoder.h @@ -7,9 +7,9 @@ #include <cstddef> #include <cstdint> +#include <string> #include "net/third_party/quiche/src/http2/platform/api/http2_export.h" -#include "net/third_party/quiche/src/http2/platform/api/http2_string.h" namespace http2 { @@ -23,7 +23,7 @@ class HTTP2_EXPORT_PRIVATE HpackVarintEncoder { static void Encode(uint8_t high_bits, uint8_t prefix_length, uint64_t varint, - Http2String* output); + std::string* output); }; } // namespace http2 diff --git a/chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_encoder_test.cc b/chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_encoder_test.cc index 94f9a9e5935..23c19c41603 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_encoder_test.cc +++ b/chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_encoder_test.cc @@ -31,7 +31,7 @@ struct { // Encode integers that fit in the prefix. TEST(HpackVarintEncoderTest, Short) { for (size_t i = 0; i < HTTP2_ARRAYSIZE(kShortTestData); ++i) { - Http2String output; + std::string output; HpackVarintEncoder::Encode(kShortTestData[i].high_bits, kShortTestData[i].prefix_length, kShortTestData[i].value, &output); @@ -104,10 +104,10 @@ TEST(HpackVarintEncoderTest, Long) { // Test encoding byte by byte, also test encoding in // a single ResumeEncoding() call. for (size_t i = 0; i < HTTP2_ARRAYSIZE(kLongTestData); ++i) { - Http2String expected_encoding = + std::string expected_encoding = Http2HexDecode(kLongTestData[i].expected_encoding); - Http2String output; + std::string output; HpackVarintEncoder::Encode(kLongTestData[i].high_bits, kLongTestData[i].prefix_length, kLongTestData[i].value, &output); @@ -131,7 +131,7 @@ struct { // happens exactly when encoding the value 2^prefix_length - 1. TEST(HpackVarintEncoderTest, LastByteIsZero) { for (size_t i = 0; i < HTTP2_ARRAYSIZE(kLastByteIsZeroTestData); ++i) { - Http2String output; + std::string output; HpackVarintEncoder::Encode(kLastByteIsZeroTestData[i].high_bits, kLastByteIsZeroTestData[i].prefix_length, kLastByteIsZeroTestData[i].value, &output); @@ -144,7 +144,7 @@ TEST(HpackVarintEncoderTest, LastByteIsZero) { // Test that encoder appends correctly to non-empty string. TEST(HpackVarintEncoderTest, Append) { - Http2String output("foo"); + std::string output("foo"); EXPECT_EQ(Http2HexDecode("666f6f"), output); HpackVarintEncoder::Encode(0b10011000, 3, 103, &output); diff --git a/chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_round_trip_test.cc b/chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_round_trip_test.cc index 594317b3866..a5318fb9949 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_round_trip_test.cc +++ b/chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_round_trip_test.cc @@ -161,7 +161,7 @@ class HpackVarintRoundTripTest : public RandomDecoderTest { for (const uint64_t value : values) { Encode(value, prefix_length); // Sets buffer_. - Http2String msg = Http2StrCat("value=", value, " (0x", Http2Hex(value), + std::string msg = Http2StrCat("value=", value, " (0x", Http2Hex(value), "), prefix_length=", prefix_length, ", expected_bytes=", expected_bytes, "\n", Http2HexDump(buffer_)); @@ -241,7 +241,7 @@ class HpackVarintRoundTripTest : public RandomDecoderTest { } HpackVarintDecoder decoder_; - Http2String buffer_; + std::string buffer_; uint8_t prefix_length_; }; @@ -283,7 +283,7 @@ TEST_F(HpackVarintRoundTripTest, Encode) { for (uint64_t value : values) { EncodeNoRandom(value, prefix_length); - Http2String dump = Http2HexDump(buffer_); + std::string dump = Http2HexDump(buffer_); HTTP2_LOG(INFO) << Http2StringPrintf("%10llu %0#18x ", value, value) << Http2HexDump(buffer_).substr(7); } diff --git a/chromium/net/third_party/quiche/src/http2/http2_constants.cc b/chromium/net/third_party/quiche/src/http2/http2_constants.cc index ad15b69da90..38f8ab58cf9 100644 --- a/chromium/net/third_party/quiche/src/http2/http2_constants.cc +++ b/chromium/net/third_party/quiche/src/http2/http2_constants.cc @@ -10,7 +10,7 @@ namespace http2 { -Http2String Http2FrameTypeToString(Http2FrameType v) { +std::string Http2FrameTypeToString(Http2FrameType v) { switch (v) { case Http2FrameType::DATA: return "DATA"; @@ -38,13 +38,13 @@ Http2String Http2FrameTypeToString(Http2FrameType v) { return Http2StrCat("UnknownFrameType(", static_cast<int>(v), ")"); } -Http2String Http2FrameTypeToString(uint8_t v) { +std::string Http2FrameTypeToString(uint8_t v) { return Http2FrameTypeToString(static_cast<Http2FrameType>(v)); } -Http2String Http2FrameFlagsToString(Http2FrameType type, uint8_t flags) { - Http2String s; - // Closure to append flag name |v| to the Http2String |s|, +std::string Http2FrameFlagsToString(Http2FrameType type, uint8_t flags) { + std::string s; + // Closure to append flag name |v| to the std::string |s|, // and to clear |bit| from |flags|. auto append_and_clear = [&s, &flags](Http2StringPiece v, uint8_t bit) { if (!s.empty()) { @@ -85,11 +85,11 @@ Http2String Http2FrameFlagsToString(Http2FrameType type, uint8_t flags) { DCHECK_EQ(0, flags); return s; } -Http2String Http2FrameFlagsToString(uint8_t type, uint8_t flags) { +std::string Http2FrameFlagsToString(uint8_t type, uint8_t flags) { return Http2FrameFlagsToString(static_cast<Http2FrameType>(type), flags); } -Http2String Http2ErrorCodeToString(uint32_t v) { +std::string Http2ErrorCodeToString(uint32_t v) { switch (v) { case 0x0: return "NO_ERROR"; @@ -122,11 +122,11 @@ Http2String Http2ErrorCodeToString(uint32_t v) { } return Http2StrCat("UnknownErrorCode(0x", Http2Hex(v), ")"); } -Http2String Http2ErrorCodeToString(Http2ErrorCode v) { +std::string Http2ErrorCodeToString(Http2ErrorCode v) { return Http2ErrorCodeToString(static_cast<uint32_t>(v)); } -Http2String Http2SettingsParameterToString(uint32_t v) { +std::string Http2SettingsParameterToString(uint32_t v) { switch (v) { case 0x1: return "HEADER_TABLE_SIZE"; @@ -143,7 +143,7 @@ Http2String Http2SettingsParameterToString(uint32_t v) { } return Http2StrCat("UnknownSettingsParameter(0x", Http2Hex(v), ")"); } -Http2String Http2SettingsParameterToString(Http2SettingsParameter v) { +std::string Http2SettingsParameterToString(Http2SettingsParameter v) { return Http2SettingsParameterToString(static_cast<uint32_t>(v)); } diff --git a/chromium/net/third_party/quiche/src/http2/http2_constants.h b/chromium/net/third_party/quiche/src/http2/http2_constants.h index 93920069c5a..75f294e6672 100644 --- a/chromium/net/third_party/quiche/src/http2/http2_constants.h +++ b/chromium/net/third_party/quiche/src/http2/http2_constants.h @@ -10,9 +10,9 @@ #include <cstdint> #include <iosfwd> #include <ostream> +#include <string> #include "net/third_party/quiche/src/http2/platform/api/http2_export.h" -#include "net/third_party/quiche/src/http2/platform/api/http2_string.h" namespace http2 { @@ -52,12 +52,12 @@ inline bool IsSupportedHttp2FrameType(Http2FrameType v) { return IsSupportedHttp2FrameType(static_cast<uint32_t>(v)); } -// The return type is 'Http2String' so that they can generate a unique string +// The return type is 'std::string' so that they can generate a unique string // for each unsupported value. Since these are just used for debugging/error // messages, that isn't a cost to we need to worry about. The same applies to // the functions later in this file. -HTTP2_EXPORT_PRIVATE Http2String Http2FrameTypeToString(Http2FrameType v); -HTTP2_EXPORT_PRIVATE Http2String Http2FrameTypeToString(uint8_t v); +HTTP2_EXPORT_PRIVATE std::string Http2FrameTypeToString(Http2FrameType v); +HTTP2_EXPORT_PRIVATE std::string Http2FrameTypeToString(uint8_t v); HTTP2_EXPORT_PRIVATE inline std::ostream& operator<<(std::ostream& out, Http2FrameType v) { return out << Http2FrameTypeToString(v); @@ -75,9 +75,9 @@ enum Http2FrameFlag { // Formats zero or more flags for the specified type of frame. Returns an // empty string if flags==0. -HTTP2_EXPORT_PRIVATE Http2String Http2FrameFlagsToString(Http2FrameType type, +HTTP2_EXPORT_PRIVATE std::string Http2FrameFlagsToString(Http2FrameType type, uint8_t flags); -HTTP2_EXPORT_PRIVATE Http2String Http2FrameFlagsToString(uint8_t type, +HTTP2_EXPORT_PRIVATE std::string Http2FrameFlagsToString(uint8_t type, uint8_t flags); // Error codes for GOAWAY and RST_STREAM frames. @@ -142,8 +142,8 @@ inline bool IsSupportedHttp2ErrorCode(Http2ErrorCode v) { } // Format the specified error code. -HTTP2_EXPORT_PRIVATE Http2String Http2ErrorCodeToString(uint32_t v); -HTTP2_EXPORT_PRIVATE Http2String Http2ErrorCodeToString(Http2ErrorCode v); +HTTP2_EXPORT_PRIVATE std::string Http2ErrorCodeToString(uint32_t v); +HTTP2_EXPORT_PRIVATE std::string Http2ErrorCodeToString(Http2ErrorCode v); HTTP2_EXPORT_PRIVATE inline std::ostream& operator<<(std::ostream& out, Http2ErrorCode v) { return out << Http2ErrorCodeToString(v); @@ -222,9 +222,9 @@ inline bool IsSupportedHttp2SettingsParameter(Http2SettingsParameter v) { } // Format the specified settings parameter. -HTTP2_EXPORT_PRIVATE Http2String Http2SettingsParameterToString(uint32_t v); -HTTP2_EXPORT_PRIVATE Http2String -Http2SettingsParameterToString(Http2SettingsParameter v); +HTTP2_EXPORT_PRIVATE std::string Http2SettingsParameterToString(uint32_t v); +HTTP2_EXPORT_PRIVATE std::string Http2SettingsParameterToString( + Http2SettingsParameter v); inline std::ostream& operator<<(std::ostream& out, Http2SettingsParameter v) { return out << Http2SettingsParameterToString(v); } diff --git a/chromium/net/third_party/quiche/src/http2/http2_structures.cc b/chromium/net/third_party/quiche/src/http2/http2_structures.cc index 7dbaf30aedc..7b00db47cb2 100644 --- a/chromium/net/third_party/quiche/src/http2/http2_structures.cc +++ b/chromium/net/third_party/quiche/src/http2/http2_structures.cc @@ -19,13 +19,13 @@ bool Http2FrameHeader::IsProbableHttpResponse() const { flags == '/'); // "/" } -Http2String Http2FrameHeader::ToString() const { +std::string Http2FrameHeader::ToString() const { return Http2StrCat("length=", payload_length, ", type=", Http2FrameTypeToString(type), ", flags=", FlagsToString(), ", stream=", stream_id); } -Http2String Http2FrameHeader::FlagsToString() const { +std::string Http2FrameHeader::FlagsToString() const { return Http2FrameFlagsToString(type, flags); } @@ -44,7 +44,7 @@ bool operator==(const Http2PriorityFields& a, const Http2PriorityFields& b) { return a.stream_dependency == b.stream_dependency && a.weight == b.weight; } -Http2String Http2PriorityFields::ToString() const { +std::string Http2PriorityFields::ToString() const { std::stringstream ss; ss << "E=" << (is_exclusive ? "true" : "false") << ", stream=" << stream_dependency diff --git a/chromium/net/third_party/quiche/src/http2/http2_structures.h b/chromium/net/third_party/quiche/src/http2/http2_structures.h index f236cfbacec..f73e1099b8d 100644 --- a/chromium/net/third_party/quiche/src/http2/http2_structures.h +++ b/chromium/net/third_party/quiche/src/http2/http2_structures.h @@ -29,11 +29,11 @@ #include <cstdint> #include <ostream> +#include <string> #include "net/third_party/quiche/src/http2/http2_constants.h" #include "net/third_party/quiche/src/http2/platform/api/http2_export.h" #include "net/third_party/quiche/src/http2/platform/api/http2_logging.h" -#include "net/third_party/quiche/src/http2/platform/api/http2_string.h" namespace http2 { @@ -106,8 +106,8 @@ struct HTTP2_EXPORT_PRIVATE Http2FrameHeader { bool IsProbableHttpResponse() const; // Produce strings useful for debugging/logging messages. - Http2String ToString() const; - Http2String FlagsToString() const; + std::string ToString() const; + std::string FlagsToString() const; // 24 bit length of the payload after the header, including any padding. // First field in encoding. @@ -158,7 +158,7 @@ struct HTTP2_EXPORT_PRIVATE Http2PriorityFields { static constexpr size_t EncodedSize() { return 5; } // Produce strings useful for debugging/logging messages. - Http2String ToString() const; + std::string ToString() const; // A 31-bit stream identifier for the stream that this stream depends on. uint32_t stream_dependency; diff --git a/chromium/net/third_party/quiche/src/http2/http2_structures_test.cc b/chromium/net/third_party/quiche/src/http2/http2_structures_test.cc index 9e38248b036..49032260056 100644 --- a/chromium/net/third_party/quiche/src/http2/http2_structures_test.cc +++ b/chromium/net/third_party/quiche/src/http2/http2_structures_test.cc @@ -14,6 +14,7 @@ #include <memory> #include <ostream> #include <sstream> +#include <string> #include <tuple> #include <type_traits> #include <vector> @@ -171,7 +172,7 @@ INSTANTIATE_TEST_SUITE_P(IsEndStream, TEST_P(IsEndStreamTest, IsEndStream) { const bool is_set = (flags_ & Http2FrameFlag::END_STREAM) == Http2FrameFlag::END_STREAM; - Http2String flags_string; + std::string flags_string; Http2FrameHeader v(0, type_, flags_, 0); switch (type_) { case Http2FrameType::DATA: @@ -208,7 +209,7 @@ INSTANTIATE_TEST_SUITE_P(IsAck, Values(~Http2FrameFlag::ACK, 0xff))); TEST_P(IsACKTest, IsAck) { const bool is_set = (flags_ & Http2FrameFlag::ACK) == Http2FrameFlag::ACK; - Http2String flags_string; + std::string flags_string; Http2FrameHeader v(0, type_, flags_, 0); switch (type_) { case Http2FrameType::SETTINGS: @@ -246,7 +247,7 @@ INSTANTIATE_TEST_SUITE_P(IsEndHeaders, TEST_P(IsEndHeadersTest, IsEndHeaders) { const bool is_set = (flags_ & Http2FrameFlag::END_HEADERS) == Http2FrameFlag::END_HEADERS; - Http2String flags_string; + std::string flags_string; Http2FrameHeader v(0, type_, flags_, 0); switch (type_) { case Http2FrameType::HEADERS: @@ -287,7 +288,7 @@ INSTANTIATE_TEST_SUITE_P(IsPadded, TEST_P(IsPaddedTest, IsPadded) { const bool is_set = (flags_ & Http2FrameFlag::PADDED) == Http2FrameFlag::PADDED; - Http2String flags_string; + std::string flags_string; Http2FrameHeader v(0, type_, flags_, 0); switch (type_) { case Http2FrameType::DATA: @@ -326,7 +327,7 @@ INSTANTIATE_TEST_SUITE_P(HasPriority, TEST_P(HasPriorityTest, HasPriority) { const bool is_set = (flags_ & Http2FrameFlag::PRIORITY) == Http2FrameFlag::PRIORITY; - Http2String flags_string; + std::string flags_string; Http2FrameHeader v(0, type_, flags_, 0); switch (type_) { case Http2FrameType::HEADERS: diff --git a/chromium/net/third_party/quiche/src/http2/http2_structures_test_util.h b/chromium/net/third_party/quiche/src/http2/http2_structures_test_util.h index 86fbf3f20af..77127a1ff3c 100644 --- a/chromium/net/third_party/quiche/src/http2/http2_structures_test_util.h +++ b/chromium/net/third_party/quiche/src/http2/http2_structures_test_util.h @@ -5,9 +5,10 @@ #ifndef QUICHE_HTTP2_HTTP2_STRUCTURES_TEST_UTIL_H_ #define QUICHE_HTTP2_HTTP2_STRUCTURES_TEST_UTIL_H_ +#include <string> + #include "testing/gtest/include/gtest/gtest.h" #include "net/third_party/quiche/src/http2/http2_structures.h" -#include "net/third_party/quiche/src/http2/platform/api/http2_string.h" #include "net/third_party/quiche/src/http2/test_tools/http2_random.h" #include "net/third_party/quiche/src/http2/tools/http2_frame_builder.h" @@ -15,7 +16,7 @@ namespace http2 { namespace test { template <class S> -Http2String SerializeStructure(const S& s) { +std::string SerializeStructure(const S& s) { Http2FrameBuilder fb; fb.Append(s); EXPECT_EQ(S::EncodedSize(), fb.size()); diff --git a/chromium/net/third_party/quiche/src/http2/platform/api/http2_string.h b/chromium/net/third_party/quiche/src/http2/platform/api/http2_string.h deleted file mode 100644 index 25f9e593cc1..00000000000 --- a/chromium/net/third_party/quiche/src/http2/platform/api/http2_string.h +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef QUICHE_HTTP2_PLATFORM_API_HTTP2_STRING_H_ -#define QUICHE_HTTP2_PLATFORM_API_HTTP2_STRING_H_ - -#include "net/http2/platform/impl/http2_string_impl.h" - -namespace http2 { - -using Http2String = Http2StringImpl; - -} // namespace http2 - -#endif // QUICHE_HTTP2_PLATFORM_API_HTTP2_STRING_H_ diff --git a/chromium/net/third_party/quiche/src/http2/platform/api/http2_string_utils.h b/chromium/net/third_party/quiche/src/http2/platform/api/http2_string_utils.h index ba4056051ee..1db9d66d7aa 100644 --- a/chromium/net/third_party/quiche/src/http2/platform/api/http2_string_utils.h +++ b/chromium/net/third_party/quiche/src/http2/platform/api/http2_string_utils.h @@ -5,48 +5,48 @@ #ifndef QUICHE_HTTP2_PLATFORM_API_HTTP2_STRING_UTILS_H_ #define QUICHE_HTTP2_PLATFORM_API_HTTP2_STRING_UTILS_H_ +#include <string> #include <type_traits> #include <utility> -#include "net/third_party/quiche/src/http2/platform/api/http2_string.h" #include "net/third_party/quiche/src/http2/platform/api/http2_string_piece.h" #include "net/http2/platform/impl/http2_string_utils_impl.h" namespace http2 { template <typename... Args> -inline Http2String Http2StrCat(const Args&... args) { +inline std::string Http2StrCat(const Args&... args) { return Http2StrCatImpl(std::forward<const Args&>(args)...); } template <typename... Args> -inline void Http2StrAppend(Http2String* output, const Args&... args) { +inline void Http2StrAppend(std::string* output, const Args&... args) { Http2StrAppendImpl(output, std::forward<const Args&>(args)...); } template <typename... Args> -inline Http2String Http2StringPrintf(const Args&... args) { +inline std::string Http2StringPrintf(const Args&... args) { return Http2StringPrintfImpl(std::forward<const Args&>(args)...); } -inline Http2String Http2HexEncode(const void* bytes, size_t size) { +inline std::string Http2HexEncode(const void* bytes, size_t size) { return Http2HexEncodeImpl(bytes, size); } -inline Http2String Http2HexDecode(Http2StringPiece data) { +inline std::string Http2HexDecode(Http2StringPiece data) { return Http2HexDecodeImpl(data); } -inline Http2String Http2HexDump(Http2StringPiece data) { +inline std::string Http2HexDump(Http2StringPiece data) { return Http2HexDumpImpl(data); } -inline Http2String Http2HexEscape(Http2StringPiece data) { +inline std::string Http2HexEscape(Http2StringPiece data) { return Http2HexEscapeImpl(data); } template <typename Number> -inline Http2String Http2Hex(Number number) { +inline std::string Http2Hex(Number number) { static_assert(std::is_integral<Number>::value, "Number has to be an int"); return Http2HexImpl(number); } diff --git a/chromium/net/third_party/quiche/src/http2/platform/api/http2_string_utils_test.cc b/chromium/net/third_party/quiche/src/http2/platform/api/http2_string_utils_test.cc index 254f9d7d460..6ffc4b437cf 100644 --- a/chromium/net/third_party/quiche/src/http2/platform/api/http2_string_utils_test.cc +++ b/chromium/net/third_party/quiche/src/http2/platform/api/http2_string_utils_test.cc @@ -19,7 +19,7 @@ TEST(Http2StringUtilsTest, Http2StrCat) { // Single string-like argument. const char kFoo[] = "foo"; - const Http2String string_foo(kFoo); + const std::string string_foo(kFoo); const Http2StringPiece stringpiece_foo(string_foo); EXPECT_EQ("foo", Http2StrCat(kFoo)); EXPECT_EQ("foo", Http2StrCat(string_foo)); @@ -28,7 +28,7 @@ TEST(Http2StringUtilsTest, Http2StrCat) { // Two string-like arguments. const char kBar[] = "bar"; const Http2StringPiece stringpiece_bar(kBar); - const Http2String string_bar(kBar); + const std::string string_bar(kBar); EXPECT_EQ("foobar", Http2StrCat(kFoo, kBar)); EXPECT_EQ("foobar", Http2StrCat(kFoo, string_bar)); EXPECT_EQ("foobar", Http2StrCat(kFoo, stringpiece_bar)); @@ -72,13 +72,13 @@ TEST(Http2StringUtilsTest, Http2StrCat) { TEST(Http2StringUtilsTest, Http2StrAppend) { // No arguments on empty string. - Http2String output; + std::string output; Http2StrAppend(&output); EXPECT_TRUE(output.empty()); // Single string-like argument. const char kFoo[] = "foo"; - const Http2String string_foo(kFoo); + const std::string string_foo(kFoo); const Http2StringPiece stringpiece_foo(string_foo); Http2StrAppend(&output, kFoo); EXPECT_EQ("foo", output); @@ -96,7 +96,7 @@ TEST(Http2StringUtilsTest, Http2StrAppend) { // Two string-like arguments. const char kBar[] = "bar"; const Http2StringPiece stringpiece_bar(kBar); - const Http2String string_bar(kBar); + const std::string string_bar(kBar); Http2StrAppend(&output, kFoo, kBar); EXPECT_EQ("foobar", output); Http2StrAppend(&output, kFoo, string_bar); diff --git a/chromium/net/third_party/quiche/src/http2/test_tools/frame_parts.cc b/chromium/net/third_party/quiche/src/http2/test_tools/frame_parts.cc index d5507882da9..d9dffa146e2 100644 --- a/chromium/net/third_party/quiche/src/http2/test_tools/frame_parts.cc +++ b/chromium/net/third_party/quiche/src/http2/test_tools/frame_parts.cc @@ -507,7 +507,7 @@ AssertionResult FrameParts::InPaddedFrame() { } AssertionResult FrameParts::AppendString(Http2StringPiece source, - Http2String* target, + std::string* target, Http2Optional<size_t>* opt_length) { target->append(source.data(), source.size()); if (opt_length != nullptr) { diff --git a/chromium/net/third_party/quiche/src/http2/test_tools/frame_parts.h b/chromium/net/third_party/quiche/src/http2/test_tools/frame_parts.h index 5a7d872e893..598a6878bff 100644 --- a/chromium/net/third_party/quiche/src/http2/test_tools/frame_parts.h +++ b/chromium/net/third_party/quiche/src/http2/test_tools/frame_parts.h @@ -13,6 +13,7 @@ #include <stddef.h> #include <cstdint> +#include <string> #include <vector> #include "testing/gtest/include/gtest/gtest.h" @@ -21,7 +22,6 @@ #include "net/third_party/quiche/src/http2/http2_structures.h" #include "net/third_party/quiche/src/http2/platform/api/http2_logging.h" #include "net/third_party/quiche/src/http2/platform/api/http2_optional.h" -#include "net/third_party/quiche/src/http2/platform/api/http2_string.h" #include "net/third_party/quiche/src/http2/platform/api/http2_string_piece.h" namespace http2 { @@ -208,15 +208,15 @@ class FrameParts : public Http2FrameDecoderListener { // the optional has a value (i.e. that the necessary On*Start method has been // called), and that target is not longer than opt_length->value(). ::testing::AssertionResult AppendString(Http2StringPiece source, - Http2String* target, + std::string* target, Http2Optional<size_t>* opt_length); const Http2FrameHeader frame_header_; - Http2String payload_; - Http2String padding_; - Http2String altsvc_origin_; - Http2String altsvc_value_; + std::string payload_; + std::string padding_; + std::string altsvc_origin_; + std::string altsvc_value_; Http2Optional<Http2PriorityFields> opt_priority_; Http2Optional<Http2ErrorCode> opt_rst_stream_error_code_; diff --git a/chromium/net/third_party/quiche/src/http2/test_tools/http2_random.cc b/chromium/net/third_party/quiche/src/http2/test_tools/http2_random.cc index 81c036679dd..6b61a583fed 100644 --- a/chromium/net/third_party/quiche/src/http2/test_tools/http2_random.cc +++ b/chromium/net/third_party/quiche/src/http2/test_tools/http2_random.cc @@ -17,12 +17,12 @@ Http2Random::Http2Random() { } Http2Random::Http2Random(Http2StringPiece key) { - Http2String decoded_key = Http2HexDecode(key); + std::string decoded_key = Http2HexDecode(key); CHECK_EQ(sizeof(key_), decoded_key.size()); memcpy(key_, decoded_key.data(), sizeof(key_)); } -Http2String Http2Random::Key() const { +std::string Http2Random::Key() const { return Http2HexEncode(key_, sizeof(key_)); } @@ -33,8 +33,8 @@ void Http2Random::FillRandom(void* buffer, size_t buffer_size) { counter_++); } -Http2String Http2Random::RandString(int length) { - Http2String result; +std::string Http2Random::RandString(int length) { + std::string result; result.resize(length); FillRandom(&result[0], length); return result; @@ -58,9 +58,9 @@ double Http2Random::RandDouble() { return value.f - 1.0; } -Http2String Http2Random::RandStringWithAlphabet(int length, +std::string Http2Random::RandStringWithAlphabet(int length, Http2StringPiece alphabet) { - Http2String result; + std::string result; result.resize(length); for (int i = 0; i < length; i++) { result[i] = alphabet[Uniform(alphabet.size())]; diff --git a/chromium/net/third_party/quiche/src/http2/test_tools/http2_random.h b/chromium/net/third_party/quiche/src/http2/test_tools/http2_random.h index def60f8665d..774212cb5e9 100644 --- a/chromium/net/third_party/quiche/src/http2/test_tools/http2_random.h +++ b/chromium/net/third_party/quiche/src/http2/test_tools/http2_random.h @@ -9,8 +9,8 @@ #include <cstdint> #include <limits> #include <random> +#include <string> -#include "net/third_party/quiche/src/http2/platform/api/http2_string.h" #include "net/third_party/quiche/src/http2/platform/api/http2_string_piece.h" namespace http2 { @@ -29,10 +29,10 @@ class Http2Random { // Reproducible random number generation: by using the same key, the same // sequence of results is obtained. explicit Http2Random(Http2StringPiece key); - Http2String Key() const; + std::string Key() const; void FillRandom(void* buffer, size_t buffer_size); - Http2String RandString(int length); + std::string RandString(int length); // Returns a random 64-bit value. uint64_t Rand64(); @@ -67,7 +67,7 @@ class Http2Random { // Return a random string consisting of the characters from the specified // alphabet. - Http2String RandStringWithAlphabet(int length, Http2StringPiece alphabet); + std::string RandStringWithAlphabet(int length, Http2StringPiece alphabet); // STL UniformRandomNumberGenerator implementation. using result_type = uint64_t; diff --git a/chromium/net/third_party/quiche/src/http2/test_tools/http2_random_test.cc b/chromium/net/third_party/quiche/src/http2/test_tools/http2_random_test.cc index a1b74f64a7b..c9490bdb2ba 100644 --- a/chromium/net/third_party/quiche/src/http2/test_tools/http2_random_test.cc +++ b/chromium/net/third_party/quiche/src/http2/test_tools/http2_random_test.cc @@ -43,9 +43,9 @@ TEST(Http2RandomTest, ReproducibleRandom) { TEST(Http2RandomTest, STLShuffle) { Http2Random random; - const Http2String original = "abcdefghijklmonpqrsuvwxyz"; + const std::string original = "abcdefghijklmonpqrsuvwxyz"; - Http2String shuffled = original; + std::string shuffled = original; std::shuffle(shuffled.begin(), shuffled.end(), random); EXPECT_NE(original, shuffled); } @@ -61,7 +61,7 @@ TEST(Http2RandomTest, RandFloat) { TEST(Http2RandomTest, RandStringWithAlphabet) { Http2Random random; - Http2String str = random.RandStringWithAlphabet(1000, "xyz"); + std::string str = random.RandStringWithAlphabet(1000, "xyz"); EXPECT_EQ(1000u, str.size()); std::set<char> characters(str.begin(), str.end()); diff --git a/chromium/net/third_party/quiche/src/http2/tools/http2_frame_builder.h b/chromium/net/third_party/quiche/src/http2/tools/http2_frame_builder.h index c36cb56e48a..70d5f44415d 100644 --- a/chromium/net/third_party/quiche/src/http2/tools/http2_frame_builder.h +++ b/chromium/net/third_party/quiche/src/http2/tools/http2_frame_builder.h @@ -16,10 +16,10 @@ #include <stddef.h> // for size_t #include <cstdint> +#include <string> #include "net/third_party/quiche/src/http2/http2_constants.h" #include "net/third_party/quiche/src/http2/http2_structures.h" -#include "net/third_party/quiche/src/http2/platform/api/http2_string.h" #include "net/third_party/quiche/src/http2/platform/api/http2_string_piece.h" namespace http2 { @@ -33,7 +33,7 @@ class Http2FrameBuilder { ~Http2FrameBuilder() {} size_t size() const { return buffer_.size(); } - const Http2String& buffer() const { return buffer_; } + const std::string& buffer() const { return buffer_; } //---------------------------------------------------------------------------- // Methods for appending to the end of the buffer. @@ -92,7 +92,7 @@ class Http2FrameBuilder { size_t SetPayloadLength(); private: - Http2String buffer_; + std::string buffer_; }; } // namespace test diff --git a/chromium/net/third_party/quiche/src/http2/tools/random_decoder_test.h b/chromium/net/third_party/quiche/src/http2/tools/random_decoder_test.h index f162ad79ad9..ef31d01e455 100644 --- a/chromium/net/third_party/quiche/src/http2/tools/random_decoder_test.h +++ b/chromium/net/third_party/quiche/src/http2/tools/random_decoder_test.h @@ -21,7 +21,6 @@ #include "net/third_party/quiche/src/http2/decoder/decode_buffer.h" #include "net/third_party/quiche/src/http2/decoder/decode_status.h" #include "net/third_party/quiche/src/http2/platform/api/http2_logging.h" -#include "net/third_party/quiche/src/http2/platform/api/http2_string.h" #include "net/third_party/quiche/src/http2/platform/api/http2_string_piece.h" #include "net/third_party/quiche/src/http2/platform/api/http2_test_helpers.h" #include "net/third_party/quiche/src/http2/test_tools/http2_random.h" diff --git a/chromium/net/third_party/quiche/src/http2/tools/random_util.cc b/chromium/net/third_party/quiche/src/http2/tools/random_util.cc index 82c3edd4063..a7d95c4f053 100644 --- a/chromium/net/third_party/quiche/src/http2/tools/random_util.cc +++ b/chromium/net/third_party/quiche/src/http2/tools/random_util.cc @@ -11,7 +11,7 @@ namespace test { // Here "word" means something that starts with a lower-case letter, and has // zero or more additional characters that are numbers or lower-case letters. -Http2String GenerateHttp2HeaderName(size_t len, Http2Random* rng) { +std::string GenerateHttp2HeaderName(size_t len, Http2Random* rng) { Http2StringPiece alpha_lc = "abcdefghijklmnopqrstuvwxyz"; // If the name is short, just make it one word. if (len < 8) { @@ -25,13 +25,13 @@ Http2String GenerateHttp2HeaderName(size_t len, Http2Random* rng) { rng->RandStringWithAlphabet(len - 4, alphanumdash_lc); } -Http2String GenerateWebSafeString(size_t len, Http2Random* rng) { +std::string GenerateWebSafeString(size_t len, Http2Random* rng) { static const char* kWebsafe64 = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_"; return rng->RandStringWithAlphabet(len, kWebsafe64); } -Http2String GenerateWebSafeString(size_t lo, size_t hi, Http2Random* rng) { +std::string GenerateWebSafeString(size_t lo, size_t hi, Http2Random* rng) { return GenerateWebSafeString(rng->UniformInRange(lo, hi), rng); } diff --git a/chromium/net/third_party/quiche/src/http2/tools/random_util.h b/chromium/net/third_party/quiche/src/http2/tools/random_util.h index a2107b7c7c9..6e721890650 100644 --- a/chromium/net/third_party/quiche/src/http2/tools/random_util.h +++ b/chromium/net/third_party/quiche/src/http2/tools/random_util.h @@ -7,7 +7,8 @@ #include <stddef.h> -#include "net/third_party/quiche/src/http2/platform/api/http2_string.h" +#include <string> + #include "net/third_party/quiche/src/http2/test_tools/http2_random.h" namespace http2 { @@ -15,13 +16,13 @@ namespace test { // Generate a string with the allowed character set for HTTP/2 / HPACK header // names. -Http2String GenerateHttp2HeaderName(size_t len, Http2Random* rng); +std::string GenerateHttp2HeaderName(size_t len, Http2Random* rng); // Generate a string with the web-safe string character set of specified len. -Http2String GenerateWebSafeString(size_t len, Http2Random* rng); +std::string GenerateWebSafeString(size_t len, Http2Random* rng); // Generate a string with the web-safe string character set of length [lo, hi). -Http2String GenerateWebSafeString(size_t lo, size_t hi, Http2Random* rng); +std::string GenerateWebSafeString(size_t lo, size_t hi, Http2Random* rng); } // namespace test } // namespace http2 diff --git a/chromium/net/third_party/quiche/src/quic/core/chlo_extractor.cc b/chromium/net/third_party/quiche/src/quic/core/chlo_extractor.cc index 0e8e6f57402..38e97bd15fe 100644 --- a/chromium/net/third_party/quiche/src/quic/core/chlo_extractor.cc +++ b/chromium/net/third_party/quiche/src/quic/core/chlo_extractor.cc @@ -44,6 +44,9 @@ class ChloFramerVisitor : public QuicFramerVisitorInterface, void OnDecryptedPacket(EncryptionLevel /*level*/) override {} bool OnPacketHeader(const QuicPacketHeader& header) override; void OnCoalescedPacket(const QuicEncryptedPacket& packet) override; + void OnUndecryptablePacket(const QuicEncryptedPacket& packet, + EncryptionLevel decryption_level, + bool has_decryption_key) override; bool OnStreamFrame(const QuicStreamFrame& frame) override; bool OnCryptoFrame(const QuicCryptoFrame& frame) override; bool OnAckFrameStart(QuicPacketNumber largest_acked, @@ -142,8 +145,15 @@ bool ChloFramerVisitor::OnUnauthenticatedHeader( bool ChloFramerVisitor::OnPacketHeader(const QuicPacketHeader& /*header*/) { return true; } + void ChloFramerVisitor::OnCoalescedPacket( const QuicEncryptedPacket& /*packet*/) {} + +void ChloFramerVisitor::OnUndecryptablePacket( + const QuicEncryptedPacket& /*packet*/, + EncryptionLevel /*decryption_level*/, + bool /*has_decryption_key*/) {} + bool ChloFramerVisitor::OnStreamFrame(const QuicStreamFrame& frame) { if (QuicVersionUsesCryptoFrames(framer_->transport_version())) { // CHLO will be sent in CRYPTO frames in v47 and above. diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bandwidth_sampler.cc b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bandwidth_sampler.cc index 11a625c768c..0311191d641 100644 --- a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bandwidth_sampler.cc +++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bandwidth_sampler.cc @@ -6,13 +6,49 @@ #include <algorithm> +#include "net/third_party/quiche/src/quic/core/quic_types.h" #include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" #include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h" #include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" namespace quic { -BandwidthSampler::BandwidthSampler() + +QuicByteCount MaxAckHeightTracker::Update(QuicBandwidth bandwidth_estimate, + QuicRoundTripCount round_trip_count, + QuicTime ack_time, + QuicByteCount bytes_acked) { + if (aggregation_epoch_start_time_ == QuicTime::Zero()) { + aggregation_epoch_bytes_ = bytes_acked; + aggregation_epoch_start_time_ = ack_time; + return 0; + } + + // Compute how many bytes are expected to be delivered, assuming max bandwidth + // is correct. + QuicByteCount expected_bytes_acked = + bandwidth_estimate * (ack_time - aggregation_epoch_start_time_); + // Reset the current aggregation epoch as soon as the ack arrival rate is less + // than or equal to the max bandwidth. + if (aggregation_epoch_bytes_ <= expected_bytes_acked) { + // Reset to start measuring a new aggregation epoch. + aggregation_epoch_bytes_ = bytes_acked; + aggregation_epoch_start_time_ = ack_time; + return 0; + } + + aggregation_epoch_bytes_ += bytes_acked; + + // Compute how many extra bytes were delivered vs max bandwidth. + QuicByteCount extra_bytes_acked = + aggregation_epoch_bytes_ - expected_bytes_acked; + max_ack_height_filter_.Update(extra_bytes_acked, round_trip_count); + return extra_bytes_acked; +} + +BandwidthSampler::BandwidthSampler( + const QuicUnackedPacketMap* unacked_packet_map, + QuicRoundTripCount max_height_tracker_window_length) : total_bytes_sent_(0), total_bytes_acked_(0), total_bytes_lost_(0), @@ -21,7 +57,16 @@ BandwidthSampler::BandwidthSampler() last_acked_packet_ack_time_(QuicTime::Zero()), is_app_limited_(false), connection_state_map_(), - max_tracked_packets_(GetQuicFlag(FLAGS_quic_max_tracked_packet_count)) {} + max_tracked_packets_(GetQuicFlag(FLAGS_quic_max_tracked_packet_count)), + unacked_packet_map_(unacked_packet_map), + max_ack_height_tracker_(max_height_tracker_window_length), + total_bytes_acked_after_last_ack_event_(0), + quic_track_ack_height_in_bandwidth_sampler_( + GetQuicReloadableFlag(quic_track_ack_height_in_bandwidth_sampler2)) { + if (quic_track_ack_height_in_bandwidth_sampler_) { + QUIC_RELOADABLE_FLAG_COUNT(quic_track_ack_height_in_bandwidth_sampler2); + } +} BandwidthSampler::~BandwidthSampler() {} @@ -57,9 +102,18 @@ void BandwidthSampler::OnPacketSent( if (!connection_state_map_.IsEmpty() && packet_number > connection_state_map_.last_packet() + max_tracked_packets_) { - QUIC_BUG << "BandwidthSampler in-flight packet map has exceeded maximum " - "number " - "of tracked packets."; + if (unacked_packet_map_ != nullptr) { + QUIC_BUG << "BandwidthSampler in-flight packet map has exceeded maximum " + "number of tracked packets. First tracked: " + << connection_state_map_.first_packet() + << "; last tracked: " << connection_state_map_.last_packet() + << "; least unacked: " << unacked_packet_map_->GetLeastUnacked() + << "; largest observed: " + << unacked_packet_map_->largest_acked(); + } else { + QUIC_BUG << "BandwidthSampler in-flight packet map has exceeded maximum " + "number of tracked packets."; + } } bool success = @@ -69,6 +123,22 @@ void BandwidthSampler::OnPacketSent( "in it."; } +QuicByteCount BandwidthSampler::OnAckEventEnd( + QuicBandwidth bandwidth_estimate, + QuicRoundTripCount round_trip_count) { + const QuicByteCount newly_acked_bytes = + total_bytes_acked_ - total_bytes_acked_after_last_ack_event_; + + if (newly_acked_bytes == 0) { + return 0; + } + total_bytes_acked_after_last_ack_event_ = total_bytes_acked_; + + return max_ack_height_tracker_.Update(bandwidth_estimate, round_trip_count, + last_acked_packet_ack_time_, + newly_acked_bytes); +} + BandwidthSample BandwidthSampler::OnPacketAcknowledged( QuicTime ack_time, QuicPacketNumber packet_number) { @@ -96,7 +166,8 @@ BandwidthSample BandwidthSampler::OnPacketAcknowledgedInner( // Exit app-limited phase once a packet that was sent while the connection is // not app-limited is acknowledged. - if (is_app_limited_ && packet_number > end_of_app_limited_phase_) { + if (is_app_limited_ && end_of_app_limited_phase_.IsInitialized() && + packet_number > end_of_app_limited_phase_) { is_app_limited_ = false; } diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bandwidth_sampler.h b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bandwidth_sampler.h index 9a58cf6e8de..8b279c61e32 100644 --- a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bandwidth_sampler.h +++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bandwidth_sampler.h @@ -5,11 +5,14 @@ #ifndef QUICHE_QUIC_CORE_CONGESTION_CONTROL_BANDWIDTH_SAMPLER_H_ #define QUICHE_QUIC_CORE_CONGESTION_CONTROL_BANDWIDTH_SAMPLER_H_ +#include "net/third_party/quiche/src/quic/core/congestion_control/send_algorithm_interface.h" +#include "net/third_party/quiche/src/quic/core/congestion_control/windowed_filter.h" #include "net/third_party/quiche/src/quic/core/packet_number_indexed_queue.h" #include "net/third_party/quiche/src/quic/core/quic_bandwidth.h" #include "net/third_party/quiche/src/quic/core/quic_packets.h" #include "net/third_party/quiche/src/quic/core/quic_time.h" #include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/core/quic_unacked_packet_map.h" #include "net/third_party/quiche/src/quic/platform/api/quic_export.h" namespace quic { @@ -75,6 +78,43 @@ struct QUIC_EXPORT_PRIVATE BandwidthSample { : bandwidth(QuicBandwidth::Zero()), rtt(QuicTime::Delta::Zero()) {} }; +// MaxAckHeightTracker is part of the BandwidthSampler. It is called after every +// ack event to keep track the degree of ack aggregation(a.k.a "ack height"). +class QUIC_EXPORT_PRIVATE MaxAckHeightTracker { + public: + explicit MaxAckHeightTracker(QuicRoundTripCount initial_filter_window) + : max_ack_height_filter_(initial_filter_window, 0, 0) {} + + QuicByteCount Get() const { return max_ack_height_filter_.GetBest(); } + + QuicByteCount Update(QuicBandwidth bandwidth_estimate, + QuicRoundTripCount round_trip_count, + QuicTime ack_time, + QuicByteCount bytes_acked); + + void SetFilterWindowLength(QuicRoundTripCount length) { + max_ack_height_filter_.SetWindowLength(length); + } + + void Reset(QuicByteCount new_height, QuicRoundTripCount new_time) { + max_ack_height_filter_.Reset(new_height, new_time); + } + + private: + // Tracks the maximum number of bytes acked faster than the estimated + // bandwidth. + typedef WindowedFilter<QuicByteCount, + MaxFilter<QuicByteCount>, + QuicRoundTripCount, + QuicRoundTripCount> + MaxAckHeightFilter; + MaxAckHeightFilter max_ack_height_filter_; + + // The time this aggregation started and the number of bytes acked during it. + QuicTime aggregation_epoch_start_time_ = QuicTime::Zero(); + QuicByteCount aggregation_epoch_bytes_ = 0; +}; + // An interface common to any class that can provide bandwidth samples from the // information per individual acknowledged packet. class QUIC_EXPORT_PRIVATE BandwidthSamplerInterface { @@ -204,7 +244,8 @@ class QUIC_EXPORT_PRIVATE BandwidthSamplerInterface { // connection is app-limited, the approach works in other cases too. class QUIC_EXPORT_PRIVATE BandwidthSampler : public BandwidthSamplerInterface { public: - BandwidthSampler(); + BandwidthSampler(const QuicUnackedPacketMap* unacked_packet_map, + QuicRoundTripCount max_height_tracker_window_length); ~BandwidthSampler() override; void OnPacketSent(QuicTime sent_time, @@ -214,6 +255,8 @@ class QUIC_EXPORT_PRIVATE BandwidthSampler : public BandwidthSamplerInterface { HasRetransmittableData has_retransmittable_data) override; BandwidthSample OnPacketAcknowledged(QuicTime ack_time, QuicPacketNumber packet_number) override; + QuicByteCount OnAckEventEnd(QuicBandwidth bandwidth_estimate, + QuicRoundTripCount round_trip_count); SendTimeState OnPacketLost(QuicPacketNumber packet_number) override; void OnAppLimited() override; @@ -228,6 +271,21 @@ class QUIC_EXPORT_PRIVATE BandwidthSampler : public BandwidthSamplerInterface { QuicPacketNumber end_of_app_limited_phase() const override; + QuicByteCount max_ack_height() const { return max_ack_height_tracker_.Get(); } + + void SetMaxAckHeightTrackerWindowLength(QuicRoundTripCount length) { + max_ack_height_tracker_.SetFilterWindowLength(length); + } + + void ResetMaxAckHeightTracker(QuicByteCount new_height, + QuicRoundTripCount new_time) { + max_ack_height_tracker_.Reset(new_height, new_time); + } + + bool quic_track_ack_height_in_bandwidth_sampler() const { + return quic_track_ack_height_in_bandwidth_sampler_; + } + private: friend class test::BandwidthSamplerPeer; @@ -327,12 +385,23 @@ class QUIC_EXPORT_PRIVATE BandwidthSampler : public BandwidthSamplerInterface { // Maximum number of tracked packets. const QuicPacketCount max_tracked_packets_; + // The main unacked packet map. Used for outputting extra debugging details. + // May be null. + // TODO(vasilvv): remove this once it's no longer useful for debugging. + const QuicUnackedPacketMap* unacked_packet_map_; + // Handles the actual bandwidth calculations, whereas the outer method handles // retrieving and removing |sent_packet|. BandwidthSample OnPacketAcknowledgedInner( QuicTime ack_time, QuicPacketNumber packet_number, const ConnectionStateOnSentPacket& sent_packet); + + MaxAckHeightTracker max_ack_height_tracker_; + QuicByteCount total_bytes_acked_after_last_ack_event_; + + // Latched value of --quic_track_ack_height_in_bandwidth_sampler2. + const bool quic_track_ack_height_in_bandwidth_sampler_; }; } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bandwidth_sampler_test.cc b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bandwidth_sampler_test.cc index 750b14e6922..aeff9762f0e 100644 --- a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bandwidth_sampler_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bandwidth_sampler_test.cc @@ -32,7 +32,9 @@ static_assert((kRegularPacketSize & 31) == 0, // A test fixture with utility methods for BandwidthSampler tests. class BandwidthSamplerTest : public QuicTest { protected: - BandwidthSamplerTest() : bytes_in_flight_(0) { + BandwidthSamplerTest() + : sampler_(nullptr, /*max_height_tracker_window_length=*/0), + bytes_in_flight_(0) { // Ensure that the clock does not start at zero. clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1)); } diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_misc.cc b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_misc.cc index f5e3d188e6a..a527417a19d 100644 --- a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_misc.cc +++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_misc.cc @@ -15,8 +15,6 @@ namespace { // Sensitivity in response to losses. 0 means no loss response. // 0.3 is also used by TCP bbr and cubic. const float kBeta = 0.3; - -const QuicTime::Delta kMinRttExpiry = QuicTime::Delta::FromSeconds(10); } // namespace RoundTripCounter::RoundTripCounter() : round_trip_count_(0) {} @@ -79,42 +77,14 @@ const SendTimeState& SendStateOfLargestPacket( return last_acked_sample.bandwidth_sample.state_at_send; } -QuicByteCount Bbr2MaxAckHeightTracker::Update( - const QuicBandwidth& bandwidth_estimate, - QuicRoundTripCount round_trip_count, - QuicTime ack_time, - QuicByteCount bytes_acked) { - // TODO(wub): Find out whether TCP adds bytes_acked before or after the check. - aggregation_epoch_bytes_ += bytes_acked; - - // Compute how many bytes are expected to be delivered, assuming max bandwidth - // is correct. - QuicByteCount expected_bytes_acked = - bandwidth_estimate * (ack_time - aggregation_epoch_start_time_); - // Reset the current aggregation epoch as soon as the ack arrival rate is less - // than or equal to the max bandwidth. - if (aggregation_epoch_bytes_ <= expected_bytes_acked) { - // Reset to start measuring a new aggregation epoch. - aggregation_epoch_bytes_ = bytes_acked; - aggregation_epoch_start_time_ = ack_time; - return 0; - } - - // Compute how many extra bytes were delivered vs max bandwidth. - QuicByteCount extra_bytes_acked = - aggregation_epoch_bytes_ - expected_bytes_acked; - max_ack_height_filter_.Update(extra_bytes_acked, round_trip_count); - return extra_bytes_acked; -} - Bbr2NetworkModel::Bbr2NetworkModel(const Bbr2Params* params, QuicTime::Delta initial_rtt, QuicTime initial_rtt_timestamp, float cwnd_gain, float pacing_gain) : params_(params), + bandwidth_sampler_(nullptr, params->initial_max_ack_height_filter_window), min_rtt_filter_(initial_rtt, initial_rtt_timestamp), - max_ack_height_tracker_(params->initial_max_ack_height_filter_window), cwnd_gain_(cwnd_gain), pacing_gain_(pacing_gain) {} @@ -200,8 +170,7 @@ void Bbr2NetworkModel::OnCongestionEventStart( congestion_event->bytes_lost = total_bytes_lost() - prior_bytes_lost; bytes_lost_in_round_ += congestion_event->bytes_lost; - max_ack_height_tracker_.Update(BandwidthEstimate(), RoundTripCount(), - event_time, congestion_event->bytes_acked); + bandwidth_sampler_.OnAckEventEnd(BandwidthEstimate(), RoundTripCount()); if (!congestion_event->end_of_round_trip) { return; @@ -264,7 +233,8 @@ void Bbr2NetworkModel::UpdateNetworkParameters(QuicBandwidth bandwidth, bool Bbr2NetworkModel::MaybeExpireMinRtt( const Bbr2CongestionEvent& congestion_event) { - if (congestion_event.event_time < (MinRttTimestamp() + kMinRttExpiry)) { + if (congestion_event.event_time < + (MinRttTimestamp() + Params().probe_rtt_period)) { return false; } if (congestion_event.sample_min_rtt.IsInfinite()) { diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_misc.h b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_misc.h index 1b695e35314..4ad351e32cd 100644 --- a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_misc.h +++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_misc.h @@ -15,12 +15,11 @@ #include "net/third_party/quiche/src/quic/core/quic_time.h" #include "net/third_party/quiche/src/quic/core/quic_types.h" #include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" #include "net/quic/platform/impl/quic_export_impl.h" namespace quic { -typedef uint64_t QuicRoundTripCount; - template <typename T> class QUIC_EXPORT_PRIVATE Limits { public: @@ -88,7 +87,8 @@ struct QUIC_EXPORT_PRIVATE Bbr2Params { QuicRoundTripCount startup_full_bw_rounds = 3; // The minimum number of loss marking events to exit STARTUP. - int64_t startup_full_loss_count = 8; + int64_t startup_full_loss_count = + GetQuicFlag(FLAGS_quic_bbr2_default_startup_full_loss_count); /* * DRAIN parameters. @@ -111,11 +111,13 @@ struct QUIC_EXPORT_PRIVATE Bbr2Params { // Minimum duration for BBR-native probes. QuicTime::Delta probe_bw_probe_base_duration = - QuicTime::Delta::FromSeconds(2); + QuicTime::Delta::FromMilliseconds( + GetQuicFlag(FLAGS_quic_bbr2_default_probe_bw_base_duration_ms)); - // The upper bound of the random amound of BBR-native probes. + // The upper bound of the random amount of BBR-native probes. QuicTime::Delta probe_bw_probe_max_rand_duration = - QuicTime::Delta::FromSeconds(1); + QuicTime::Delta::FromMilliseconds( + GetQuicFlag(FLAGS_quic_bbr2_default_probe_bw_max_rand_duration_ms)); // Multiplier to get target inflight (as multiple of BDP) for PROBE_UP phase. float probe_bw_probe_inflight_gain = 1.25; @@ -131,6 +133,8 @@ struct QUIC_EXPORT_PRIVATE Bbr2Params { * PROBE_RTT parameters. */ float probe_rtt_inflight_target_bdp_fraction = 0.5; + QuicTime::Delta probe_rtt_period = QuicTime::Delta::FromMilliseconds( + GetQuicFlag(FLAGS_quic_bbr2_default_probe_rtt_period_ms)); QuicTime::Delta probe_rtt_duration = QuicTime::Delta::FromMilliseconds(200); /* @@ -144,7 +148,7 @@ struct QUIC_EXPORT_PRIVATE Bbr2Params { float inflight_hi_headroom = 0.15; // Estimate startup/bw probing has gone too far if loss rate exceeds this. - float loss_threshold = 0.02; + float loss_threshold = GetQuicFlag(FLAGS_quic_bbr2_default_loss_threshold); Limits<QuicByteCount> cwnd_limits; }; @@ -267,32 +271,6 @@ struct QUIC_EXPORT_PRIVATE Bbr2CongestionEvent { QUIC_EXPORT_PRIVATE const SendTimeState& SendStateOfLargestPacket( const Bbr2CongestionEvent& congestion_event); -class QUIC_EXPORT_PRIVATE Bbr2MaxAckHeightTracker { - public: - explicit Bbr2MaxAckHeightTracker(QuicRoundTripCount initial_filter_window) - : max_ack_height_filter_(initial_filter_window, 0, 0) {} - - QuicByteCount Get() const { return max_ack_height_filter_.GetBest(); } - - QuicByteCount Update(const QuicBandwidth& bandwidth_estimate, - QuicRoundTripCount round_trip_count, - QuicTime ack_time, - QuicByteCount bytes_acked); - - private: - // Tracks the maximum number of bytes acked faster than the sending rate. - typedef WindowedFilter<QuicByteCount, - MaxFilter<QuicByteCount>, - QuicRoundTripCount, - QuicRoundTripCount> - MaxAckHeightFilter; - MaxAckHeightFilter max_ack_height_filter_; - - // The time this aggregation started and the number of bytes acked during it. - QuicTime aggregation_epoch_start_time_ = QuicTime::Zero(); - QuicByteCount aggregation_epoch_bytes_ = 0; -}; - // Bbr2NetworkModel takes low level congestion signals(packets sent/acked/lost) // as input and produces BBRv2 model parameters like inflight_(hi|lo), // bandwidth_(hi|lo), bandwidth and rtt estimates, etc. @@ -348,7 +326,9 @@ class QUIC_EXPORT_PRIVATE Bbr2NetworkModel { QuicBandwidth MaxBandwidth() const { return max_bandwidth_filter_.Get(); } - QuicByteCount MaxAckHeight() const { return max_ack_height_tracker_.Get(); } + QuicByteCount MaxAckHeight() const { + return bandwidth_sampler_.max_ack_height(); + } bool MaybeExpireMinRtt(const Bbr2CongestionEvent& congestion_event); @@ -428,8 +408,6 @@ class QUIC_EXPORT_PRIVATE Bbr2NetworkModel { Bbr2MaxBandwidthFilter max_bandwidth_filter_; MinRttFilter min_rtt_filter_; - Bbr2MaxAckHeightTracker max_ack_height_tracker_; - // Bytes lost in the current round. Updated once per congestion event. QuicByteCount bytes_lost_in_round_ = 0; diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_sender.cc b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_sender.cc index 66aa123d78a..9cd40b93621 100644 --- a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_sender.cc +++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_sender.cc @@ -69,8 +69,9 @@ Bbr2Sender::Bbr2Sender(QuicTime now, rtt_stats->last_update_time(), /*cwnd_gain=*/1.0, /*pacing_gain=*/kInitialPacingGain), - cwnd_( + initial_cwnd_( cwnd_limits().ApplyLimits(initial_cwnd_in_packets * kDefaultTCPMSS)), + cwnd_(initial_cwnd_), pacing_rate_(kInitialPacingGain * QuicBandwidth::FromBytesAndTimeDelta( cwnd_, rtt_stats->SmoothedOrInitialRtt())), @@ -240,7 +241,7 @@ void Bbr2Sender::UpdateCongestionWindow(QuicByteCount bytes_acked) { if (startup_.FullBandwidthReached()) { target_cwnd += model_.MaxAckHeight(); cwnd_ = std::min(prior_cwnd + bytes_acked, target_cwnd); - } else if (prior_cwnd < target_cwnd || prior_cwnd < 2 * cwnd_limits().Min()) { + } else if (prior_cwnd < target_cwnd || prior_cwnd < 2 * initial_cwnd_) { cwnd_ = prior_cwnd + bytes_acked; } const QuicByteCount desired_cwnd = cwnd_; diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_sender.h b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_sender.h index 64f63477558..b58dd93e271 100644 --- a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_sender.h +++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_sender.h @@ -161,6 +161,8 @@ class QUIC_EXPORT_PRIVATE Bbr2Sender final : public SendAlgorithmInterface { Bbr2NetworkModel model_; + const QuicByteCount initial_cwnd_; + // Current cwnd and pacing rate. QuicByteCount cwnd_; QuicBandwidth pacing_rate_; diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_simulator_test.cc b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_simulator_test.cc index 736327fc0d4..ab4b67b2bcc 100644 --- a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_simulator_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_simulator_test.cc @@ -426,13 +426,13 @@ TEST_F(Bbr2DefaultTopologyTest, BandwidthIncrease) { sender_endpoint_.AddBytesToTransfer(20 * 1024 * 1024); - simulator_.RunFor(QuicTime::Delta::FromSeconds(10)); + simulator_.RunFor(QuicTime::Delta::FromSeconds(15)); EXPECT_TRUE(Bbr2ModeIsOneOf({Bbr2Mode::PROBE_BW, Bbr2Mode::PROBE_RTT})); QUIC_LOG(INFO) << "Bandwidth increasing at time " << SimulatedNow(); EXPECT_APPROX_EQ(params.test_link.bandwidth, sender_->ExportDebugState().bandwidth_est, 0.1f); - EXPECT_LE(sender_loss_rate_in_packets(), 0.20); + EXPECT_LE(sender_loss_rate_in_packets(), 0.30); // Now increase the bottleneck bandwidth from 100Kbps to 10Mbps. params.test_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(10000); diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr_sender.cc b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr_sender.cc index 65a637c1611..ae1432a1956 100644 --- a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr_sender.cc +++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr_sender.cc @@ -90,6 +90,7 @@ BbrSender::BbrSender(QuicTime now, random_(random), stats_(stats), mode_(STARTUP), + sampler_(unacked_packets, kBandwidthWindowSize), round_trip_count_(0), max_bandwidth_(kBandwidthWindowSize, QuicBandwidth::Zero(), 0), max_ack_height_(kBandwidthWindowSize, 0, 0), @@ -174,8 +175,10 @@ void BbrSender::OnPacketSent(QuicTime sent_time, exiting_quiescence_ = true; } - if (!aggregation_epoch_start_time_.IsInitialized()) { - aggregation_epoch_start_time_ = sent_time; + if (!sampler_.quic_track_ack_height_in_bandwidth_sampler()) { + if (!aggregation_epoch_start_time_.IsInitialized()) { + aggregation_epoch_start_time_ = sent_time; + } } sampler_.OnPacketSent(sent_time, packet_number, bytes, bytes_in_flight, @@ -284,10 +287,18 @@ void BbrSender::SetFromConfig(const QuicConfig& config, startup_rate_reduction_multiplier_ = 2; } if (config.HasClientRequestedIndependentOption(kBBR4, perspective)) { - max_ack_height_.SetWindowLength(2 * kBandwidthWindowSize); + if (sampler_.quic_track_ack_height_in_bandwidth_sampler()) { + sampler_.SetMaxAckHeightTrackerWindowLength(2 * kBandwidthWindowSize); + } else { + max_ack_height_.SetWindowLength(2 * kBandwidthWindowSize); + } } if (config.HasClientRequestedIndependentOption(kBBR5, perspective)) { - max_ack_height_.SetWindowLength(4 * kBandwidthWindowSize); + if (sampler_.quic_track_ack_height_in_bandwidth_sampler()) { + sampler_.SetMaxAckHeightTrackerWindowLength(4 * kBandwidthWindowSize); + } else { + max_ack_height_.SetWindowLength(4 * kBandwidthWindowSize); + } } if (GetQuicReloadableFlag(quic_bbr_less_probe_rtt) && config.HasClientRequestedIndependentOption(kBBR6, perspective)) { @@ -405,7 +416,10 @@ void BbrSender::OnCongestionEvent(bool /*rtt_updated*/, const QuicByteCount bytes_acked = sampler_.total_bytes_acked() - total_bytes_acked_before; - excess_acked = UpdateAckAggregationBytes(event_time, bytes_acked); + excess_acked = sampler_.quic_track_ack_height_in_bandwidth_sampler() + ? sampler_.OnAckEventEnd(max_bandwidth_.GetBest(), + round_trip_count_) + : UpdateAckAggregationBytes(event_time, bytes_acked); } // Handle logic specific to PROBE_BW mode. @@ -647,7 +661,11 @@ void BbrSender::CheckIfFullBandwidthReached() { rounds_without_bandwidth_gain_ = 0; if (expire_ack_aggregation_in_startup_) { // Expire old excess delivery measurements now that bandwidth increased. - max_ack_height_.Reset(0, round_trip_count_); + if (sampler_.quic_track_ack_height_in_bandwidth_sampler()) { + sampler_.ResetMaxAckHeightTracker(0, round_trip_count_); + } else { + max_ack_height_.Reset(0, round_trip_count_); + } } return; } @@ -847,7 +865,9 @@ void BbrSender::CalculateCongestionWindow(QuicByteCount bytes_acked, GetTargetCongestionWindow(congestion_window_gain_); if (is_at_full_bandwidth_) { // Add the max recently measured ack aggregation to CWND. - target_window += max_ack_height_.GetBest(); + target_window += sampler_.quic_track_ack_height_in_bandwidth_sampler() + ? sampler_.max_ack_height() + : max_ack_height_.GetBest(); } else if (enable_ack_aggregation_during_startup_) { // Add the most recent excess acked. Because CWND never decreases in // STARTUP, this will automatically create a very localized max filter. diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr_sender.h b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr_sender.h index a4d9925b535..592893ef661 100644 --- a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr_sender.h +++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr_sender.h @@ -25,8 +25,6 @@ namespace quic { class RttStats; -typedef uint64_t QuicRoundTripCount; - // BbrSender implements BBR congestion control algorithm. BBR aims to estimate // the current available Bottleneck Bandwidth and RTT (hence the name), and // regulates the pacing rate and the size of the congestion window based on diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/general_loss_algorithm.cc b/chromium/net/third_party/quiche/src/quic/core/congestion_control/general_loss_algorithm.cc index d42896cee57..356501f7f92 100644 --- a/chromium/net/third_party/quiche/src/quic/core/congestion_control/general_loss_algorithm.cc +++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/general_loss_algorithm.cc @@ -66,11 +66,7 @@ void GeneralLossAlgorithm::DetectLosses( loss_detection_timeout_ = QuicTime::Zero(); if (!packets_acked.empty() && packets_acked.front().packet_number == least_in_flight_) { - if (GetQuicReloadableFlag(quic_fix_packets_acked)) { - QUIC_RELOADABLE_FLAG_COUNT(quic_fix_packets_acked); - } - if ((!GetQuicReloadableFlag(quic_fix_packets_acked) || - packets_acked.back().packet_number == largest_newly_acked) && + if (packets_acked.back().packet_number == largest_newly_acked && least_in_flight_ + packets_acked.size() - 1 == largest_newly_acked) { // Optimization for the case when no packet is missing. Please note, // packets_acked can include packets of different packet number space, so diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/general_loss_algorithm_test.cc b/chromium/net/third_party/quiche/src/quic/core/congestion_control/general_loss_algorithm_test.cc index f2ebbb2a68b..4fdd05f20c5 100644 --- a/chromium/net/third_party/quiche/src/quic/core/congestion_control/general_loss_algorithm_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/general_loss_algorithm_test.cc @@ -34,8 +34,9 @@ class GeneralLossAlgorithmTest : public QuicTest { void SendDataPacket(uint64_t packet_number) { QuicStreamFrame frame; - frame.stream_id = QuicUtils::GetHeadersStreamId( - CurrentSupportedVersions()[0].transport_version); + frame.stream_id = QuicUtils::GetFirstBidirectionalStreamId( + CurrentSupportedVersions()[0].transport_version, + Perspective::IS_CLIENT); SerializedPacket packet(QuicPacketNumber(packet_number), PACKET_1BYTE_PACKET_NUMBER, nullptr, kDefaultLength, false, false); diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/rtt_stats.h b/chromium/net/third_party/quiche/src/quic/core/congestion_control/rtt_stats.h index 7b58276aa86..997faa402e8 100644 --- a/chromium/net/third_party/quiche/src/quic/core/congestion_control/rtt_stats.h +++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/rtt_stats.h @@ -73,7 +73,10 @@ class QUIC_EXPORT_PRIVATE RttStats { QuicTime::Delta mean_deviation() const { return mean_deviation_; } - QuicTime::Delta max_ack_delay() const { return max_ack_delay_; } + QuicTime::Delta max_ack_delay() const { + DCHECK(!GetQuicReloadableFlag(quic_sent_packet_manager_cleanup)); + return max_ack_delay_; + } QuicTime last_update_time() const { return last_update_time_; } @@ -101,6 +104,7 @@ class QUIC_EXPORT_PRIVATE RttStats { QuicTime::Delta initial_rtt_; // The maximum ack delay observed over the connection after excluding ack // delays that were too large to be included in an RTT measurement. + // TODO(ianswett): Remove when deprecating quic_sent_packet_manager_cleanup. QuicTime::Delta max_ack_delay_; QuicTime last_update_time_; // Whether to ignore the peer's max ack delay. diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/rtt_stats_test.cc b/chromium/net/third_party/quiche/src/quic/core/congestion_control/rtt_stats_test.cc index 83cb15578a8..4d259d3b248 100644 --- a/chromium/net/third_party/quiche/src/quic/core/congestion_control/rtt_stats_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/rtt_stats_test.cc @@ -35,20 +35,28 @@ TEST_F(RttStatsTest, SmoothedRtt) { QuicTime::Zero()); EXPECT_EQ(QuicTime::Delta::FromMilliseconds(300), rtt_stats_.latest_rtt()); EXPECT_EQ(QuicTime::Delta::FromMilliseconds(300), rtt_stats_.smoothed_rtt()); - EXPECT_EQ(QuicTime::Delta::Zero(), rtt_stats_.max_ack_delay()); + if (!GetQuicReloadableFlag(quic_sent_packet_manager_cleanup)) { + EXPECT_EQ(QuicTime::Delta::Zero(), rtt_stats_.max_ack_delay()); + } // Verify that a plausible ack delay increases the max ack delay. rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(400), QuicTime::Delta::FromMilliseconds(100), QuicTime::Zero()); EXPECT_EQ(QuicTime::Delta::FromMilliseconds(300), rtt_stats_.latest_rtt()); EXPECT_EQ(QuicTime::Delta::FromMilliseconds(300), rtt_stats_.smoothed_rtt()); - EXPECT_EQ(QuicTime::Delta::FromMilliseconds(100), rtt_stats_.max_ack_delay()); + if (!GetQuicReloadableFlag(quic_sent_packet_manager_cleanup)) { + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(100), + rtt_stats_.max_ack_delay()); + } // Verify that Smoothed RTT includes max ack delay if it's reasonable. rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(350), QuicTime::Delta::FromMilliseconds(50), QuicTime::Zero()); EXPECT_EQ(QuicTime::Delta::FromMilliseconds(300), rtt_stats_.latest_rtt()); EXPECT_EQ(QuicTime::Delta::FromMilliseconds(300), rtt_stats_.smoothed_rtt()); - EXPECT_EQ(QuicTime::Delta::FromMilliseconds(100), rtt_stats_.max_ack_delay()); + if (!GetQuicReloadableFlag(quic_sent_packet_manager_cleanup)) { + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(100), + rtt_stats_.max_ack_delay()); + } // Verify that large erroneous ack_delay does not change Smoothed RTT. rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(200), QuicTime::Delta::FromMilliseconds(300), @@ -56,7 +64,10 @@ TEST_F(RttStatsTest, SmoothedRtt) { EXPECT_EQ(QuicTime::Delta::FromMilliseconds(200), rtt_stats_.latest_rtt()); EXPECT_EQ(QuicTime::Delta::FromMicroseconds(287500), rtt_stats_.smoothed_rtt()); - EXPECT_EQ(QuicTime::Delta::FromMilliseconds(100), rtt_stats_.max_ack_delay()); + if (!GetQuicReloadableFlag(quic_sent_packet_manager_cleanup)) { + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(100), + rtt_stats_.max_ack_delay()); + } } TEST_F(RttStatsTest, SmoothedRttIgnoreAckDelay) { @@ -67,14 +78,18 @@ TEST_F(RttStatsTest, SmoothedRttIgnoreAckDelay) { QuicTime::Zero()); EXPECT_EQ(QuicTime::Delta::FromMilliseconds(300), rtt_stats_.latest_rtt()); EXPECT_EQ(QuicTime::Delta::FromMilliseconds(300), rtt_stats_.smoothed_rtt()); - EXPECT_EQ(QuicTime::Delta::Zero(), rtt_stats_.max_ack_delay()); + if (!GetQuicReloadableFlag(quic_sent_packet_manager_cleanup)) { + EXPECT_EQ(QuicTime::Delta::Zero(), rtt_stats_.max_ack_delay()); + } // Verify that a plausible ack delay increases the max ack delay. rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(300), QuicTime::Delta::FromMilliseconds(100), QuicTime::Zero()); EXPECT_EQ(QuicTime::Delta::FromMilliseconds(300), rtt_stats_.latest_rtt()); EXPECT_EQ(QuicTime::Delta::FromMilliseconds(300), rtt_stats_.smoothed_rtt()); - EXPECT_EQ(QuicTime::Delta::Zero(), rtt_stats_.max_ack_delay()); + if (!GetQuicReloadableFlag(quic_sent_packet_manager_cleanup)) { + EXPECT_EQ(QuicTime::Delta::Zero(), rtt_stats_.max_ack_delay()); + } // Verify that Smoothed RTT includes max ack delay if it's reasonable. rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(300), QuicTime::Delta::FromMilliseconds(50), QuicTime::Zero()); @@ -87,7 +102,9 @@ TEST_F(RttStatsTest, SmoothedRttIgnoreAckDelay) { EXPECT_EQ(QuicTime::Delta::FromMilliseconds(200), rtt_stats_.latest_rtt()); EXPECT_EQ(QuicTime::Delta::FromMicroseconds(287500), rtt_stats_.smoothed_rtt()); - EXPECT_EQ(QuicTime::Delta::Zero(), rtt_stats_.max_ack_delay()); + if (!GetQuicReloadableFlag(quic_sent_packet_manager_cleanup)) { + EXPECT_EQ(QuicTime::Delta::Zero(), rtt_stats_.max_ack_delay()); + } } // Ensure that the potential rounding artifacts in EWMA calculation do not cause @@ -208,7 +225,9 @@ TEST_F(RttStatsTest, ResetAfterConnectionMigrations) { EXPECT_EQ(QuicTime::Delta::FromMilliseconds(200), rtt_stats_.latest_rtt()); EXPECT_EQ(QuicTime::Delta::FromMilliseconds(200), rtt_stats_.smoothed_rtt()); EXPECT_EQ(QuicTime::Delta::FromMilliseconds(200), rtt_stats_.min_rtt()); - EXPECT_EQ(QuicTime::Delta::FromMilliseconds(0), rtt_stats_.max_ack_delay()); + if (!GetQuicReloadableFlag(quic_sent_packet_manager_cleanup)) { + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(0), rtt_stats_.max_ack_delay()); + } rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(300), QuicTime::Delta::FromMilliseconds(100), @@ -216,14 +235,19 @@ TEST_F(RttStatsTest, ResetAfterConnectionMigrations) { EXPECT_EQ(QuicTime::Delta::FromMilliseconds(200), rtt_stats_.latest_rtt()); EXPECT_EQ(QuicTime::Delta::FromMilliseconds(200), rtt_stats_.smoothed_rtt()); EXPECT_EQ(QuicTime::Delta::FromMilliseconds(200), rtt_stats_.min_rtt()); - EXPECT_EQ(QuicTime::Delta::FromMilliseconds(100), rtt_stats_.max_ack_delay()); + if (!GetQuicReloadableFlag(quic_sent_packet_manager_cleanup)) { + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(100), + rtt_stats_.max_ack_delay()); + } // Reset rtt stats on connection migrations. rtt_stats_.OnConnectionMigration(); EXPECT_EQ(QuicTime::Delta::Zero(), rtt_stats_.latest_rtt()); EXPECT_EQ(QuicTime::Delta::Zero(), rtt_stats_.smoothed_rtt()); EXPECT_EQ(QuicTime::Delta::Zero(), rtt_stats_.min_rtt()); - EXPECT_EQ(QuicTime::Delta::Zero(), rtt_stats_.max_ack_delay()); + if (!GetQuicReloadableFlag(quic_sent_packet_manager_cleanup)) { + EXPECT_EQ(QuicTime::Delta::Zero(), rtt_stats_.max_ack_delay()); + } } } // namespace test diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/send_algorithm_interface.h b/chromium/net/third_party/quiche/src/quic/core/congestion_control/send_algorithm_interface.h index 69f7455219c..dab8fc294a7 100644 --- a/chromium/net/third_party/quiche/src/quic/core/congestion_control/send_algorithm_interface.h +++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/send_algorithm_interface.h @@ -24,6 +24,8 @@ namespace quic { +typedef uint64_t QuicRoundTripCount; + class CachedNetworkParameters; class RttStats; diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/uber_loss_algorithm_test.cc b/chromium/net/third_party/quiche/src/quic/core/congestion_control/uber_loss_algorithm_test.cc index 0c79ac55352..84b9c993ced 100644 --- a/chromium/net/third_party/quiche/src/quic/core/congestion_control/uber_loss_algorithm_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/uber_loss_algorithm_test.cc @@ -32,7 +32,8 @@ class UberLossAlgorithmTest : public QuicTest { QuicStreamFrame frame; QuicTransportVersion version = CurrentSupportedVersions()[0].transport_version; - frame.stream_id = QuicUtils::GetHeadersStreamId(version); + frame.stream_id = QuicUtils::GetFirstBidirectionalStreamId( + version, Perspective::IS_CLIENT); if (encryption_level == ENCRYPTION_INITIAL) { if (QuicVersionUsesCryptoFrames(version)) { frame.stream_id = QuicUtils::GetFirstBidirectionalStreamId( @@ -183,13 +184,8 @@ TEST_F(UberLossAlgorithmTest, PacketInLimbo) { AckPackets({5, 6}); unacked_packets_->MaybeUpdateLargestAckedOfPacketNumberSpace( APPLICATION_DATA, QuicPacketNumber(6)); - if (GetQuicReloadableFlag(quic_fix_packets_acked)) { - // Verify packet 2 is detected lost. - VerifyLosses(6, packets_acked_, std::vector<uint64_t>{2}); - } else { - // No losses, packet 2 is in limbo. - VerifyLosses(6, packets_acked_, std::vector<uint64_t>{}); - } + // Verify packet 2 is detected lost. + VerifyLosses(6, packets_acked_, std::vector<uint64_t>{2}); } } // namespace diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_handshake_message.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_handshake_message.cc index bf1df2b5396..022a86b9227 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_handshake_message.cc +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_handshake_message.cc @@ -278,6 +278,7 @@ std::string CryptoHandshakeMessage::DebugStringInternal(size_t indent) const { case kMIBS: case kSCLS: case kTCID: + case kMAD: // uint32_t value if (it->second.size() == 4) { uint32_t value; diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h index f2033e1228b..812aae082a9 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h @@ -170,6 +170,14 @@ const QuicTag kSMHL = TAG('S', 'M', 'H', 'L'); // Support MAX_HEADER_LIST_SIZE const QuicTag kNSTP = TAG('N', 'S', 'T', 'P'); // No stop waiting frames. const QuicTag kNRTT = TAG('N', 'R', 'T', 'T'); // Ignore initial RTT +const QuicTag k1PTO = TAG('1', 'P', 'T', 'O'); // Send 1 packet upon PTO. +const QuicTag k2PTO = TAG('2', 'P', 'T', 'O'); // Send 2 packets upon PTO. + +const QuicTag k7PTO = TAG('7', 'P', 'T', 'O'); // Closes connection on 7 + // consecutive PTOs. +const QuicTag k8PTO = TAG('8', 'P', 'T', 'O'); // Closes connection on 8 + // consecutive PTOs. + // Optional support of truncated Connection IDs. If sent by a peer, the value // is the minimum number of bytes allowed for the connection ID sent to the // peer. @@ -197,6 +205,13 @@ const QuicTag kBWS5 = TAG('B', 'W', 'S', '5'); // QUIC Initial CWND up and down const QuicTag kMTUH = TAG('M', 'T', 'U', 'H'); // High-target MTU discovery. const QuicTag kMTUL = TAG('M', 'T', 'U', 'L'); // Low-target MTU discovery. +// Enable Priority scheme experiment. +const QuicTag kH2PR = TAG('H', '2', 'P', 'R'); // HTTP2 priorities. +const QuicTag kFIFO = TAG('F', 'I', 'F', 'O'); // Stream with the smallest ID + // has the highest priority. +const QuicTag kLIFO = TAG('L', 'I', 'F', 'O'); // Stream with the largest ID + // has the highest priority. + // Proof types (i.e. certificate types) // NOTE: although it would be silly to do so, specifying both kX509 and kX59R // is allowed and is equivalent to specifying only kX509. @@ -219,6 +234,8 @@ const QuicTag kICSL = TAG('I', 'C', 'S', 'L'); // Idle network timeout const QuicTag kSCLS = TAG('S', 'C', 'L', 'S'); // Silently close on timeout const QuicTag kMIBS = TAG('M', 'I', 'D', 'S'); // Max incoming bidi streams const QuicTag kMIUS = TAG('M', 'I', 'U', 'S'); // Max incoming unidi streams +const QuicTag kADE = TAG('A', 'D', 'E', 0); // Ack Delay Exponent (IETF + // QUIC ACK Frame Only). const QuicTag kIRTT = TAG('I', 'R', 'T', 'T'); // Estimated initial RTT in us. const QuicTag kSNI = TAG('S', 'N', 'I', '\0'); // Server name // indication @@ -238,6 +255,8 @@ const QuicTag kCFCW = TAG('C', 'F', 'C', 'W'); // Initial session/connection const QuicTag kUAID = TAG('U', 'A', 'I', 'D'); // Client's User Agent ID. const QuicTag kXLCT = TAG('X', 'L', 'C', 'T'); // Expected leaf certificate. +const QuicTag kMAD = TAG('M', 'A', 'D', 0); // Max Ack Delay (IETF QUIC) + // Rejection tags const QuicTag kRREJ = TAG('R', 'R', 'E', 'J'); // Reasons for server sending diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_secret_boxer.h b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_secret_boxer.h index 395d3892d4f..eba7a57baea 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_secret_boxer.h +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_secret_boxer.h @@ -59,7 +59,7 @@ class QUIC_EXPORT_PRIVATE CryptoSecretBoxer { // state_ is an opaque pointer to whatever additional state the concrete // implementation of CryptoSecretBoxer requires. - std::unique_ptr<State> state_ GUARDED_BY(lock_); + std::unique_ptr<State> state_ QUIC_GUARDED_BY(lock_); }; } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_server_test.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_server_test.cc index 5cc4d363c95..3216ea85b27 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_server_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_server_test.cc @@ -103,7 +103,10 @@ class CryptoServerTest : public QuicTestWithParam<TestParams> { config_.set_enable_serving_sct(true); client_version_ = supported_versions_.front(); - client_version_string_ = ParsedQuicVersionToString(client_version_); + client_version_label_ = CreateQuicVersionLabel(client_version_); + client_version_string_ = + std::string(reinterpret_cast<const char*>(&client_version_label_), + sizeof(client_version_label_)); } void SetUp() override { @@ -265,7 +268,6 @@ class CryptoServerTest : public QuicTestWithParam<TestParams> { } else { ASSERT_NE(error, QUIC_NO_ERROR) << "Message didn't fail: " << result_->client_hello.DebugString(); - EXPECT_TRUE(error_details.find(error_substr_) != std::string::npos) << error_substr_ << " not in " << error_details; } @@ -341,6 +343,7 @@ class CryptoServerTest : public QuicTestWithParam<TestParams> { QuicSocketAddress client_address_; ParsedQuicVersionVector supported_versions_; ParsedQuicVersion client_version_; + QuicVersionLabel client_version_label_; std::string client_version_string_; QuicCryptoServerConfig config_; QuicCryptoServerConfigPeer peer_; @@ -497,9 +500,8 @@ TEST_P(CryptoServerTest, RejectTooLargeButValidSTK) { TEST_P(CryptoServerTest, TooSmall) { ShouldFailMentioning( - "too small", - crypto_test_utils::CreateCHLO( - {{"PDMD", "X509"}, {"VER\0", client_version_string_.c_str()}})); + "too small", crypto_test_utils::CreateCHLO( + {{"PDMD", "X509"}, {"VER\0", client_version_string_}})); const HandshakeFailureReason kRejectReasons[] = { SERVER_CONFIG_INCHOATE_HELLO_FAILURE}; diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_utils.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_utils.cc index 085a65a52bb..642799e7792 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_utils.cc +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_utils.cc @@ -88,6 +88,8 @@ void CryptoUtils::SetKeyAndIV(const EVP_MD* prf, namespace { +static_assert(kQuicIetfDraftVersion == 22, "Salts do not match draft version"); +// Salt from https://tools.ietf.org/html/draft-ietf-quic-tls-22#section-5.2 const uint8_t kInitialSalt[] = {0x7f, 0xbc, 0xdb, 0x0e, 0x7c, 0x66, 0xbb, 0xe9, 0x19, 0x3a, 0x96, 0xcd, 0x21, 0x51, 0x9e, 0xbd, 0x7a, 0x02, 0x64, 0x4a}; diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.cc index 50ea2f88264..862dc622b2f 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.cc +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.cc @@ -1490,24 +1490,18 @@ void QuicCryptoServerConfig::BuildRejection( out->SetStringPiece(kPROF, context.signed_config()->proof.signature); if (should_return_sct) { if (cert_sct.empty()) { - if (!GetQuicReloadableFlag(quic_log_cert_name_for_empty_sct)) { - QUIC_LOG_EVERY_N_SEC(WARNING, 60) - << "SCT is expected but it is empty. sni :" - << context.params()->sni; - } else { - // Log SNI and subject name for the leaf cert if its SCT is empty. - // This is for debugging b/28342827. - const std::vector<std::string>& certs = - context.signed_config()->chain->certs; - QuicStringPiece ca_subject; - if (!certs.empty()) { - QuicCertUtils::ExtractSubjectNameFromDERCert(certs[0], &ca_subject); - } - QUIC_LOG_EVERY_N_SEC(WARNING, 60) - << "SCT is expected but it is empty. sni: '" - << context.params()->sni << "' cert subject: '" << ca_subject - << "'"; + // Log SNI and subject name for the leaf cert if its SCT is empty. + // This is for debugging b/28342827. + const std::vector<std::string>& certs = + context.signed_config()->chain->certs; + QuicStringPiece ca_subject; + if (!certs.empty()) { + QuicCertUtils::ExtractSubjectNameFromDERCert(certs[0], &ca_subject); } + QUIC_LOG_EVERY_N_SEC(WARNING, 60) + << "SCT is expected but it is empty. sni: '" + << context.params()->sni << "' cert subject: '" << ca_subject + << "'"; } else { out->SetStringPiece(kCertificateSCTTag, cert_sct); } diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.h b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.h index d8d9cab7a51..3fb424d4022 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.h +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.h @@ -508,7 +508,7 @@ class QUIC_EXPORT_PRIVATE QuicCryptoServerConfig { // Get a ref to the config with a given server config id. QuicReferenceCountedPointer<Config> GetConfigWithScid( QuicStringPiece requested_scid) const - SHARED_LOCKS_REQUIRED(configs_lock_); + QUIC_SHARED_LOCKS_REQUIRED(configs_lock_); // A snapshot of the configs associated with an in-progress handshake. struct Configs { @@ -537,7 +537,7 @@ class QUIC_EXPORT_PRIVATE QuicCryptoServerConfig { // SelectNewPrimaryConfig reevaluates the primary config based on the // "primary_time" deadlines contained in each. void SelectNewPrimaryConfig(QuicWallTime now) const - EXCLUSIVE_LOCKS_REQUIRED(configs_lock_); + QUIC_EXCLUSIVE_LOCKS_REQUIRED(configs_lock_); // EvaluateClientHello checks |client_hello_state->client_hello| for gross // errors and determines whether it is fresh (i.e. not a replay). The results @@ -850,7 +850,7 @@ class QUIC_EXPORT_PRIVATE QuicCryptoServerConfig { // Returns true if the next config promotion should happen now. bool IsNextConfigReady(QuicWallTime now) const - SHARED_LOCKS_REQUIRED(configs_lock_); + QUIC_SHARED_LOCKS_REQUIRED(configs_lock_); // replay_protection_ controls whether the server enforces that handshakes // aren't replays. @@ -869,12 +869,12 @@ class QUIC_EXPORT_PRIVATE QuicCryptoServerConfig { // configs_ contains all active server configs. It's expected that there are // about half-a-dozen configs active at any one time. - ConfigMap configs_ GUARDED_BY(configs_lock_); + ConfigMap configs_ QUIC_GUARDED_BY(configs_lock_); // primary_config_ points to a Config (which is also in |configs_|) which is // the primary config - i.e. the one that we'll give out to new clients. mutable QuicReferenceCountedPointer<Config> primary_config_ - GUARDED_BY(configs_lock_); + QUIC_GUARDED_BY(configs_lock_); // fallback_config_ points to a Config (which is also in |configs_|) which is // the fallback config, which will be used if the other configs are unuseable @@ -882,15 +882,16 @@ class QUIC_EXPORT_PRIVATE QuicCryptoServerConfig { // // TODO(b/112548056): This is currently always nullptr. QuicReferenceCountedPointer<Config> fallback_config_ - GUARDED_BY(configs_lock_); + QUIC_GUARDED_BY(configs_lock_); // next_config_promotion_time_ contains the nearest, future time when an // active config will be promoted to primary. - mutable QuicWallTime next_config_promotion_time_ GUARDED_BY(configs_lock_); + mutable QuicWallTime next_config_promotion_time_ + QUIC_GUARDED_BY(configs_lock_); // Callback to invoke when the primary config changes. std::unique_ptr<PrimaryConfigChangedCallback> primary_config_changed_cb_ - GUARDED_BY(configs_lock_); + QUIC_GUARDED_BY(configs_lock_); // Used to protect the source-address tokens that are given to clients. CryptoSecretBoxer source_address_token_boxer_; diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/tls_connection.h b/chromium/net/third_party/quiche/src/quic/core/crypto/tls_connection.h index c15d9202af4..4774ba6924d 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/tls_connection.h +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/tls_connection.h @@ -69,7 +69,7 @@ class QUIC_EXPORT_PRIVATE TlsConnection { static enum ssl_encryption_level_t BoringEncryptionLevel( EncryptionLevel level); - SSL* ssl() { return ssl_.get(); } + SSL* ssl() const { return ssl_.get(); } protected: // TlsConnection does not take ownership of any of its arguments; they must diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/transport_parameters.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/transport_parameters.cc index 247f4071c32..8f2349cd335 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/transport_parameters.cc +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/transport_parameters.cc @@ -13,6 +13,7 @@ #include "net/third_party/quiche/src/quic/core/quic_data_reader.h" #include "net/third_party/quiche/src/quic/core/quic_data_writer.h" #include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" #include "net/third_party/quiche/src/quic/core/quic_versions.h" #include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" #include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" @@ -365,7 +366,8 @@ bool TransportParameters::AreValid() const { TransportParameters::~TransportParameters() = default; -bool SerializeTransportParameters(const TransportParameters& in, +bool SerializeTransportParameters(ParsedQuicVersion /*version*/, + const TransportParameters& in, std::vector<uint8_t>* out) { if (!in.AreValid()) { QUIC_DLOG(ERROR) << "Not serializing invalid transport parameters " << in; @@ -543,9 +545,10 @@ bool SerializeTransportParameters(const TransportParameters& in, return true; } -bool ParseTransportParameters(const uint8_t* in, - size_t in_len, +bool ParseTransportParameters(ParsedQuicVersion version, Perspective perspective, + const uint8_t* in, + size_t in_len, TransportParameters* out) { out->perspective = perspective; CBS cbs; @@ -572,22 +575,25 @@ bool ParseTransportParameters(const uint8_t* in, } bool parse_success = true; switch (param_id) { - case TransportParameters::kOriginalConnectionId: + case TransportParameters::kOriginalConnectionId: { if (!out->original_connection_id.IsEmpty()) { QUIC_DLOG(ERROR) << "Received a second original connection ID"; return false; } - if (CBS_len(&value) > static_cast<size_t>(kQuicMaxConnectionIdLength)) { + const size_t connection_id_length = CBS_len(&value); + if (!QuicUtils::IsConnectionIdLengthValidForVersion( + connection_id_length, version.transport_version)) { QUIC_DLOG(ERROR) << "Received original connection ID of " - << "invalid length " << CBS_len(&value); + << "invalid length " << connection_id_length; return false; } - if (CBS_len(&value) != 0) { - out->original_connection_id.set_length(CBS_len(&value)); + out->original_connection_id.set_length( + static_cast<uint8_t>(connection_id_length)); + if (out->original_connection_id.length() != 0) { memcpy(out->original_connection_id.mutable_data(), CBS_data(&value), - CBS_len(&value)); + out->original_connection_id.length()); } - break; + } break; case TransportParameters::kIdleTimeout: parse_success = out->idle_timeout_milliseconds.ReadFromCbs(&value); break; @@ -675,11 +681,15 @@ bool ParseTransportParameters(const uint8_t* in, << "Failed to parse length of preferred address connection ID"; return false; } - if (CBS_len(&connection_id_cbs) > kQuicMaxConnectionIdLength) { - QUIC_DLOG(ERROR) << "Bad preferred address connection ID length"; + const size_t connection_id_length = CBS_len(&connection_id_cbs); + if (!QuicUtils::IsConnectionIdLengthValidForVersion( + connection_id_length, version.transport_version)) { + QUIC_DLOG(ERROR) << "Received preferred address connection ID of " + << "invalid length " << connection_id_length; return false; } - preferred_address.connection_id.set_length(CBS_len(&connection_id_cbs)); + preferred_address.connection_id.set_length( + static_cast<uint8_t>(connection_id_length)); if (preferred_address.connection_id.length() > 0 && !CBS_copy_bytes(&connection_id_cbs, reinterpret_cast<uint8_t*>( diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/transport_parameters.h b/chromium/net/third_party/quiche/src/quic/core/crypto/transport_parameters.h index 368a7bf6aba..95a01dac8d9 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/transport_parameters.h +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/transport_parameters.h @@ -185,6 +185,7 @@ struct QUIC_EXPORT_PRIVATE TransportParameters { // TLS extension. The serialized bytes are written to |*out|. Returns if the // parameters are valid and serialization succeeded. QUIC_EXPORT_PRIVATE bool SerializeTransportParameters( + ParsedQuicVersion version, const TransportParameters& in, std::vector<uint8_t>* out); @@ -193,9 +194,10 @@ QUIC_EXPORT_PRIVATE bool SerializeTransportParameters( // |perspective| indicates whether the input came from a client or a server. // This method returns true if the input was successfully parsed. // TODO(nharper): Write fuzz tests for this method. -QUIC_EXPORT_PRIVATE bool ParseTransportParameters(const uint8_t* in, - size_t in_len, +QUIC_EXPORT_PRIVATE bool ParseTransportParameters(ParsedQuicVersion version, Perspective perspective, + const uint8_t* in, + size_t in_len, TransportParameters* out); } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/transport_parameters_test.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/transport_parameters_test.cc index 3f7e339339b..7379224a366 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/transport_parameters_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/transport_parameters_test.cc @@ -7,6 +7,7 @@ #include <cstring> #include "third_party/boringssl/src/include/openssl/mem.h" +#include "net/third_party/quiche/src/quic/core/quic_versions.h" #include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" #include "net/third_party/quiche/src/quic/platform/api/quic_ip_address.h" #include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" @@ -16,6 +17,7 @@ namespace quic { namespace test { namespace { +const ParsedQuicVersion kVersion(PROTOCOL_TLS1_3, QUIC_VERSION_99); const QuicVersionLabel kFakeVersionLabel = 0x01234567; const QuicVersionLabel kFakeVersionLabel2 = 0x89ABCDEF; const QuicConnectionId kFakeOriginalConnectionId = TestConnectionId(0x1337); @@ -101,11 +103,12 @@ TEST_F(TransportParametersTest, RoundTripClient) { kFakeActiveConnectionIdLimit); std::vector<uint8_t> serialized; - ASSERT_TRUE(SerializeTransportParameters(orig_params, &serialized)); + ASSERT_TRUE(SerializeTransportParameters(kVersion, orig_params, &serialized)); TransportParameters new_params; - ASSERT_TRUE(ParseTransportParameters(serialized.data(), serialized.size(), - Perspective::IS_CLIENT, &new_params)); + ASSERT_TRUE(ParseTransportParameters(kVersion, Perspective::IS_CLIENT, + serialized.data(), serialized.size(), + &new_params)); EXPECT_EQ(Perspective::IS_CLIENT, new_params.perspective); EXPECT_EQ(kFakeVersionLabel, new_params.version); @@ -160,11 +163,12 @@ TEST_F(TransportParametersTest, RoundTripServer) { kFakeActiveConnectionIdLimit); std::vector<uint8_t> serialized; - ASSERT_TRUE(SerializeTransportParameters(orig_params, &serialized)); + ASSERT_TRUE(SerializeTransportParameters(kVersion, orig_params, &serialized)); TransportParameters new_params; - ASSERT_TRUE(ParseTransportParameters(serialized.data(), serialized.size(), - Perspective::IS_SERVER, &new_params)); + ASSERT_TRUE(ParseTransportParameters(kVersion, Perspective::IS_SERVER, + serialized.data(), serialized.size(), + &new_params)); EXPECT_EQ(Perspective::IS_SERVER, new_params.perspective); EXPECT_EQ(kFakeVersionLabel, new_params.version); @@ -255,7 +259,7 @@ TEST_F(TransportParametersTest, NoClientParamsWithStatelessResetToken) { orig_params.max_packet_size.set_value(kFakeMaxPacketSize); std::vector<uint8_t> out; - EXPECT_FALSE(SerializeTransportParameters(orig_params, &out)); + EXPECT_FALSE(SerializeTransportParameters(kVersion, orig_params, &out)); } TEST_F(TransportParametersTest, ParseClientParams) { @@ -317,9 +321,9 @@ TEST_F(TransportParametersTest, ParseClientParams) { // clang-format on TransportParameters new_params; - ASSERT_TRUE(ParseTransportParameters(kClientParams, - QUIC_ARRAYSIZE(kClientParams), - Perspective::IS_CLIENT, &new_params)); + ASSERT_TRUE( + ParseTransportParameters(kVersion, Perspective::IS_CLIENT, kClientParams, + QUIC_ARRAYSIZE(kClientParams), &new_params)); EXPECT_EQ(Perspective::IS_CLIENT, new_params.perspective); EXPECT_EQ(kFakeVersionLabel, new_params.version); @@ -374,8 +378,8 @@ TEST_F(TransportParametersTest, ParseClientParamsFailsWithStatelessResetToken) { // clang-format on EXPECT_FALSE(ParseTransportParameters( - kClientParamsWithFullToken, QUIC_ARRAYSIZE(kClientParamsWithFullToken), - Perspective::IS_CLIENT, &out_params)); + kVersion, Perspective::IS_CLIENT, kClientParamsWithFullToken, + QUIC_ARRAYSIZE(kClientParamsWithFullToken), &out_params)); // clang-format off const uint8_t kClientParamsWithEmptyToken[] = { @@ -399,8 +403,8 @@ TEST_F(TransportParametersTest, ParseClientParamsFailsWithStatelessResetToken) { // clang-format on EXPECT_FALSE(ParseTransportParameters( - kClientParamsWithEmptyToken, QUIC_ARRAYSIZE(kClientParamsWithEmptyToken), - Perspective::IS_CLIENT, &out_params)); + kVersion, Perspective::IS_CLIENT, kClientParamsWithEmptyToken, + QUIC_ARRAYSIZE(kClientParamsWithEmptyToken), &out_params)); } TEST_F(TransportParametersTest, ParseClientParametersRepeated) { @@ -425,9 +429,9 @@ TEST_F(TransportParametersTest, ParseClientParametersRepeated) { }; // clang-format on TransportParameters out_params; - EXPECT_FALSE(ParseTransportParameters(kClientParamsRepeated, - QUIC_ARRAYSIZE(kClientParamsRepeated), - Perspective::IS_CLIENT, &out_params)); + EXPECT_FALSE(ParseTransportParameters( + kVersion, Perspective::IS_CLIENT, kClientParamsRepeated, + QUIC_ARRAYSIZE(kClientParamsRepeated), &out_params)); } TEST_F(TransportParametersTest, ParseServerParams) { @@ -513,9 +517,9 @@ TEST_F(TransportParametersTest, ParseServerParams) { // clang-format on TransportParameters new_params; - ASSERT_TRUE(ParseTransportParameters(kServerParams, - QUIC_ARRAYSIZE(kServerParams), - Perspective::IS_SERVER, &new_params)); + ASSERT_TRUE( + ParseTransportParameters(kVersion, Perspective::IS_SERVER, kServerParams, + QUIC_ARRAYSIZE(kServerParams), &new_params)); EXPECT_EQ(Perspective::IS_SERVER, new_params.perspective); EXPECT_EQ(kFakeVersionLabel, new_params.version); @@ -579,9 +583,9 @@ TEST_F(TransportParametersTest, ParseServerParametersRepeated) { // clang-format on TransportParameters out_params; - EXPECT_FALSE(ParseTransportParameters(kServerParamsRepeated, - QUIC_ARRAYSIZE(kServerParamsRepeated), - Perspective::IS_SERVER, &out_params)); + EXPECT_FALSE(ParseTransportParameters( + kVersion, Perspective::IS_SERVER, kServerParamsRepeated, + QUIC_ARRAYSIZE(kServerParamsRepeated), &out_params)); } TEST_F(TransportParametersTest, CryptoHandshakeMessageRoundtrip) { @@ -597,11 +601,12 @@ TEST_F(TransportParametersTest, CryptoHandshakeMessageRoundtrip) { orig_params.google_quic_params->SetValue(1337, kTestValue); std::vector<uint8_t> serialized; - ASSERT_TRUE(SerializeTransportParameters(orig_params, &serialized)); + ASSERT_TRUE(SerializeTransportParameters(kVersion, orig_params, &serialized)); TransportParameters new_params; - ASSERT_TRUE(ParseTransportParameters(serialized.data(), serialized.size(), - Perspective::IS_CLIENT, &new_params)); + ASSERT_TRUE(ParseTransportParameters(kVersion, Perspective::IS_CLIENT, + serialized.data(), serialized.size(), + &new_params)); ASSERT_NE(new_params.google_quic_params.get(), nullptr); EXPECT_EQ(new_params.google_quic_params->tag(), diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_connection_close_frame.cc b/chromium/net/third_party/quiche/src/quic/core/frames/quic_connection_close_frame.cc index d436499ca0c..a7b3d53e0ef 100644 --- a/chromium/net/third_party/quiche/src/quic/core/frames/quic_connection_close_frame.cc +++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_connection_close_frame.cc @@ -6,6 +6,8 @@ #include <memory> +#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h" + namespace quic { QuicConnectionCloseFrame::QuicConnectionCloseFrame() @@ -20,7 +22,7 @@ QuicConnectionCloseFrame::QuicConnectionCloseFrame(QuicErrorCode error_code, // Default close type ensures that existing, pre-V99 code works as expected. : close_type(GOOGLE_QUIC_CONNECTION_CLOSE), quic_error_code(error_code), - extracted_error_code(QUIC_IETF_GQUIC_ERROR_MISSING), + extracted_error_code(error_code), error_details(std::move(error_details)), transport_close_frame_type(0) {} @@ -49,39 +51,28 @@ std::ostream& operator<<( std::ostream& os, const QuicConnectionCloseFrame& connection_close_frame) { os << "{ Close type: " << connection_close_frame.close_type - << ", error_code: " - << ((connection_close_frame.close_type == - IETF_QUIC_TRANSPORT_CONNECTION_CLOSE) - ? static_cast<uint16_t>( - connection_close_frame.transport_error_code) - : ((connection_close_frame.close_type == - IETF_QUIC_APPLICATION_CONNECTION_CLOSE) - ? connection_close_frame.application_error_code - : static_cast<uint16_t>( - connection_close_frame.quic_error_code))) - << ", extracted_error_code: " - << connection_close_frame.extracted_error_code << ", error_details: '" - << connection_close_frame.error_details - << "', frame_type: " << connection_close_frame.transport_close_frame_type - << "}\n"; - return os; -} - -std::ostream& operator<<(std::ostream& os, const QuicConnectionCloseType type) { - switch (type) { - case GOOGLE_QUIC_CONNECTION_CLOSE: - os << "GOOGLE_QUIC_CONNECTION_CLOSE"; - break; + << ", error_code: "; + switch (connection_close_frame.close_type) { case IETF_QUIC_TRANSPORT_CONNECTION_CLOSE: - os << "IETF_QUIC_TRANSPORT_CONNECTION_CLOSE"; + os << connection_close_frame.transport_error_code; break; case IETF_QUIC_APPLICATION_CONNECTION_CLOSE: - os << "IETF_QUIC_APPLICATION_CONNECTION_CLOSE"; + os << connection_close_frame.application_error_code; break; - default: - os << "Unknown: " << static_cast<int>(type); + case GOOGLE_QUIC_CONNECTION_CLOSE: + os << connection_close_frame.quic_error_code; break; } + os << ", extracted_error_code: " + << QuicErrorCodeToString(connection_close_frame.extracted_error_code) + << ", error_details: '" << connection_close_frame.error_details << "'"; + if (connection_close_frame.close_type == + IETF_QUIC_TRANSPORT_CONNECTION_CLOSE) { + os << ", frame_type: " + << static_cast<QuicIetfFrameType>( + connection_close_frame.transport_close_frame_type); + } + os << "}\n"; return os; } diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_connection_close_frame.h b/chromium/net/third_party/quiche/src/quic/core/frames/quic_connection_close_frame.h index aba67b9a88f..b4f4fd70971 100644 --- a/chromium/net/third_party/quiche/src/quic/core/frames/quic_connection_close_frame.h +++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_connection_close_frame.h @@ -14,16 +14,6 @@ namespace quic { -// There are three different forms of CONNECTION_CLOSE. -typedef enum QuicConnectionCloseType { - GOOGLE_QUIC_CONNECTION_CLOSE = 0, - IETF_QUIC_TRANSPORT_CONNECTION_CLOSE = 1, - IETF_QUIC_APPLICATION_CONNECTION_CLOSE = 2 -} QuicConnectionCloseType; -QUIC_EXPORT_PRIVATE std::ostream& operator<<( - std::ostream& os, - const QuicConnectionCloseType type); - struct QUIC_EXPORT_PRIVATE QuicConnectionCloseFrame { QuicConnectionCloseFrame(); @@ -64,9 +54,10 @@ struct QUIC_EXPORT_PRIVATE QuicConnectionCloseFrame { QuicErrorCode quic_error_code; }; - // This error code is extracted from, or added to, the "QuicErrorCode: - // QUIC_...(123)" text in the error_details. It provides fine-grained - // information as to the source of the error. + // For IETF QUIC frames, this is the error code is extracted from, or added + // to, the error details text. For received Google QUIC frames, the Google + // QUIC error code from the frame's error code field is copied here (as well + // as in quic_error_code, above). QuicErrorCode extracted_error_code; // String with additional error details. "QuicErrorCode: 123" will be appended diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_frames_test.cc b/chromium/net/third_party/quiche/src/quic/core/frames/quic_frames_test.cc index 3467c7849f1..b1030ee3b7f 100644 --- a/chromium/net/third_party/quiche/src/quic/core/frames/quic_frames_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_frames_test.cc @@ -148,10 +148,31 @@ TEST_F(QuicFramesTest, ConnectionCloseFrameToString) { // underlying frame. EXPECT_EQ( "{ Close type: GOOGLE_QUIC_CONNECTION_CLOSE, error_code: 25, " - "extracted_error_code: 122, " + "extracted_error_code: QUIC_IETF_GQUIC_ERROR_MISSING, " + "error_details: 'No recent " + "network activity.'" + "}\n", + stream.str()); + QuicFrame quic_frame(&frame); + EXPECT_FALSE(IsControlFrame(quic_frame.type)); +} + +TEST_F(QuicFramesTest, TransportConnectionCloseFrameToString) { + QuicConnectionCloseFrame frame; + frame.close_type = IETF_QUIC_TRANSPORT_CONNECTION_CLOSE; + frame.transport_error_code = FINAL_SIZE_ERROR; + frame.extracted_error_code = QUIC_NETWORK_IDLE_TIMEOUT; + frame.error_details = "No recent network activity."; + frame.transport_close_frame_type = IETF_STREAM; + std::ostringstream stream; + stream << frame; + EXPECT_EQ( + "{ Close type: IETF_QUIC_TRANSPORT_CONNECTION_CLOSE, error_code: " + "FINAL_SIZE_ERROR, " + "extracted_error_code: QUIC_NETWORK_IDLE_TIMEOUT, " "error_details: 'No recent " "network activity.', " - "frame_type: 0" + "frame_type: IETF_STREAM" "}\n", stream.str()); QuicFrame quic_frame(&frame); diff --git a/chromium/net/third_party/quiche/src/quic/core/http/end_to_end_test.cc b/chromium/net/third_party/quiche/src/quic/core/http/end_to_end_test.cc index 578df127ddd..3a6d5149230 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/end_to_end_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/http/end_to_end_test.cc @@ -15,6 +15,7 @@ #include "net/third_party/quiche/src/quic/core/http/http_constants.h" #include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_stream.h" #include "net/third_party/quiche/src/quic/core/qpack/qpack_encoder_test_utils.h" +#include "net/third_party/quiche/src/quic/core/quic_data_writer.h" #include "net/third_party/quiche/src/quic/core/quic_epoll_connection_helper.h" #include "net/third_party/quiche/src/quic/core/quic_error_codes.h" #include "net/third_party/quiche/src/quic/core/quic_framer.h" @@ -85,11 +86,13 @@ struct TestParams { TestParams(const ParsedQuicVersionVector& client_supported_versions, const ParsedQuicVersionVector& server_supported_versions, ParsedQuicVersion negotiated_version, - QuicTag congestion_control_tag) + QuicTag congestion_control_tag, + QuicTag priority_tag) : client_supported_versions(client_supported_versions), server_supported_versions(server_supported_versions), negotiated_version(negotiated_version), - congestion_control_tag(congestion_control_tag) {} + congestion_control_tag(congestion_control_tag), + priority_tag(priority_tag) {} friend std::ostream& operator<<(std::ostream& os, const TestParams& p) { os << "{ server_supported_versions: " @@ -99,7 +102,8 @@ struct TestParams { os << " negotiated_version: " << ParsedQuicVersionToString(p.negotiated_version); os << " congestion_control_tag: " - << QuicTagToString(p.congestion_control_tag) << " }"; + << QuicTagToString(p.congestion_control_tag); + os << " priority_tag: " << QuicTagToString(p.priority_tag) << " }"; return os; } @@ -107,6 +111,7 @@ struct TestParams { ParsedQuicVersionVector server_supported_versions; ParsedQuicVersion negotiated_version; QuicTag congestion_control_tag; + QuicTag priority_tag; }; // Constructs various test permutations. @@ -153,28 +158,32 @@ std::vector<TestParams> GetTestParams(bool use_tls_handshake) { if (FilterSupportedVersions(client_versions).empty()) { continue; } - // Add an entry for server and client supporting all versions. - params.push_back(TestParams(client_versions, all_supported_versions, - client_versions.front(), - congestion_control_tag)); - - // Test client supporting all versions and server supporting - // 1 version. Simulate an old server and exercise version - // downgrade in the client. Protocol negotiation should - // occur. Skip the i = 0 case because it is essentially the - // same as the default case. - for (size_t i = 1; i < client_versions.size(); ++i) { - ParsedQuicVersionVector server_supported_versions; - server_supported_versions.push_back(client_versions[i]); - if (FilterSupportedVersions(server_supported_versions).empty()) { - continue; - } - params.push_back(TestParams(client_versions, server_supported_versions, - server_supported_versions.front(), - congestion_control_tag)); - } // End of inner version loop. - } // End of outer version loop. - } // End of congestion_control_tag loop. + for (const QuicTag priority_tag : + {/*no tag*/ static_cast<QuicTag>(0), kH2PR, kFIFO, kLIFO}) { + // Add an entry for server and client supporting all versions. + params.push_back(TestParams(client_versions, all_supported_versions, + client_versions.front(), + congestion_control_tag, priority_tag)); + + // Test client supporting all versions and server supporting + // 1 version. Simulate an old server and exercise version + // downgrade in the client. Protocol negotiation should + // occur. Skip the i = 0 case because it is essentially the + // same as the default case. + for (size_t i = 1; i < client_versions.size(); ++i) { + ParsedQuicVersionVector server_supported_versions; + server_supported_versions.push_back(client_versions[i]); + if (FilterSupportedVersions(server_supported_versions).empty()) { + continue; + } + params.push_back(TestParams(client_versions, + server_supported_versions, + server_supported_versions.front(), + congestion_control_tag, priority_tag)); + } // End of inner version loop. + } // End of priority_tag loop. + } // End of outer version loop. + } // End of congestion_control_tag loop. return params; } @@ -216,8 +225,8 @@ class EndToEndTest : public QuicTestWithParam<TestParams> { EndToEndTest() : initialized_(false), connect_to_server_on_initialize_(true), - server_address_( - QuicSocketAddress(TestLoopback(), QuicPickUnusedPortOrDie())), + server_address_(QuicSocketAddress(TestLoopback(), + QuicPickServerPortForTestsOrDie())), server_hostname_("test.example.com"), client_writer_(nullptr), server_writer_(nullptr), @@ -225,10 +234,9 @@ class EndToEndTest : public QuicTestWithParam<TestParams> { chlo_multiplier_(0), stream_factory_(nullptr), support_server_push_(false), - override_server_connection_id_(nullptr), - override_client_connection_id_(nullptr), expected_server_connection_id_length_(kQuicDefaultConnectionIdLength) { SetQuicFlag(FLAGS_quic_supports_tls_handshake, true); + SetQuicReloadableFlag(quic_simplify_stop_waiting, true); client_supported_versions_ = GetParam().client_supported_versions; server_supported_versions_ = GetParam().server_supported_versions; negotiated_version_ = GetParam().negotiated_version; @@ -272,13 +280,11 @@ class EndToEndTest : public QuicTestWithParam<TestParams> { if (!pre_shared_key_client_.empty()) { client->client()->SetPreSharedKey(pre_shared_key_client_); } - if (override_server_connection_id_ != nullptr) { - client->UseConnectionId(*override_server_connection_id_); + client->UseConnectionIdLength(override_server_connection_id_length_); + client->UseClientConnectionIdLength(override_client_connection_id_length_); + if (support_server_push_) { + client->client()->set_max_allowed_push_id(kMaxQuicStreamId); } - if (override_client_connection_id_ != nullptr) { - client->UseClientConnectionId(*override_client_connection_id_); - } - client->client()->set_max_allowed_push_id(kMaxQuicStreamId); client->Connect(); return client; } @@ -326,15 +332,24 @@ class EndToEndTest : public QuicTestWithParam<TestParams> { return &GetServerConnection()->sent_packet_manager(); } + QuicSpdyClientSession* GetClientSession() { + return client_->client()->client_session(); + } + + QuicConnection* GetClientConnection() { + return GetClientSession()->connection(); + } + QuicConnection* GetServerConnection() { return GetServerSession()->connection(); } - QuicSession* GetServerSession() { + QuicSpdySession* GetServerSession() { QuicDispatcher* dispatcher = QuicServerPeer::GetDispatcher(server_thread_->server()); EXPECT_EQ(1u, dispatcher->session_map().size()); - return dispatcher->session_map().begin()->second.get(); + return static_cast<QuicSpdySession*>( + dispatcher->session_map().begin()->second.get()); } bool Initialize() { @@ -349,7 +364,10 @@ class EndToEndTest : public QuicTestWithParam<TestParams> { GetQuicReloadableFlag(quic_enable_pcc3)) { copt.push_back(kTPCC); } - + copt.push_back(GetParam().priority_tag); + if (GetQuicReloadableFlag(quic_enable_pto)) { + copt.push_back(k2PTO); + } client_config_.SetConnectionOptionsToSend(copt); // Start the server first, because CreateQuicClient() attempts @@ -365,10 +383,8 @@ class EndToEndTest : public QuicTestWithParam<TestParams> { static QuicEpollEvent event(EPOLLOUT); if (client_writer_ != nullptr) { client_writer_->Initialize( - QuicConnectionPeer::GetHelper( - client_->client()->client_session()->connection()), - QuicConnectionPeer::GetAlarmFactory( - client_->client()->client_session()->connection()), + QuicConnectionPeer::GetHelper(GetClientConnection()), + QuicConnectionPeer::GetAlarmFactory(GetClientConnection()), QuicMakeUnique<ClientDelegate>(client_->client())); } initialized_ = true; @@ -401,6 +417,8 @@ class EndToEndTest : public QuicTestWithParam<TestParams> { server_thread_->server()->SetPreSharedKey(pre_shared_key_server_); } server_thread_->Initialize(); + server_address_ = + QuicSocketAddress(server_address_.host(), server_thread_->GetPort()); QuicDispatcher* dispatcher = QuicServerPeer::GetDispatcher(server_thread_->server()); QuicDispatcherPeer::UseWriter(dispatcher, server_writer_); @@ -450,8 +468,7 @@ class EndToEndTest : public QuicTestWithParam<TestParams> { // Calls server_thread_ Pause() and Resume(), which may only be called once // per test. void VerifyCleanConnection(bool had_packet_loss) { - QuicConnectionStats client_stats = - client_->client()->client_session()->connection()->GetStats(); + QuicConnectionStats client_stats = GetClientConnection()->GetStats(); // TODO(ianswett): Determine why this becomes even more flaky with BBR // enabled. b/62141144 if (!had_packet_loss && !GetQuicReloadableFlag(quic_default_to_bbr)) { @@ -505,12 +522,23 @@ class EndToEndTest : public QuicTestWithParam<TestParams> { version.handshake_protocol == PROTOCOL_TLS1_3; } - void ExpectFlowControlsSynced(QuicFlowController* client, - QuicFlowController* server) { - EXPECT_EQ(QuicFlowControllerPeer::SendWindowSize(client), - QuicFlowControllerPeer::ReceiveWindowSize(server)); - EXPECT_EQ(QuicFlowControllerPeer::ReceiveWindowSize(client), - QuicFlowControllerPeer::SendWindowSize(server)); + static void ExpectFlowControlsSynced(QuicSession* client, + QuicSession* server) { + EXPECT_EQ( + QuicFlowControllerPeer::SendWindowSize(client->flow_controller()), + QuicFlowControllerPeer::ReceiveWindowSize(server->flow_controller())); + EXPECT_EQ( + QuicFlowControllerPeer::ReceiveWindowSize(client->flow_controller()), + QuicFlowControllerPeer::SendWindowSize(server->flow_controller())); + } + + static void ExpectFlowControlsSynced(QuicStream* client, QuicStream* server) { + EXPECT_EQ( + QuicFlowControllerPeer::SendWindowSize(client->flow_controller()), + QuicFlowControllerPeer::ReceiveWindowSize(server->flow_controller())); + EXPECT_EQ( + QuicFlowControllerPeer::ReceiveWindowSize(client->flow_controller()), + QuicFlowControllerPeer::SendWindowSize(server->flow_controller())); } // Must be called before Initialize to have effect. @@ -520,14 +548,12 @@ class EndToEndTest : public QuicTestWithParam<TestParams> { QuicStreamId GetNthClientInitiatedBidirectionalId(int n) { return GetNthClientInitiatedBidirectionalStreamId( - client_->client()->client_session()->connection()->transport_version(), - n); + GetClientConnection()->transport_version(), n); } QuicStreamId GetNthServerInitiatedBidirectionalId(int n) { return GetNthServerInitiatedBidirectionalStreamId( - client_->client()->client_session()->connection()->transport_version(), - n); + GetClientConnection()->transport_version(), n); } ScopedEnvironmentForThreads environment_; @@ -554,8 +580,8 @@ class EndToEndTest : public QuicTestWithParam<TestParams> { bool support_server_push_; std::string pre_shared_key_client_; std::string pre_shared_key_server_; - QuicConnectionId* override_server_connection_id_; - QuicConnectionId* override_client_connection_id_; + int override_server_connection_id_length_ = -1; + int override_client_connection_id_length_ = -1; uint8_t expected_server_connection_id_length_; }; @@ -578,12 +604,11 @@ TEST_P(EndToEndTestWithTls, HandshakeSuccessful) { // version in the connection are not in sync. If it is happening, it has not // been recreatable; this assert is here just to check and raise a flag if it // happens. - ASSERT_EQ( - client_->client()->client_session()->connection()->transport_version(), - negotiated_version_.transport_version); + ASSERT_EQ(GetClientConnection()->transport_version(), + negotiated_version_.transport_version); - QuicCryptoStream* crypto_stream = QuicSessionPeer::GetMutableCryptoStream( - client_->client()->client_session()); + QuicCryptoStream* crypto_stream = + QuicSessionPeer::GetMutableCryptoStream(GetClientSession()); QuicStreamSequencer* sequencer = QuicStreamPeer::sequencer(crypto_stream); EXPECT_FALSE(QuicStreamSequencerPeer::IsUnderlyingBufferAllocated(sequencer)); server_thread_->Pause(); @@ -603,6 +628,14 @@ TEST_P(EndToEndTest, SimpleRequestResponse) { } EXPECT_EQ(expected_num_client_hellos, client_->client()->GetNumSentClientHellos()); + if (VersionUsesQpack(GetClientConnection()->transport_version())) { + EXPECT_TRUE(QuicSpdySessionPeer::GetSendControlStream(GetClientSession())); + EXPECT_TRUE( + QuicSpdySessionPeer::GetReceiveControlStream(GetClientSession())); + EXPECT_TRUE(QuicSpdySessionPeer::GetSendControlStream(GetServerSession())); + EXPECT_TRUE( + QuicSpdySessionPeer::GetReceiveControlStream(GetServerSession())); + } } TEST_P(EndToEndTestWithTls, SimpleRequestResponse) { @@ -611,7 +644,70 @@ TEST_P(EndToEndTestWithTls, SimpleRequestResponse) { EXPECT_EQ("200", client_->response_headers()->find(":status")->second); } +// Simple transaction, but set a non-default ack delay at the client +// and ensure it gets to the server. +TEST_P(EndToEndTest, SimpleRequestResponseWithAckDelayChange) { + // Force the ACK delay to be something other than the default. + // Note that it is sent only if doing IETF QUIC. + client_config_.SetMaxAckDelayToSendMs(kDefaultDelayedAckTimeMs + 100u); + ASSERT_TRUE(Initialize()); + + EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); + EXPECT_EQ("200", client_->response_headers()->find(":status")->second); + int expected_num_client_hellos = 2; + if (ServerSendsVersionNegotiation()) { + ++expected_num_client_hellos; + } + EXPECT_EQ(expected_num_client_hellos, + client_->client()->GetNumSentClientHellos()); + if (GetQuicReloadableFlag(quic_negotiate_ack_delay_time)) { + EXPECT_EQ(kDefaultDelayedAckTimeMs + 100u, + GetSentPacketManagerFromFirstServerSession() + ->peer_max_ack_delay() + .ToMilliseconds()); + } else { + EXPECT_EQ(kDefaultDelayedAckTimeMs, + GetSentPacketManagerFromFirstServerSession() + ->peer_max_ack_delay() + .ToMilliseconds()); + } +} + +// Simple transaction, but set a non-default ack exponent at the client +// and ensure it gets to the server. +TEST_P(EndToEndTest, SimpleRequestResponseWithAckExponentChange) { + const uint32_t kClientAckDelayExponent = kDefaultAckDelayExponent + 100u; + // Force the ACK exponent to be something other than the default. + // Note that it is sent only if doing IETF QUIC. + client_config_.SetAckDelayExponentToSend(kClientAckDelayExponent); + ASSERT_TRUE(Initialize()); + + EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); + EXPECT_EQ("200", client_->response_headers()->find(":status")->second); + int expected_num_client_hellos = 2; + if (ServerSendsVersionNegotiation()) { + ++expected_num_client_hellos; + } + + EXPECT_EQ(expected_num_client_hellos, + client_->client()->GetNumSentClientHellos()); + if (VersionHasIetfQuicFrames( + GetParam().negotiated_version.transport_version)) { + // Should be only for IETF QUIC. + EXPECT_EQ(kClientAckDelayExponent, + GetServerConnection()->framer().peer_ack_delay_exponent()); + } else { + // No change for Google QUIC. + EXPECT_EQ(kDefaultAckDelayExponent, + GetServerConnection()->framer().peer_ack_delay_exponent()); + } + // No change, regardless of version. + EXPECT_EQ(kDefaultAckDelayExponent, + GetServerConnection()->framer().local_ack_delay_exponent()); +} + TEST_P(EndToEndTest, SimpleRequestResponseForcedVersionNegotiation) { + SetQuicReloadableFlag(quic_use_parse_public_header, true); client_supported_versions_.insert(client_supported_versions_.begin(), QuicVersionReservedForNegotiation()); ASSERT_TRUE(Initialize()); @@ -624,6 +720,7 @@ TEST_P(EndToEndTest, SimpleRequestResponseForcedVersionNegotiation) { } TEST_P(EndToEndTestWithTls, ForcedVersionNegotiation) { + SetQuicReloadableFlag(quic_use_parse_public_header, true); client_supported_versions_.insert(client_supported_versions_.begin(), QuicVersionReservedForNegotiation()); ASSERT_TRUE(Initialize()); @@ -634,10 +731,13 @@ TEST_P(EndToEndTestWithTls, ForcedVersionNegotiation) { } TEST_P(EndToEndTest, SimpleRequestResponseZeroConnectionID) { - QuicConnectionId connection_id = QuicUtils::CreateZeroConnectionId( - GetParam().negotiated_version.transport_version); - override_server_connection_id_ = &connection_id; - expected_server_connection_id_length_ = connection_id.length(); + if (!QuicUtils::VariableLengthConnectionIdAllowedForVersion( + GetParam().negotiated_version.transport_version)) { + ASSERT_TRUE(Initialize()); + return; + } + override_server_connection_id_length_ = 0; + expected_server_connection_id_length_ = 0; ASSERT_TRUE(Initialize()); EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); @@ -648,21 +748,24 @@ TEST_P(EndToEndTest, SimpleRequestResponseZeroConnectionID) { } EXPECT_EQ(expected_num_client_hellos, client_->client()->GetNumSentClientHellos()); - EXPECT_EQ(client_->client()->client_session()->connection()->connection_id(), + EXPECT_EQ(GetClientConnection()->connection_id(), QuicUtils::CreateZeroConnectionId( GetParam().negotiated_version.transport_version)); } TEST_P(EndToEndTestWithTls, ZeroConnectionID) { - QuicConnectionId connection_id = QuicUtils::CreateZeroConnectionId( - GetParam().negotiated_version.transport_version); - override_server_connection_id_ = &connection_id; - expected_server_connection_id_length_ = connection_id.length(); + if (!QuicUtils::VariableLengthConnectionIdAllowedForVersion( + GetParam().negotiated_version.transport_version)) { + ASSERT_TRUE(Initialize()); + return; + } + override_server_connection_id_length_ = 0; + expected_server_connection_id_length_ = 0; ASSERT_TRUE(Initialize()); EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); EXPECT_EQ("200", client_->response_headers()->find(":status")->second); - EXPECT_EQ(client_->client()->client_session()->connection()->connection_id(), + EXPECT_EQ(GetClientConnection()->connection_id(), QuicUtils::CreateZeroConnectionId( GetParam().negotiated_version.transport_version)); } @@ -673,9 +776,7 @@ TEST_P(EndToEndTestWithTls, BadConnectionIdLength) { ASSERT_TRUE(Initialize()); return; } - QuicConnectionId connection_id = - TestConnectionIdNineBytesLong(UINT64_C(0xBADbadBADbad)); - override_server_connection_id_ = &connection_id; + override_server_connection_id_length_ = 9; ASSERT_TRUE(Initialize()); EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); EXPECT_EQ("200", client_->response_headers()->find(":status")->second); @@ -694,12 +795,7 @@ TEST_P(EndToEndTestWithTls, LongBadConnectionIdLength) { ASSERT_TRUE(Initialize()); return; } - const char connection_id_bytes[16] = {0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, - 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, - 0xbc, 0xbd, 0xbe, 0xbf}; - QuicConnectionId connection_id = - QuicConnectionId(connection_id_bytes, sizeof(connection_id_bytes)); - override_server_connection_id_ = &connection_id; + override_server_connection_id_length_ = 16; ASSERT_TRUE(Initialize()); EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); EXPECT_EQ("200", client_->response_headers()->find(":status")->second); @@ -715,16 +811,15 @@ TEST_P(EndToEndTestWithTls, ClientConnectionId) { ASSERT_TRUE(Initialize()); return; } - QuicConnectionId client_connection_id = - TestConnectionId(UINT64_C(0xc1c2c3c4c5c6c7c8)); - override_client_connection_id_ = &client_connection_id; + override_client_connection_id_length_ = kQuicDefaultConnectionIdLength; ASSERT_TRUE(Initialize()); EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); EXPECT_EQ("200", client_->response_headers()->find(":status")->second); - EXPECT_EQ(client_connection_id, client_->client() - ->client_session() - ->connection() - ->client_connection_id()); + EXPECT_EQ(override_client_connection_id_length_, client_->client() + ->client_session() + ->connection() + ->client_connection_id() + .length()); } TEST_P(EndToEndTestWithTls, ForcedVersionNegotiationAndClientConnectionId) { @@ -732,19 +827,19 @@ TEST_P(EndToEndTestWithTls, ForcedVersionNegotiationAndClientConnectionId) { ASSERT_TRUE(Initialize()); return; } + SetQuicReloadableFlag(quic_use_parse_public_header, true); client_supported_versions_.insert(client_supported_versions_.begin(), QuicVersionReservedForNegotiation()); - QuicConnectionId client_connection_id = - TestConnectionId(UINT64_C(0xc1c2c3c4c5c6c7c8)); - override_client_connection_id_ = &client_connection_id; + override_client_connection_id_length_ = kQuicDefaultConnectionIdLength; ASSERT_TRUE(Initialize()); ASSERT_TRUE(ServerSendsVersionNegotiation()); EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); EXPECT_EQ("200", client_->response_headers()->find(":status")->second); - EXPECT_EQ(client_connection_id, client_->client() - ->client_session() - ->connection() - ->client_connection_id()); + EXPECT_EQ(override_client_connection_id_length_, client_->client() + ->client_session() + ->connection() + ->client_connection_id() + .length()); } TEST_P(EndToEndTestWithTls, ForcedVersionNegotiationAndBadConnectionIdLength) { @@ -753,11 +848,10 @@ TEST_P(EndToEndTestWithTls, ForcedVersionNegotiationAndBadConnectionIdLength) { ASSERT_TRUE(Initialize()); return; } + SetQuicReloadableFlag(quic_use_parse_public_header, true); client_supported_versions_.insert(client_supported_versions_.begin(), QuicVersionReservedForNegotiation()); - QuicConnectionId connection_id = - TestConnectionIdNineBytesLong(UINT64_C(0xBADbadBADbad)); - override_server_connection_id_ = &connection_id; + override_server_connection_id_length_ = 9; ASSERT_TRUE(Initialize()); ASSERT_TRUE(ServerSendsVersionNegotiation()); EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); @@ -780,18 +874,8 @@ TEST_P(EndToEndTestWithTls, ForcedVersNegoAndClientCIDAndLongCID) { } client_supported_versions_.insert(client_supported_versions_.begin(), QuicVersionReservedForNegotiation()); - const char connection_id_bytes[16] = {0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, - 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, - 0xbc, 0xbd, 0xbe, 0xbf}; - QuicConnectionId connection_id = - QuicConnectionId(connection_id_bytes, sizeof(connection_id_bytes)); - override_server_connection_id_ = &connection_id; - const char client_connection_id_bytes[18] = { - 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, - 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xc0, 0xc1}; - QuicConnectionId client_connection_id = QuicConnectionId( - client_connection_id_bytes, sizeof(client_connection_id_bytes)); - override_client_connection_id_ = &client_connection_id; + override_server_connection_id_length_ = 16; + override_client_connection_id_length_ = 18; ASSERT_TRUE(Initialize()); ASSERT_TRUE(ServerSendsVersionNegotiation()); EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); @@ -801,10 +885,11 @@ TEST_P(EndToEndTestWithTls, ForcedVersNegoAndClientCIDAndLongCID) { ->connection() ->connection_id() .length()); - EXPECT_EQ(client_connection_id, client_->client() - ->client_session() - ->connection() - ->client_connection_id()); + EXPECT_EQ(override_client_connection_id_length_, client_->client() + ->client_session() + ->connection() + ->client_connection_id() + .length()); } TEST_P(EndToEndTest, MixGoodAndBadConnectionIdLengths) { @@ -815,11 +900,9 @@ TEST_P(EndToEndTest, MixGoodAndBadConnectionIdLengths) { } // Start client_ which will use a bad connection ID length. - QuicConnectionId connection_id = - TestConnectionIdNineBytesLong(UINT64_C(0xBADbadBADbad)); - override_server_connection_id_ = &connection_id; + override_server_connection_id_length_ = 9; ASSERT_TRUE(Initialize()); - override_server_connection_id_ = nullptr; + override_server_connection_id_length_ = -1; // Start client2 which will use a good connection ID length. std::unique_ptr<QuicTestClient> client2(CreateQuicClient(nullptr)); @@ -856,7 +939,7 @@ TEST_P(EndToEndTestWithTls, SimpleRequestResponseWithIetfDraftSupport) { ASSERT_TRUE(Initialize()); return; } - QuicVersionInitializeSupportForIetfDraft(1); + QuicVersionInitializeSupportForIetfDraft(); ASSERT_TRUE(Initialize()); EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); @@ -891,8 +974,7 @@ TEST_P(EndToEndTestWithTls, NoUndecryptablePackets) { EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); EXPECT_EQ("200", client_->response_headers()->find(":status")->second); - QuicConnectionStats client_stats = - client_->client()->client_session()->connection()->GetStats(); + QuicConnectionStats client_stats = GetClientConnection()->GetStats(); EXPECT_EQ(0u, client_stats.undecryptable_packets_received); server_thread_->Pause(); @@ -935,10 +1017,13 @@ TEST_P(EndToEndTestWithTls, MultipleRequestResponse) { } TEST_P(EndToEndTest, MultipleRequestResponseZeroConnectionID) { - QuicConnectionId connection_id = QuicUtils::CreateZeroConnectionId( - GetParam().negotiated_version.transport_version); - override_server_connection_id_ = &connection_id; - expected_server_connection_id_length_ = connection_id.length(); + if (!QuicUtils::VariableLengthConnectionIdAllowedForVersion( + GetParam().negotiated_version.transport_version)) { + ASSERT_TRUE(Initialize()); + return; + } + override_server_connection_id_length_ = 0; + expected_server_connection_id_length_ = 0; ASSERT_TRUE(Initialize()); EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); @@ -1195,7 +1280,7 @@ TEST_P(EndToEndTest, LargePostZeroRTTFailure) { client_->SendCustomSynchronousRequest(headers, body)); // The same session is used for both hellos, so the number of hellos sent on // that session is 2. - EXPECT_EQ(2, client_->client()->client_session()->GetNumSentClientHellos()); + EXPECT_EQ(2, GetClientSession()->GetNumSentClientHellos()); if (ServerSendsVersionNegotiation()) { EXPECT_EQ(3, client_->client()->GetNumSentClientHellos()); } else { @@ -1211,7 +1296,7 @@ TEST_P(EndToEndTest, LargePostZeroRTTFailure) { EXPECT_EQ(kFooResponseBody, client_->SendCustomSynchronousRequest(headers, body)); - EXPECT_EQ(1, client_->client()->client_session()->GetNumSentClientHellos()); + EXPECT_EQ(1, GetClientSession()->GetNumSentClientHellos()); if (ServerSendsVersionNegotiation()) { EXPECT_EQ(2, client_->client()->GetNumSentClientHellos()); } else { @@ -1232,7 +1317,7 @@ TEST_P(EndToEndTest, LargePostZeroRTTFailure) { client_->SendCustomSynchronousRequest(headers, body)); // The same session is used for both hellos, so the number of hellos sent on // that session is 2. - EXPECT_EQ(2, client_->client()->client_session()->GetNumSentClientHellos()); + EXPECT_EQ(2, GetClientSession()->GetNumSentClientHellos()); if (ServerSendsVersionNegotiation()) { EXPECT_EQ(3, client_->client()->GetNumSentClientHellos()); } else { @@ -1250,7 +1335,7 @@ TEST_P(EndToEndTest, SynchronousRequestZeroRTTFailure) { EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); // The same session is used for both hellos, so the number of hellos sent on // that session is 2. - EXPECT_EQ(2, client_->client()->client_session()->GetNumSentClientHellos()); + EXPECT_EQ(2, GetClientSession()->GetNumSentClientHellos()); if (ServerSendsVersionNegotiation()) { EXPECT_EQ(3, client_->client()->GetNumSentClientHellos()); } else { @@ -1265,7 +1350,7 @@ TEST_P(EndToEndTest, SynchronousRequestZeroRTTFailure) { ASSERT_TRUE(client_->client()->connected()); EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); - EXPECT_EQ(1, client_->client()->client_session()->GetNumSentClientHellos()); + EXPECT_EQ(1, GetClientSession()->GetNumSentClientHellos()); if (ServerSendsVersionNegotiation()) { EXPECT_EQ(2, client_->client()->GetNumSentClientHellos()); } else { @@ -1284,7 +1369,7 @@ TEST_P(EndToEndTest, SynchronousRequestZeroRTTFailure) { ASSERT_TRUE(client_->client()->connected()); EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); - EXPECT_EQ(2, client_->client()->client_session()->GetNumSentClientHellos()); + EXPECT_EQ(2, GetClientSession()->GetNumSentClientHellos()); if (ServerSendsVersionNegotiation()) { EXPECT_EQ(3, client_->client()->GetNumSentClientHellos()); } else { @@ -1310,7 +1395,7 @@ TEST_P(EndToEndTest, LargePostSynchronousRequest) { client_->SendCustomSynchronousRequest(headers, body)); // The same session is used for both hellos, so the number of hellos sent on // that session is 2. - EXPECT_EQ(2, client_->client()->client_session()->GetNumSentClientHellos()); + EXPECT_EQ(2, GetClientSession()->GetNumSentClientHellos()); if (ServerSendsVersionNegotiation()) { EXPECT_EQ(3, client_->client()->GetNumSentClientHellos()); } else { @@ -1326,7 +1411,7 @@ TEST_P(EndToEndTest, LargePostSynchronousRequest) { EXPECT_EQ(kFooResponseBody, client_->SendCustomSynchronousRequest(headers, body)); - EXPECT_EQ(1, client_->client()->client_session()->GetNumSentClientHellos()); + EXPECT_EQ(1, GetClientSession()->GetNumSentClientHellos()); if (ServerSendsVersionNegotiation()) { EXPECT_EQ(2, client_->client()->GetNumSentClientHellos()); } else { @@ -1346,7 +1431,7 @@ TEST_P(EndToEndTest, LargePostSynchronousRequest) { EXPECT_EQ(kFooResponseBody, client_->SendCustomSynchronousRequest(headers, body)); - EXPECT_EQ(2, client_->client()->client_session()->GetNumSentClientHellos()); + EXPECT_EQ(2, GetClientSession()->GetNumSentClientHellos()); if (ServerSendsVersionNegotiation()) { EXPECT_EQ(3, client_->client()->GetNumSentClientHellos()); } else { @@ -1429,7 +1514,7 @@ TEST_P(EndToEndTestWithTls, DoNotSetSendAlarmIfConnectionFlowControlBlocked) { const uint64_t flow_control_window = server_config_.GetInitialStreamFlowControlWindowToSend(); QuicSpdyClientStream* stream = client_->GetOrCreateStream(); - QuicSession* session = client_->client()->client_session(); + QuicSession* session = GetClientSession(); QuicFlowControllerPeer::SetSendWindowOffset(stream->flow_controller(), 0); QuicFlowControllerPeer::SetSendWindowOffset(session->flow_controller(), 0); EXPECT_TRUE(stream->flow_controller()->IsBlocked()); @@ -1471,7 +1556,7 @@ TEST_P(EndToEndTest, InvalidStream) { // Force the client to write with a stream ID belonging to a nonexistent // server-side stream. - QuicSpdySession* session = client_->client()->client_session(); + QuicSpdySession* session = GetClientSession(); QuicSessionPeer::SetNextOutgoingBidirectionalStreamId( session, GetNthServerInitiatedBidirectionalId(0)); @@ -1585,8 +1670,8 @@ TEST_P(EndToEndTestWithTls, MaxIncomingDynamicStreamsLimitRespected) { // Make the client misbehave after negotiation. const int kServerMaxStreams = kMaxStreamsMinimumIncrement + 1; - QuicSessionPeer::SetMaxOpenOutgoingStreams( - client_->client()->client_session(), kServerMaxStreams + 1); + QuicSessionPeer::SetMaxOpenOutgoingStreams(GetClientSession(), + kServerMaxStreams + 1); SpdyHeaderBlock headers; headers[":method"] = "POST"; @@ -1609,8 +1694,8 @@ TEST_P(EndToEndTestWithTls, MaxIncomingDynamicStreamsLimitRespected) { TEST_P(EndToEndTest, SetIndependentMaxIncomingDynamicStreamsLimits) { // Each endpoint can set max incoming dynamic streams independently. - const uint32_t kClientMaxIncomingDynamicStreams = 2; - const uint32_t kServerMaxIncomingDynamicStreams = 1; + const uint32_t kClientMaxIncomingDynamicStreams = 4; + const uint32_t kServerMaxIncomingDynamicStreams = 3; client_config_.SetMaxIncomingBidirectionalStreamsToSend( kClientMaxIncomingDynamicStreams); server_config_.SetMaxIncomingBidirectionalStreamsToSend( @@ -1624,7 +1709,7 @@ TEST_P(EndToEndTest, SetIndependentMaxIncomingDynamicStreamsLimits) { EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); // The client has received the server's limit and vice versa. - QuicSpdyClientSession* client_session = client_->client()->client_session(); + QuicSpdyClientSession* client_session = GetClientSession(); // The value returned by max_allowed... includes the Crypto and Header // stream (created as a part of initialization). The config. values, // above, are treated as "number of requests/responses" - that is, they do @@ -1635,10 +1720,7 @@ TEST_P(EndToEndTest, SetIndependentMaxIncomingDynamicStreamsLimits) { VersionHasIetfQuicFrames( client_session->connection()->transport_version()) ? QuicSessionPeer::v99_streamid_manager(client_session) - ->max_allowed_outgoing_bidirectional_streams() - - QuicSessionPeer::v99_bidirectional_stream_id_manager( - client_session) - ->outgoing_static_stream_count() + ->max_allowed_outgoing_bidirectional_streams() : QuicSessionPeer::GetStreamIdManager(client_session) ->max_open_outgoing_streams(); size_t client_max_open_outgoing_unidirectional_streams = @@ -1646,9 +1728,7 @@ TEST_P(EndToEndTest, SetIndependentMaxIncomingDynamicStreamsLimits) { client_session->connection()->transport_version()) ? QuicSessionPeer::v99_streamid_manager(client_session) ->max_allowed_outgoing_unidirectional_streams() - - QuicSessionPeer::v99_unidirectional_stream_id_manager( - client_session) - ->outgoing_static_stream_count() + client_session->num_expected_unidirectional_static_streams() : QuicSessionPeer::GetStreamIdManager(client_session) ->max_open_outgoing_streams(); EXPECT_EQ(kServerMaxIncomingDynamicStreams, @@ -1669,15 +1749,14 @@ TEST_P(EndToEndTest, SetIndependentMaxIncomingDynamicStreamsLimits) { server_session->connection()->transport_version()) ? QuicSessionPeer::v99_streamid_manager(server_session) ->max_allowed_outgoing_unidirectional_streams() - - QuicSessionPeer::v99_unidirectional_stream_id_manager( - server_session) - ->outgoing_static_stream_count() + server_session->num_expected_unidirectional_static_streams() : QuicSessionPeer::GetStreamIdManager(server_session) ->max_open_outgoing_streams(); EXPECT_EQ(kClientMaxIncomingDynamicStreams, server_max_open_outgoing_bidirectional_streams); EXPECT_EQ(kClientMaxIncomingDynamicStreams, server_max_open_outgoing_unidirectional_streams); + server_thread_->Resume(); } @@ -1733,7 +1812,7 @@ TEST_P(EndToEndTest, ClientSuggestsRTT) { QuicServerPeer::GetDispatcher(server_thread_->server()); ASSERT_EQ(1u, dispatcher->session_map().size()); const QuicSentPacketManager& client_sent_packet_manager = - client_->client()->client_session()->connection()->sent_packet_manager(); + GetClientConnection()->sent_packet_manager(); const QuicSentPacketManager* server_sent_packet_manager = GetSentPacketManagerFromFirstServerSession(); @@ -1762,7 +1841,7 @@ TEST_P(EndToEndTest, ClientSuggestsIgnoredRTT) { QuicServerPeer::GetDispatcher(server_thread_->server()); ASSERT_EQ(1u, dispatcher->session_map().size()); const QuicSentPacketManager& client_sent_packet_manager = - client_->client()->client_session()->connection()->sent_packet_manager(); + GetClientConnection()->sent_packet_manager(); const QuicSentPacketManager* server_sent_packet_manager = GetSentPacketManagerFromFirstServerSession(); @@ -1786,7 +1865,7 @@ TEST_P(EndToEndTest, MaxInitialRTT) { // Pause the server so we can access the server's internals without races. server_thread_->Pause(); const QuicSentPacketManager& client_sent_packet_manager = - client_->client()->client_session()->connection()->sent_packet_manager(); + GetClientConnection()->sent_packet_manager(); // Now that acks have been exchanged, the RTT estimate has decreased on the // server and is not infinite on the client. @@ -1812,7 +1891,7 @@ TEST_P(EndToEndTest, MinInitialRTT) { // Pause the server so we can access the server's internals without races. server_thread_->Pause(); const QuicSentPacketManager& client_sent_packet_manager = - client_->client()->client_session()->connection()->sent_packet_manager(); + GetClientConnection()->sent_packet_manager(); const QuicSentPacketManager& server_sent_packet_manager = GetServerConnection()->sent_packet_manager(); @@ -1841,15 +1920,10 @@ TEST_P(EndToEndTest, 0ByteConnectionId) { EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); EXPECT_EQ("200", client_->response_headers()->find(":status")->second); - QuicConnection* client_connection = - client_->client()->client_session()->connection(); + QuicConnection* client_connection = GetClientConnection(); QuicPacketHeader* header = QuicConnectionPeer::GetLastHeader(client_connection); - if (!GetQuicRestartFlag(quic_do_not_override_connection_id)) { - EXPECT_EQ(CONNECTION_ID_ABSENT, header->destination_connection_id_included); - } else { - EXPECT_EQ(CONNECTION_ID_ABSENT, header->source_connection_id_included); - } + EXPECT_EQ(CONNECTION_ID_ABSENT, header->source_connection_id_included); } TEST_P(EndToEndTestWithTls, 8ByteConnectionId) { @@ -1864,8 +1938,7 @@ TEST_P(EndToEndTestWithTls, 8ByteConnectionId) { EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); EXPECT_EQ("200", client_->response_headers()->find(":status")->second); - QuicConnection* client_connection = - client_->client()->client_session()->connection(); + QuicConnection* client_connection = GetClientConnection(); QuicPacketHeader* header = QuicConnectionPeer::GetLastHeader(client_connection); EXPECT_EQ(CONNECTION_ID_PRESENT, header->destination_connection_id_included); @@ -1884,8 +1957,7 @@ TEST_P(EndToEndTestWithTls, 15ByteConnectionId) { // Our server is permissive and allows for out of bounds values. EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); EXPECT_EQ("200", client_->response_headers()->find(":status")->second); - QuicConnection* client_connection = - client_->client()->client_session()->connection(); + QuicConnection* client_connection = GetClientConnection(); QuicPacketHeader* header = QuicConnectionPeer::GetLastHeader(client_connection); EXPECT_EQ(CONNECTION_ID_PRESENT, header->destination_connection_id_included); @@ -1905,7 +1977,6 @@ TEST_P(EndToEndTestWithTls, ResetConnection) { // TODO(nharper): Needs to get turned back to EndToEndTestWithTls // when we figure out why the test doesn't work on chrome. TEST_P(EndToEndTest, MaxStreamsUberTest) { - SetQuicFlag(FLAGS_quic_headers_stream_id_in_v99, 0); // Connect with lower fake packet loss than we'd like to test. Until // b/10126687 is fixed, losing handshake packets is pretty brutal. SetPacketLossPercentage(1); @@ -1936,7 +2007,7 @@ TEST_P(EndToEndTestWithTls, StreamCancelErrorTest) { EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); - QuicSession* session = client_->client()->client_session(); + QuicSession* session = GetClientSession(); // Lose the request. SetPacketLossPercentage(100); EXPECT_LT(0, client_->SendRequest("/small_response")); @@ -2006,14 +2077,13 @@ TEST_P(EndToEndTest, ConnectionMigrationClientPortChanged) { int new_port = client_->client()->network_helper()->GetLatestClientAddress().port(); QuicClientPeer::SetClientPort(client_->client(), new_port); - QuicConnectionPeer::SetSelfAddress( - client_->client()->client_session()->connection(), - QuicSocketAddress(client_->client() - ->client_session() - ->connection() - ->self_address() - .host(), - new_port)); + QuicConnectionPeer::SetSelfAddress(GetClientConnection(), + QuicSocketAddress(client_->client() + ->client_session() + ->connection() + ->self_address() + .host(), + new_port)); // Register the new FD for epoll events. int new_fd = client_->client()->GetLatestFD(); @@ -2086,9 +2156,8 @@ TEST_P(EndToEndTest, DifferentFlowControlWindows) { ->ReceivedInitialSessionFlowControlWindowBytes()); EXPECT_EQ(kServerStreamIFCW, QuicFlowControllerPeer::SendWindowOffset( stream->flow_controller())); - EXPECT_EQ(kServerSessionIFCW, - QuicFlowControllerPeer::SendWindowOffset( - client_->client()->client_session()->flow_controller())); + EXPECT_EQ(kServerSessionIFCW, QuicFlowControllerPeer::SendWindowOffset( + GetClientSession()->flow_controller())); // Server should have the right values for client's receive window. server_thread_->Pause(); @@ -2143,9 +2212,8 @@ TEST_P(EndToEndTest, NegotiatedServerInitialFlowControlWindow) { ->ReceivedInitialSessionFlowControlWindowBytes()); EXPECT_EQ(kExpectedStreamIFCW, QuicFlowControllerPeer::SendWindowOffset( stream->flow_controller())); - EXPECT_EQ(kExpectedSessionIFCW, - QuicFlowControllerPeer::SendWindowOffset( - client_->client()->client_session()->flow_controller())); + EXPECT_EQ(kExpectedSessionIFCW, QuicFlowControllerPeer::SendWindowOffset( + GetClientSession()->flow_controller())); } TEST_P(EndToEndTest, HeadersAndCryptoStreamsNoConnectionFlowControl) { @@ -2166,12 +2234,12 @@ TEST_P(EndToEndTest, HeadersAndCryptoStreamsNoConnectionFlowControl) { EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); server_thread_->WaitForCryptoHandshakeConfirmed(); - QuicCryptoStream* crypto_stream = QuicSessionPeer::GetMutableCryptoStream( - client_->client()->client_session()); + QuicCryptoStream* crypto_stream = + QuicSessionPeer::GetMutableCryptoStream(GetClientSession()); // In v47 and later, the crypto handshake (sent in CRYPTO frames) is not // subject to flow control. const QuicTransportVersion transport_version = - client_->client()->client_session()->connection()->transport_version(); + GetClientConnection()->transport_version(); if (!QuicVersionUsesCryptoFrames(transport_version)) { EXPECT_LT(QuicFlowControllerPeer::SendWindowSize( crypto_stream->flow_controller()), @@ -2180,9 +2248,8 @@ TEST_P(EndToEndTest, HeadersAndCryptoStreamsNoConnectionFlowControl) { // When stream type is enabled, control streams will send settings and // contribute to flow control windows, so this expectation is no longer valid. if (!VersionHasStreamType(transport_version)) { - EXPECT_EQ(kSessionIFCW, - QuicFlowControllerPeer::SendWindowSize( - client_->client()->client_session()->flow_controller())); + EXPECT_EQ(kSessionIFCW, QuicFlowControllerPeer::SendWindowSize( + GetClientSession()->flow_controller())); } // Send a request with no body, and verify that the connection level window @@ -2194,14 +2261,13 @@ TEST_P(EndToEndTest, HeadersAndCryptoStreamsNoConnectionFlowControl) { return; } - QuicHeadersStream* headers_stream = QuicSpdySessionPeer::GetHeadersStream( - client_->client()->client_session()); + QuicHeadersStream* headers_stream = + QuicSpdySessionPeer::GetHeadersStream(GetClientSession()); EXPECT_LT( QuicFlowControllerPeer::SendWindowSize(headers_stream->flow_controller()), kStreamIFCW); - EXPECT_EQ(kSessionIFCW, - QuicFlowControllerPeer::SendWindowSize( - client_->client()->client_session()->flow_controller())); + EXPECT_EQ(kSessionIFCW, QuicFlowControllerPeer::SendWindowSize( + GetClientSession()->flow_controller())); // Server should be in a similar state: connection flow control window should // not have any bytes marked as received. @@ -2222,95 +2288,97 @@ TEST_P(EndToEndTest, FlowControlsSynced) { EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); server_thread_->WaitForCryptoHandshakeConfirmed(); + QuicSpdySession* const client_session = GetClientSession(); + const QuicTransportVersion version = + client_session->connection()->transport_version(); + + if (VersionUsesQpack(version)) { + // Make sure that the client has received the initial SETTINGS frame, which + // is sent in the first packet on the control stream. + while (!QuicSpdySessionPeer::GetReceiveControlStream(client_session)) { + client_->client()->WaitForEvents(); + } + } + + // Make sure that all data sent by the client has been received by the server + // (and the ack received by the client). + while (client_session->HasUnackedStreamData()) { + client_->client()->WaitForEvents(); + } + server_thread_->Pause(); - QuicSpdySession* const client_session = client_->client()->client_session(); - auto* server_session = static_cast<QuicSpdySession*>(GetServerSession()); - - if (VersionHasStreamType(client_->client() - ->client_session() - ->connection() - ->transport_version())) { - // Settings frame will be sent through control streams, which contribute - // to the session's flow controller. And due to the timing issue described - // below, the settings frame might not be received. - HttpEncoder encoder; - SettingsFrame settings; - settings.values[6] = kDefaultMaxUncompressedHeaderSize; - std::unique_ptr<char[]> buffer; - auto header_length = encoder.SerializeSettingsFrame(settings, &buffer); + + QuicSpdySession* const server_session = GetServerSession(); + ExpectFlowControlsSynced(client_session, server_session); + + // Check control streams. + if (VersionUsesQpack(version)) { + ExpectFlowControlsSynced( + QuicSpdySessionPeer::GetReceiveControlStream(client_session), + QuicSpdySessionPeer::GetSendControlStream(server_session)); + ExpectFlowControlsSynced( + QuicSpdySessionPeer::GetSendControlStream(client_session), + QuicSpdySessionPeer::GetReceiveControlStream(server_session)); + } + + // Check crypto stream. + if (!QuicVersionUsesCryptoFrames(version)) { + ExpectFlowControlsSynced( + QuicSessionPeer::GetMutableCryptoStream(client_session), + QuicSessionPeer::GetMutableCryptoStream(server_session)); + } + + // Check headers stream. + if (!VersionHasStreamType(version)) { + SpdyFramer spdy_framer(SpdyFramer::ENABLE_COMPRESSION); + SpdySettingsIR settings_frame; + settings_frame.AddSetting(SETTINGS_MAX_HEADER_LIST_SIZE, + kDefaultMaxUncompressedHeaderSize); + SpdySerializedFrame frame(spdy_framer.SerializeFrame(settings_frame)); + + QuicFlowController* client_header_stream_flow_controller = + QuicSpdySessionPeer::GetHeadersStream(client_session) + ->flow_controller(); + QuicFlowController* server_header_stream_flow_controller = + QuicSpdySessionPeer::GetHeadersStream(server_session) + ->flow_controller(); + // Both client and server are sending this SETTINGS frame, and the send + // window is consumed. But because of timing issue, the server may send or + // not send the frame, and the client may send/ not send / receive / not + // receive the frame. + // TODO(fayang): Rewrite this part because it is hacky. QuicByteCount win_difference1 = QuicFlowControllerPeer::ReceiveWindowSize( - server_session->flow_controller()) - + server_header_stream_flow_controller) - QuicFlowControllerPeer::SendWindowSize( - client_session->flow_controller()); + client_header_stream_flow_controller); + if (win_difference1 != 0) { + EXPECT_EQ(frame.size(), win_difference1); + } + QuicByteCount win_difference2 = QuicFlowControllerPeer::ReceiveWindowSize( - client_session->flow_controller()) - + client_header_stream_flow_controller) - QuicFlowControllerPeer::SendWindowSize( - server_session->flow_controller()); - EXPECT_TRUE(win_difference1 == 0 || - win_difference1 == - header_length + - QuicDataWriter::GetVarInt62Len(kControlStream)); - EXPECT_TRUE(win_difference2 == 0 || - win_difference2 == - header_length + - QuicDataWriter::GetVarInt62Len(kControlStream)); - // The test returns early because in this version, headers stream no longer - // sends settings. - return; - } + server_header_stream_flow_controller); + if (win_difference2 != 0) { + EXPECT_EQ(frame.size(), win_difference2); + } - ExpectFlowControlsSynced(client_session->flow_controller(), - server_session->flow_controller()); - if (!QuicVersionUsesCryptoFrames(client_->client() - ->client_session() - ->connection() - ->transport_version())) { - ExpectFlowControlsSynced( - QuicSessionPeer::GetMutableCryptoStream(client_session) - ->flow_controller(), - QuicSessionPeer::GetMutableCryptoStream(server_session) - ->flow_controller()); - } - SpdyFramer spdy_framer(SpdyFramer::ENABLE_COMPRESSION); - SpdySettingsIR settings_frame; - settings_frame.AddSetting(SETTINGS_MAX_HEADER_LIST_SIZE, - kDefaultMaxUncompressedHeaderSize); - SpdySerializedFrame frame(spdy_framer.SerializeFrame(settings_frame)); - QuicFlowController* client_header_stream_flow_controller = - QuicSpdySessionPeer::GetHeadersStream(client_session)->flow_controller(); - QuicFlowController* server_header_stream_flow_controller = - QuicSpdySessionPeer::GetHeadersStream(server_session)->flow_controller(); - // Both client and server are sending this SETTINGS frame, and the send - // window is consumed. But because of timing issue, the server may send or - // not send the frame, and the client may send/ not send / receive / not - // receive the frame. - // TODO(fayang): Rewrite this part because it is hacky. - QuicByteCount win_difference1 = QuicFlowControllerPeer::ReceiveWindowSize( - server_header_stream_flow_controller) - - QuicFlowControllerPeer::SendWindowSize( - client_header_stream_flow_controller); - QuicByteCount win_difference2 = QuicFlowControllerPeer::ReceiveWindowSize( - client_header_stream_flow_controller) - - QuicFlowControllerPeer::SendWindowSize( - server_header_stream_flow_controller); - EXPECT_TRUE(win_difference1 == 0 || win_difference1 == frame.size()); - EXPECT_TRUE(win_difference2 == 0 || win_difference2 == frame.size()); - - // Client *may* have received the SETTINGs frame. - // TODO(fayang): Rewrite this part because it is hacky. - float ratio1 = static_cast<float>(QuicFlowControllerPeer::ReceiveWindowSize( - client_session->flow_controller())) / - QuicFlowControllerPeer::ReceiveWindowSize( - QuicSpdySessionPeer::GetHeadersStream(client_session) - ->flow_controller()); - float ratio2 = static_cast<float>(QuicFlowControllerPeer::ReceiveWindowSize( - client_session->flow_controller())) / - (QuicFlowControllerPeer::ReceiveWindowSize( - QuicSpdySessionPeer::GetHeadersStream(client_session) - ->flow_controller()) + - frame.size()); - EXPECT_TRUE(ratio1 == kSessionToStreamRatio || - ratio2 == kSessionToStreamRatio); + // Client *may* have received the SETTINGs frame. + // TODO(fayang): Rewrite this part because it is hacky. + float ratio1 = static_cast<float>(QuicFlowControllerPeer::ReceiveWindowSize( + client_session->flow_controller())) / + QuicFlowControllerPeer::ReceiveWindowSize( + QuicSpdySessionPeer::GetHeadersStream(client_session) + ->flow_controller()); + float ratio2 = static_cast<float>(QuicFlowControllerPeer::ReceiveWindowSize( + client_session->flow_controller())) / + (QuicFlowControllerPeer::ReceiveWindowSize( + QuicSpdySessionPeer::GetHeadersStream(client_session) + ->flow_controller()) + + frame.size()); + EXPECT_TRUE(ratio1 == kSessionToStreamRatio || + ratio2 == kSessionToStreamRatio); + } server_thread_->Resume(); } @@ -2402,10 +2470,11 @@ TEST_P(EndToEndTest, AckNotifierWithPacketLossAndBlockedSocket) { // Determine size of compressed headers. NoopDecoderStreamErrorDelegate decoder_stream_error_delegate; NoopQpackStreamSenderDelegate encoder_stream_sender_delegate; - QpackEncoder qpack_encoder(&decoder_stream_error_delegate, - &encoder_stream_sender_delegate); + QpackEncoder qpack_encoder(&decoder_stream_error_delegate); + qpack_encoder.set_qpack_stream_sender_delegate( + &encoder_stream_sender_delegate); std::string encoded_headers = - qpack_encoder.EncodeHeaderList(/* stream_id = */ 0, &headers); + qpack_encoder.EncodeHeaderList(/* stream_id = */ 0, headers); header_size = encoded_headers.size(); } @@ -2439,18 +2508,10 @@ TEST_P(EndToEndTestWithTls, ServerSendPublicReset) { ASSERT_TRUE(Initialize()); EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); - QuicConnection* client_connection = - client_->client()->client_session()->connection(); - if (SupportsIetfQuicWithTls(client_connection->version())) { - // TLS handshake does not support stateless reset token yet. - return; - } - QuicUint128 stateless_reset_token = 0; - if (client_connection->version().handshake_protocol == PROTOCOL_QUIC_CRYPTO) { - QuicConfig* config = client_->client()->session()->config(); - EXPECT_TRUE(config->HasReceivedStatelessResetToken()); - stateless_reset_token = config->ReceivedStatelessResetToken(); - } + QuicConnection* client_connection = GetClientConnection(); + QuicConfig* config = client_->client()->session()->config(); + EXPECT_TRUE(config->HasReceivedStatelessResetToken()); + QuicUint128 stateless_reset_token = config->ReceivedStatelessResetToken(); // Send the public reset. QuicConnectionId connection_id = client_connection->connection_id(); @@ -2485,18 +2546,10 @@ TEST_P(EndToEndTestWithTls, ServerSendPublicResetWithDifferentConnectionId) { ASSERT_TRUE(Initialize()); EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); - QuicConnection* client_connection = - client_->client()->client_session()->connection(); - if (SupportsIetfQuicWithTls(client_connection->version())) { - // TLS handshake does not support stateless reset token yet. - return; - } - QuicUint128 stateless_reset_token = 0; - if (client_connection->version().handshake_protocol == PROTOCOL_QUIC_CRYPTO) { - QuicConfig* config = client_->client()->session()->config(); - EXPECT_TRUE(config->HasReceivedStatelessResetToken()); - stateless_reset_token = config->ReceivedStatelessResetToken(); - } + QuicConnection* client_connection = GetClientConnection(); + QuicConfig* config = client_->client()->session()->config(); + EXPECT_TRUE(config->HasReceivedStatelessResetToken()); + QuicUint128 stateless_reset_token = config->ReceivedStatelessResetToken(); // Send the public reset. QuicConnectionId incorrect_connection_id = TestConnectionId( TestConnectionIdToUInt64(client_connection->connection_id()) + 1); @@ -2506,8 +2559,7 @@ TEST_P(EndToEndTestWithTls, ServerSendPublicResetWithDifferentConnectionId) { Perspective::IS_SERVER, kQuicDefaultConnectionIdLength); std::unique_ptr<QuicEncryptedPacket> packet; testing::NiceMock<MockQuicConnectionDebugVisitor> visitor; - client_->client()->client_session()->connection()->set_debug_visitor( - &visitor); + GetClientConnection()->set_debug_visitor(&visitor); if (VersionHasIetfInvariantHeader(client_connection->transport_version())) { packet = framer.BuildIetfStatelessResetPacket(incorrect_connection_id, stateless_reset_token); @@ -2538,7 +2590,7 @@ TEST_P(EndToEndTestWithTls, ServerSendPublicResetWithDifferentConnectionId) { EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); EXPECT_EQ("200", client_->response_headers()->find(":status")->second); - client_->client()->client_session()->connection()->set_debug_visitor(nullptr); + GetClientConnection()->set_debug_visitor(nullptr); } // Send a public reset from the client for a different connection ID. @@ -2548,9 +2600,7 @@ TEST_P(EndToEndTestWithTls, ClientSendPublicResetWithDifferentConnectionId) { // Send the public reset. QuicConnectionId incorrect_connection_id = TestConnectionId( - TestConnectionIdToUInt64( - client_->client()->client_session()->connection()->connection_id()) + - 1); + TestConnectionIdToUInt64(GetClientConnection()->connection_id()) + 1); QuicPublicResetPacket header; header.connection_id = incorrect_connection_id; QuicFramer framer(server_supported_versions_, QuicTime::Zero(), @@ -2576,14 +2626,14 @@ TEST_P(EndToEndTestWithTls, EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); // Send the version negotiation packet. - QuicConnection* client_connection = - client_->client()->client_session()->connection(); + QuicConnection* client_connection = GetClientConnection(); QuicConnectionId incorrect_connection_id = TestConnectionId( TestConnectionIdToUInt64(client_connection->connection_id()) + 1); std::unique_ptr<QuicEncryptedPacket> packet( QuicFramer::BuildVersionNegotiationPacket( incorrect_connection_id, EmptyQuicConnectionId(), VersionHasIetfInvariantHeader(client_connection->transport_version()), + client_connection->version().HasLengthPrefixedConnectionIds(), server_supported_versions_)); testing::NiceMock<MockQuicConnectionDebugVisitor> visitor; client_connection->set_debug_visitor(&visitor); @@ -2698,9 +2748,9 @@ TEST_P(EndToEndTestWithTls, BadEncryptedData) { EXPECT_EQ("200", client_->response_headers()->find(":status")->second); std::unique_ptr<QuicEncryptedPacket> packet(ConstructEncryptedPacket( - client_->client()->client_session()->connection()->connection_id(), - EmptyQuicConnectionId(), false, false, 1, "At least 20 characters.", - CONNECTION_ID_PRESENT, CONNECTION_ID_ABSENT, PACKET_4BYTE_PACKET_NUMBER)); + GetClientConnection()->connection_id(), EmptyQuicConnectionId(), false, + false, 1, "At least 20 characters.", CONNECTION_ID_PRESENT, + CONNECTION_ID_ABSENT, PACKET_4BYTE_PACKET_NUMBER)); // Damage the encrypted data. std::string damaged_packet(packet->data(), packet->length()); damaged_packet[30] ^= 0x01; @@ -2741,7 +2791,7 @@ TEST_P(EndToEndTestWithTls, CanceledStreamDoesNotBecomeZombie) { // Cancel the stream. stream->Reset(QUIC_STREAM_CANCELLED); - QuicSession* session = client_->client()->client_session(); + QuicSession* session = GetClientSession(); // Verify canceled stream does not become zombie. EXPECT_TRUE(QuicSessionPeer::zombie_streams(session).empty()); EXPECT_EQ(1u, QuicSessionPeer::closed_streams(session).size()); @@ -3008,7 +3058,6 @@ class EndToEndTestServerPush : public EndToEndTest { const size_t kNumMaxStreams = 10; EndToEndTestServerPush() : EndToEndTest() { - SetQuicFlag(FLAGS_quic_headers_stream_id_in_v99, 0); client_config_.SetMaxIncomingBidirectionalStreamsToSend(kNumMaxStreams); server_config_.SetMaxIncomingBidirectionalStreamsToSend(kNumMaxStreams); client_config_.SetMaxIncomingUnidirectionalStreamsToSend(kNumMaxStreams); @@ -3086,12 +3135,19 @@ TEST_P(EndToEndTestServerPush, ServerPush) { QUIC_DVLOG(1) << "send request for /push_example"; EXPECT_EQ(kBody, client_->SendSynchronousRequest( "https://example.com/push_example")); - QuicHeadersStream* headers_stream = QuicSpdySessionPeer::GetHeadersStream( - client_->client()->client_session()); - QuicStreamSequencer* sequencer = QuicStreamPeer::sequencer(headers_stream); - // Headers stream's sequencer buffer shouldn't be released because server push - // hasn't finished yet. - EXPECT_TRUE(QuicStreamSequencerPeer::IsUnderlyingBufferAllocated(sequencer)); + QuicStreamSequencer* sequencer; + if (!VersionUsesQpack(client_->client() + ->client_session() + ->connection() + ->transport_version())) { + QuicHeadersStream* headers_stream = + QuicSpdySessionPeer::GetHeadersStream(GetClientSession()); + sequencer = QuicStreamPeer::sequencer(headers_stream); + // Headers stream's sequencer buffer shouldn't be released because server + // push hasn't finished yet. + EXPECT_TRUE( + QuicStreamSequencerPeer::IsUnderlyingBufferAllocated(sequencer)); + } for (const std::string& url : push_urls) { QUIC_DVLOG(1) << "send request for pushed stream on url " << url; @@ -3101,7 +3157,13 @@ TEST_P(EndToEndTestServerPush, ServerPush) { QUIC_DVLOG(1) << "response body " << response_body; EXPECT_EQ(expected_body, response_body); } - EXPECT_FALSE(QuicStreamSequencerPeer::IsUnderlyingBufferAllocated(sequencer)); + if (!VersionUsesQpack(client_->client() + ->client_session() + ->connection() + ->transport_version())) { + EXPECT_FALSE( + QuicStreamSequencerPeer::IsUnderlyingBufferAllocated(sequencer)); + } } TEST_P(EndToEndTestServerPush, ServerPushUnderLimit) { @@ -3342,10 +3404,8 @@ TEST_P(EndToEndTest, DISABLED_TestHugeResponseWithPacketLoss) { client_.reset(client); static QuicEpollEvent event(EPOLLOUT); client_writer_->Initialize( - QuicConnectionPeer::GetHelper( - client_->client()->client_session()->connection()), - QuicConnectionPeer::GetAlarmFactory( - client_->client()->client_session()->connection()), + QuicConnectionPeer::GetHelper(GetClientConnection()), + QuicConnectionPeer::GetAlarmFactory(GetClientConnection()), QuicMakeUnique<ClientDelegate>(client_->client())); initialized_ = true; ASSERT_TRUE(client_->client()->connected()); @@ -3362,8 +3422,7 @@ TEST_P(EndToEndTest, AgreeOnStopWaiting) { ASSERT_TRUE(Initialize()); EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); - QuicConnection* client_connection = - client_->client()->client_session()->connection(); + QuicConnection* client_connection = GetClientConnection(); server_thread_->Pause(); QuicConnection* server_connection = GetServerConnection(); // Verify client and server connections agree on the value of @@ -3381,8 +3440,7 @@ TEST_P(EndToEndTest, AgreeOnStopWaitingWithNoStopWaitingOption) { ASSERT_TRUE(Initialize()); EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); - QuicConnection* client_connection = - client_->client()->client_session()->connection(); + QuicConnection* client_connection = GetClientConnection(); server_thread_->Pause(); QuicConnection* server_connection = GetServerConnection(); // Verify client and server connections agree on the value of @@ -3397,8 +3455,14 @@ TEST_P(EndToEndTest, ReleaseHeadersStreamBufferWhenIdle) { // PUSH_PROMISE, its headers stream's sequencer buffer should be released. ASSERT_TRUE(Initialize()); client_->SendSynchronousRequest("/foo"); - QuicHeadersStream* headers_stream = QuicSpdySessionPeer::GetHeadersStream( - client_->client()->client_session()); + if (VersionUsesQpack(client_->client() + ->client_session() + ->connection() + ->transport_version())) { + return; + } + QuicHeadersStream* headers_stream = + QuicSpdySessionPeer::GetHeadersStream(GetClientSession()); QuicStreamSequencer* sequencer = QuicStreamPeer::sequencer(headers_stream); EXPECT_FALSE(QuicStreamSequencerPeer::IsUnderlyingBufferAllocated(sequencer)); } @@ -3444,8 +3508,7 @@ TEST_P(EndToEndTest, WindowUpdateInAck) { ASSERT_TRUE(Initialize()); EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); WindowUpdateObserver observer; - QuicConnection* client_connection = - client_->client()->client_session()->connection(); + QuicConnection* client_connection = GetClientConnection(); client_connection->set_debug_visitor(&observer); // 100KB body. std::string body(100 * 1024, 'a'); @@ -3462,7 +3525,7 @@ TEST_P(EndToEndTest, WindowUpdateInAck) { EXPECT_EQ(0u, observer.num_ping_frames()); } -TEST_P(EndToEndTest, SendStatelessResetTokenInShlo) { +TEST_P(EndToEndTestWithTls, SendStatelessResetTokenInShlo) { ASSERT_TRUE(Initialize()); EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); QuicConfig* config = client_->client()->session()->config(); @@ -3647,7 +3710,7 @@ TEST_P(EndToEndTest, RequestAndStreamRstInOnePacket) { EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); client_->WaitForDelayedAcks(); - QuicSession* session = client_->client()->client_session(); + QuicSession* session = GetClientSession(); const QuicPacketCount packets_sent_before = session->connection()->GetStats().packets_sent; @@ -3667,7 +3730,7 @@ TEST_P(EndToEndTest, RequestAndStreamRstInOnePacket) { TEST_P(EndToEndTest, ResetStreamOnTtlExpires) { ASSERT_TRUE(Initialize()); EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); - if (!client_->client()->client_session()->session_decides_what_to_write()) { + if (!GetClientSession()->session_decides_what_to_write()) { return; } SetPacketLossPercentage(30); @@ -3687,7 +3750,7 @@ TEST_P(EndToEndTest, ResetStreamOnTtlExpires) { TEST_P(EndToEndTest, SendMessages) { ASSERT_TRUE(Initialize()); EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); - QuicSession* client_session = client_->client()->client_session(); + QuicSession* client_session = GetClientSession(); QuicConnection* client_connection = client_session->connection(); if (!VersionSupportsMessageFrames(client_connection->transport_version())) { return; @@ -3812,10 +3875,8 @@ TEST_P(EndToEndPacketReorderingTest, ReorderedConnectivityProbing) { server_connection->GetStats().num_connectivity_probing_received); server_thread_->Resume(); - QuicConnection* client_connection = - client_->client()->client_session()->connection(); - EXPECT_EQ(1u, - client_connection->GetStats().num_connectivity_probing_received); + EXPECT_EQ( + 1u, GetClientConnection()->GetStats().num_connectivity_probing_received); } TEST_P(EndToEndPacketReorderingTest, Buffer0RttRequest) { @@ -3844,8 +3905,7 @@ TEST_P(EndToEndPacketReorderingTest, Buffer0RttRequest) { client_->SendMessage(headers, ""); client_->WaitForResponse(); EXPECT_EQ(kBarResponseBody, client_->response_body()); - QuicConnectionStats client_stats = - client_->client()->client_session()->connection()->GetStats(); + QuicConnectionStats client_stats = GetClientConnection()->GetStats(); EXPECT_EQ(0u, client_stats.packets_lost); if (ServerSendsVersionNegotiation()) { EXPECT_EQ(2, client_->client()->GetNumSentClientHellos()); @@ -3864,7 +3924,7 @@ TEST_P(EndToEndTest, SimpleStopSendingTest) { if (!VersionHasIetfQuicFrames(negotiated_version_.transport_version)) { return; } - QuicSession* client_session = client_->client()->client_session(); + QuicSession* client_session = GetClientSession(); ASSERT_NE(nullptr, client_session); QuicConnection* client_connection = client_session->connection(); ASSERT_NE(nullptr, client_connection); @@ -3884,7 +3944,7 @@ TEST_P(EndToEndTest, SimpleStopSendingTest) { EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); client_->WaitForDelayedAcks(); - QuicSession* session = client_->client()->client_session(); + QuicSession* session = GetClientSession(); const QuicPacketCount packets_sent_before = session->connection()->GetStats().packets_sent; @@ -4069,7 +4129,7 @@ TEST_P(EndToEndTest, TooBigStreamIdClosesConnection) { headers[":authority"] = server_hostname_; // Force the client to write with a stream ID that exceeds the limit. - QuicSpdySession* session = client_->client()->client_session(); + QuicSpdySession* session = GetClientSession(); QuicStreamIdManager* stream_id_manager = QuicSessionPeer::v99_bidirectional_stream_id_manager(session); QuicStreamCount max_number_of_streams = @@ -4078,7 +4138,34 @@ TEST_P(EndToEndTest, TooBigStreamIdClosesConnection) { session, GetNthClientInitiatedBidirectionalId(max_number_of_streams + 1)); client_->SendCustomSynchronousRequest(headers, body); EXPECT_EQ(QUIC_STREAM_CONNECTION_ERROR, client_->stream_error()); - EXPECT_EQ(QUIC_INVALID_STREAM_ID, client_->connection_error()); + EXPECT_EQ(QUIC_INVALID_STREAM_ID, GetClientSession()->error()); + EXPECT_EQ(IETF_QUIC_TRANSPORT_CONNECTION_CLOSE, + GetClientSession()->close_type()); + EXPECT_TRUE( + IS_IETF_STREAM_FRAME(GetClientSession()->transport_close_frame_type())); +} + +TEST_P(EndToEndTest, TestMaxPushId) { + // Has to be before version test, see EndToEndTest::TearDown() + ASSERT_TRUE(Initialize()); + if (!VersionHasIetfQuicFrames(negotiated_version_.transport_version)) { + // Only runs for IETF QUIC. + return; + } + + EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + static_cast<QuicSpdySession*>(client_->client()->session()) + ->set_max_allowed_push_id(kMaxQuicStreamId); + + client_->SendSynchronousRequest("/foo"); + + EXPECT_EQ(kMaxQuicStreamId, + static_cast<QuicSpdySession*>(client_->client()->session()) + ->max_allowed_push_id()); + + EXPECT_EQ( + kMaxQuicStreamId, + static_cast<QuicSpdySession*>(GetServerSession())->max_allowed_push_id()); } } // namespace diff --git a/chromium/net/third_party/quiche/src/quic/core/http/http_constants.h b/chromium/net/third_party/quiche/src/quic/core/http/http_constants.h index 2641790a847..9c2c6b58fc7 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/http_constants.h +++ b/chromium/net/third_party/quiche/src/quic/core/http/http_constants.h @@ -7,6 +7,8 @@ #include <cstdint> +#include "net/third_party/quiche/src/quic/core/quic_types.h" + namespace quic { // Unidirectional stream types. @@ -18,14 +20,25 @@ const uint64_t kServerPushStream = 0x01; const uint64_t kQpackEncoderStream = 0x02; const uint64_t kQpackDecoderStream = 0x03; -// Settings identifiers. - +// HTTP/3 and QPACK settings identifiers. // https://quicwg.org/base-drafts/draft-ietf-quic-http.html#settings-parameters -const uint64_t kSettingsMaxHeaderListSize = 0x06; -const uint64_t kSettingsNumPlaceholders = 0x09; // https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#configuration -const uint64_t kSettingsQpackMaxTableCapacity = 0x01; -const uint64_t kSettingsQpackBlockedStream = 0x07; +enum Http3AndQpackSettingsIdentifiers : uint64_t { + // Same value as spdy::SETTINGS_HEADER_TABLE_SIZE. + SETTINGS_QPACK_MAX_TABLE_CAPACITY = 0x01, + // Same value as spdy::SETTINGS_MAX_HEADER_LIST_SIZE. + SETTINGS_MAX_HEADER_LIST_SIZE = 0x06, + SETTINGS_QPACK_BLOCKED_STREAMS = 0x07, + SETTINGS_NUM_PLACEHOLDERS = 0x09, +}; + +// Default maximum dynamic table capacity, communicated via +// SETTINGS_QPACK_MAX_TABLE_CAPACITY. +const QuicByteCount kDefaultQpackMaxDynamicTableCapacity = 64 * 1024; // 64 KB + +// Default limit on number of blocked streams, communicated via +// SETTINGS_QPACK_BLOCKED_STREAMS. +const uint64_t kDefaultMaximumBlockedStreams = 100; } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/http/http_decoder.cc b/chromium/net/third_party/quiche/src/quic/core/http/http_decoder.cc index f8de0835c74..c9519fe49a4 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/http_decoder.cc +++ b/chromium/net/third_party/quiche/src/quic/core/http/http_decoder.cc @@ -23,6 +23,8 @@ HttpDecoder::HttpDecoder(Visitor* visitor) remaining_frame_length_(0), current_type_field_length_(0), remaining_type_field_length_(0), + current_push_id_length_(0), + remaining_push_id_length_(0), error_(QUIC_NO_ERROR), error_detail_("") { DCHECK(visitor_); @@ -124,33 +126,31 @@ bool HttpDecoder::ReadFrameLength(QuicDataReader* reader) { } if (current_frame_length_ > MaxFrameLength(current_frame_type_)) { - // TODO(bnc): Signal HTTP_EXCESSIVE_LOAD or similar to peer. - RaiseError(QUIC_INTERNAL_ERROR, "Frame is too large"); - visitor_->OnError(this); + // TODO(b/124216424): Use HTTP_EXCESSIVE_LOAD. + RaiseError(QUIC_INVALID_FRAME_DATA, "Frame is too large"); return false; } // Calling the following visitor methods does not require parsing of any // frame payload. bool continue_processing = true; - auto frame_meta = Http3FrameLengths( - current_length_field_length_ + current_type_field_length_, - current_frame_length_); + const QuicByteCount header_length = + current_length_field_length_ + current_type_field_length_; switch (current_frame_type_) { case static_cast<uint64_t>(HttpFrameType::DATA): - continue_processing = visitor_->OnDataFrameStart(frame_meta); + continue_processing = visitor_->OnDataFrameStart(header_length); break; case static_cast<uint64_t>(HttpFrameType::HEADERS): - continue_processing = visitor_->OnHeadersFrameStart(frame_meta); + continue_processing = visitor_->OnHeadersFrameStart(header_length); break; case static_cast<uint64_t>(HttpFrameType::PRIORITY): - continue_processing = visitor_->OnPriorityFrameStart(frame_meta); + continue_processing = visitor_->OnPriorityFrameStart(header_length); break; case static_cast<uint64_t>(HttpFrameType::CANCEL_PUSH): break; case static_cast<uint64_t>(HttpFrameType::SETTINGS): - continue_processing = visitor_->OnSettingsFrameStart(frame_meta); + continue_processing = visitor_->OnSettingsFrameStart(header_length); break; case static_cast<uint64_t>(HttpFrameType::PUSH_PROMISE): break; @@ -162,7 +162,7 @@ bool HttpDecoder::ReadFrameLength(QuicDataReader* reader) { break; default: continue_processing = - visitor_->OnUnknownFrameStart(current_frame_type_, frame_meta); + visitor_->OnUnknownFrameStart(current_frame_type_, header_length); break; } @@ -216,23 +216,57 @@ bool HttpDecoder::ReadFramePayload(QuicDataReader* reader) { break; } case static_cast<uint64_t>(HttpFrameType::PUSH_PROMISE): { + PushId push_id; if (current_frame_length_ == remaining_frame_length_) { - QuicByteCount bytes_remaining = reader->BytesRemaining(); - PushId push_id; - // TODO(rch): Handle partial delivery of this field. - if (!reader->ReadVarInt62(&push_id)) { - RaiseError(QUIC_INTERNAL_ERROR, "Unable to read push_id"); + // A new Push Promise frame just arrived. + DCHECK_EQ(0u, current_push_id_length_); + current_push_id_length_ = reader->PeekVarInt62Length(); + if (current_push_id_length_ > remaining_frame_length_) { + RaiseError(QUIC_INVALID_FRAME_DATA, "PUSH_PROMISE frame malformed."); return false; } - remaining_frame_length_ -= bytes_remaining - reader->BytesRemaining(); + if (current_push_id_length_ > reader->BytesRemaining()) { + // Not all bytes of push id is present yet, buffer push id. + DCHECK_EQ(0u, remaining_push_id_length_); + remaining_push_id_length_ = current_push_id_length_; + BufferPushId(reader); + break; + } + bool success = reader->ReadVarInt62(&push_id); + DCHECK(success); + remaining_frame_length_ -= current_push_id_length_; if (!visitor_->OnPushPromiseFrameStart( - push_id, Http3FrameLengths(current_length_field_length_ + - current_type_field_length_, - current_frame_length_))) { + push_id, + current_length_field_length_ + current_type_field_length_, + current_push_id_length_)) { continue_processing = false; + current_push_id_length_ = 0; + break; + } + current_push_id_length_ = 0; + } else if (remaining_push_id_length_ > 0) { + // Waiting for more bytes on push id. + BufferPushId(reader); + if (remaining_push_id_length_ != 0) { break; } + QuicDataReader push_id_reader(push_id_buffer_.data(), + current_push_id_length_); + + bool success = push_id_reader.ReadVarInt62(&push_id); + DCHECK(success); + if (!visitor_->OnPushPromiseFrameStart( + push_id, + current_length_field_length_ + current_type_field_length_, + current_push_id_length_)) { + continue_processing = false; + current_push_id_length_ = 0; + break; + } + current_push_id_length_ = 0; } + + // Read Push Promise headers. DCHECK_LT(remaining_frame_length_, current_frame_length_); QuicByteCount bytes_to_read = std::min<QuicByteCount>( remaining_frame_length_, reader->BytesRemaining()); @@ -252,7 +286,6 @@ bool HttpDecoder::ReadFramePayload(QuicDataReader* reader) { break; } case static_cast<uint64_t>(HttpFrameType::MAX_PUSH_ID): { - // TODO(rch): Handle partial delivery. BufferFramePayload(reader); break; } @@ -306,11 +339,16 @@ bool HttpDecoder::FinishParsing() { break; } case static_cast<uint64_t>(HttpFrameType::CANCEL_PUSH): { - // TODO(rch): Handle partial delivery. CancelPushFrame frame; QuicDataReader reader(buffer_.data(), current_frame_length_); if (!reader.ReadVarInt62(&frame.push_id)) { - RaiseError(QUIC_INTERNAL_ERROR, "Unable to read push_id"); + // TODO(b/124216424): Use HTTP_MALFORMED_FRAME. + RaiseError(QUIC_INVALID_FRAME_DATA, "Unable to read push_id"); + return false; + } + if (!reader.IsDoneReading()) { + RaiseError(QUIC_INVALID_FRAME_DATA, + "Superfluous data in CANCEL_PUSH frame."); return false; } continue_processing = visitor_->OnCancelPushFrame(frame); @@ -330,7 +368,6 @@ bool HttpDecoder::FinishParsing() { break; } case static_cast<uint64_t>(HttpFrameType::GOAWAY): { - // TODO(bnc): Handle partial delivery. QuicDataReader reader(buffer_.data(), current_frame_length_); GoAwayFrame frame; static_assert(!std::is_same<decltype(frame.stream_id), uint64_t>::value, @@ -339,7 +376,13 @@ bool HttpDecoder::FinishParsing() { "QuicStreamId from uint32_t to uint64_t."); uint64_t stream_id; if (!reader.ReadVarInt62(&stream_id)) { - RaiseError(QUIC_INTERNAL_ERROR, "Unable to read GOAWAY stream_id"); + // TODO(b/124216424): Use HTTP_MALFORMED_FRAME. + RaiseError(QUIC_INVALID_FRAME_DATA, "Unable to read GOAWAY stream_id"); + return false; + } + if (!reader.IsDoneReading()) { + RaiseError(QUIC_INVALID_FRAME_DATA, + "Superfluous data in GOAWAY frame."); return false; } frame.stream_id = static_cast<QuicStreamId>(stream_id); @@ -347,22 +390,32 @@ bool HttpDecoder::FinishParsing() { break; } case static_cast<uint64_t>(HttpFrameType::MAX_PUSH_ID): { - // TODO(bnc): Handle partial delivery. QuicDataReader reader(buffer_.data(), current_frame_length_); MaxPushIdFrame frame; if (!reader.ReadVarInt62(&frame.push_id)) { - RaiseError(QUIC_INTERNAL_ERROR, "Unable to read push_id"); + // TODO(b/124216424): Use HTTP_MALFORMED_FRAME. + RaiseError(QUIC_INVALID_FRAME_DATA, "Unable to read push_id"); + return false; + } + if (!reader.IsDoneReading()) { + RaiseError(QUIC_INVALID_FRAME_DATA, + "Superfluous data in MAX_PUSH_ID frame."); return false; } continue_processing = visitor_->OnMaxPushIdFrame(frame); break; } case static_cast<uint64_t>(HttpFrameType::DUPLICATE_PUSH): { - // TODO(bnc): Handle partial delivery. QuicDataReader reader(buffer_.data(), current_frame_length_); DuplicatePushFrame frame; if (!reader.ReadVarInt62(&frame.push_id)) { - RaiseError(QUIC_INTERNAL_ERROR, "Unable to read push_id"); + // TODO(b/124216424): Use HTTP_MALFORMED_FRAME. + RaiseError(QUIC_INVALID_FRAME_DATA, "Unable to read push_id"); + return false; + } + if (!reader.IsDoneReading()) { + RaiseError(QUIC_INVALID_FRAME_DATA, + "Superfluous data in DUPLICATE_PUSH frame."); return false; } continue_processing = visitor_->OnDuplicatePushFrame(frame); @@ -409,9 +462,6 @@ void HttpDecoder::BufferFramePayload(QuicDataReader* reader) { } void HttpDecoder::BufferFrameLength(QuicDataReader* reader) { - if (current_length_field_length_ == remaining_length_field_length_) { - length_buffer_.fill(0); - } QuicByteCount bytes_to_read = std::min<QuicByteCount>( remaining_length_field_length_, reader->BytesRemaining()); bool success = @@ -423,9 +473,6 @@ void HttpDecoder::BufferFrameLength(QuicDataReader* reader) { } void HttpDecoder::BufferFrameType(QuicDataReader* reader) { - if (current_type_field_length_ == remaining_type_field_length_) { - type_buffer_.fill(0); - } QuicByteCount bytes_to_read = std::min<QuicByteCount>( remaining_type_field_length_, reader->BytesRemaining()); bool success = @@ -436,10 +483,24 @@ void HttpDecoder::BufferFrameType(QuicDataReader* reader) { remaining_type_field_length_ -= bytes_to_read; } +void HttpDecoder::BufferPushId(QuicDataReader* reader) { + DCHECK_LE(remaining_push_id_length_, current_frame_length_); + QuicByteCount bytes_to_read = std::min<QuicByteCount>( + reader->BytesRemaining(), remaining_push_id_length_); + bool success = + reader->ReadBytes(push_id_buffer_.data() + current_push_id_length_ - + remaining_push_id_length_, + bytes_to_read); + DCHECK(success); + remaining_push_id_length_ -= bytes_to_read; + remaining_frame_length_ -= bytes_to_read; +} + void HttpDecoder::RaiseError(QuicErrorCode error, std::string error_detail) { state_ = STATE_ERROR; error_ = error; error_detail_ = std::move(error_detail); + visitor_->OnError(this); } bool HttpDecoder::ParsePriorityFrame(QuicDataReader* reader, @@ -459,7 +520,6 @@ bool HttpDecoder::ParsePriorityFrame(QuicDataReader* reader, // TODO(bnc): Close connection with HTTP_MALFORMED_FRAME // if lowest three bits are not all zero. - // TODO(b/137359636): Handle partial delivery. if (frame->prioritized_type != ROOT_OF_TREE && !reader->ReadVarInt62(&frame->prioritized_element_id)) { // TODO(b/124216424): Use HTTP_MALFORMED_FRAME. @@ -477,12 +537,12 @@ bool HttpDecoder::ParsePriorityFrame(QuicDataReader* reader, if (!reader->ReadUInt8(&frame->weight)) { // TODO(b/124216424): Use HTTP_MALFORMED_FRAME. RaiseError(QUIC_INVALID_FRAME_DATA, - "Unable to read priority frame weight."); + "Unable to read PRIORITY frame weight."); return false; } if (!reader->IsDoneReading()) { // TODO(b/124216424): Use HTTP_MALFORMED_FRAME. - RaiseError(QUIC_INVALID_FRAME_DATA, "Superfluous data in priority frame."); + RaiseError(QUIC_INVALID_FRAME_DATA, "Superfluous data in PRIORITY frame."); return false; } return true; @@ -491,19 +551,26 @@ bool HttpDecoder::ParsePriorityFrame(QuicDataReader* reader, bool HttpDecoder::ParseSettingsFrame(QuicDataReader* reader, SettingsFrame* frame) { while (!reader->IsDoneReading()) { - // TODO(bnc): Handle partial delivery of both fields. uint64_t id; if (!reader->ReadVarInt62(&id)) { - RaiseError(QUIC_INTERNAL_ERROR, + // TODO(b/124216424): Use HTTP_MALFORMED_FRAME. + RaiseError(QUIC_INVALID_FRAME_DATA, "Unable to read settings frame identifier"); return false; } uint64_t content; if (!reader->ReadVarInt62(&content)) { - RaiseError(QUIC_INTERNAL_ERROR, "Unable to read settings frame content"); + // TODO(b/124216424): Use HTTP_MALFORMED_FRAME. + RaiseError(QUIC_INVALID_FRAME_DATA, + "Unable to read settings frame content"); + return false; + } + auto result = frame->values.insert({id, content}); + if (!result.second) { + // TODO(b/124216424): Use HTTP_SETTINGS_ERROR. + RaiseError(QUIC_INVALID_FRAME_DATA, "Duplicate SETTINGS identifier."); return false; } - frame->values[id] = content; } return true; } diff --git a/chromium/net/third_party/quiche/src/quic/core/http/http_decoder.h b/chromium/net/third_party/quiche/src/quic/core/http/http_decoder.h index 99c4d100701..30ebadc49ef 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/http_decoder.h +++ b/chromium/net/third_party/quiche/src/quic/core/http/http_decoder.h @@ -5,8 +5,11 @@ #ifndef QUICHE_QUIC_CORE_HTTP_HTTP_DECODER_H_ #define QUICHE_QUIC_CORE_HTTP_HTTP_DECODER_H_ +#include <cstdint> + #include "net/third_party/quiche/src/quic/core/http/http_frames.h" #include "net/third_party/quiche/src/quic/core/quic_error_codes.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" #include "net/third_party/quiche/src/quic/platform/api/quic_export.h" #include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" @@ -20,22 +23,6 @@ class HttpDecoderPeer; class QuicDataReader; -// Struct that stores meta data of an HTTP/3 frame. -// |header_length| is frame header length in bytes. -// |payload_length| is frame payload length in bytes. -struct QUIC_EXPORT_PRIVATE Http3FrameLengths { - Http3FrameLengths(QuicByteCount header, QuicByteCount payload) - : header_length(header), payload_length(payload) {} - - bool operator==(const Http3FrameLengths& other) const { - return (header_length == other.header_length) && - (payload_length == other.payload_length); - } - - QuicByteCount header_length; - QuicByteCount payload_length; -}; - // A class for decoding the HTTP frames that are exchanged in an HTTP over QUIC // session. class QUIC_EXPORT_PRIVATE HttpDecoder { @@ -49,10 +36,13 @@ class QUIC_EXPORT_PRIVATE HttpDecoder { // All the following methods return true to continue decoding, // and false to pause it. + // On*FrameStart() methods are called after the frame header is completely + // processed. At that point it is safe to consume + // |frame_length.header_length| bytes. // Called when a PRIORITY frame has been received. // |frame_length| contains PRIORITY frame length and payload length. - virtual bool OnPriorityFrameStart(Http3FrameLengths frame_length) = 0; + virtual bool OnPriorityFrameStart(QuicByteCount header_length) = 0; // Called when a PRIORITY frame has been successfully parsed. virtual bool OnPriorityFrame(const PriorityFrame& frame) = 0; @@ -67,7 +57,7 @@ class QUIC_EXPORT_PRIVATE HttpDecoder { virtual bool OnGoAwayFrame(const GoAwayFrame& frame) = 0; // Called when a SETTINGS frame has been received. - virtual bool OnSettingsFrameStart(Http3FrameLengths frame_length) = 0; + virtual bool OnSettingsFrameStart(QuicByteCount header_length) = 0; // Called when a SETTINGS frame has been successfully parsed. virtual bool OnSettingsFrame(const SettingsFrame& frame) = 0; @@ -77,7 +67,7 @@ class QUIC_EXPORT_PRIVATE HttpDecoder { // Called when a DATA frame has been received. // |frame_length| contains DATA frame length and payload length. - virtual bool OnDataFrameStart(Http3FrameLengths frame_length) = 0; + virtual bool OnDataFrameStart(QuicByteCount header_length) = 0; // Called when part of the payload of a DATA frame has been read. May be // called multiple times for a single frame. |payload| is guaranteed to be // non-empty. @@ -87,7 +77,7 @@ class QUIC_EXPORT_PRIVATE HttpDecoder { // Called when a HEADERS frame has been received. // |frame_length| contains HEADERS frame length and payload length. - virtual bool OnHeadersFrameStart(Http3FrameLengths frame_length) = 0; + virtual bool OnHeadersFrameStart(QuicByteCount header_length) = 0; // Called when part of the payload of a HEADERS frame has been read. May be // called multiple times for a single frame. |payload| is guaranteed to be // non-empty. @@ -98,7 +88,8 @@ class QUIC_EXPORT_PRIVATE HttpDecoder { // Called when a PUSH_PROMISE frame has been received for |push_id|. virtual bool OnPushPromiseFrameStart(PushId push_id, - Http3FrameLengths frame_length) = 0; + QuicByteCount header_length, + QuicByteCount push_id_length) = 0; // Called when part of the payload of a PUSH_PROMISE frame has been read. // May be called multiple times for a single frame. |payload| is guaranteed // to be non-empty. @@ -110,7 +101,7 @@ class QUIC_EXPORT_PRIVATE HttpDecoder { // Frame type might be reserved, Visitor must make sure to ignore. // |frame_length| contains frame length and payload length. virtual bool OnUnknownFrameStart(uint64_t frame_type, - Http3FrameLengths frame_length) = 0; + QuicByteCount header_length) = 0; // Called when part of the payload of the unknown frame has been read. May // be called multiple times for a single frame. |payload| is guaranteed to // be non-empty. @@ -181,6 +172,10 @@ class QUIC_EXPORT_PRIVATE HttpDecoder { // Buffers any remaining frame type field from |reader| into |type_buffer_|. void BufferFrameType(QuicDataReader* reader); + // Buffers at most |remaining_push_id_length_| from |reader| to + // |push_id_buffer_|. + void BufferPushId(QuicDataReader* reader); + // Sets |error_| and |error_detail_| accordingly. void RaiseError(QuicErrorCode error, std::string error_detail); @@ -211,6 +206,10 @@ class QUIC_EXPORT_PRIVATE HttpDecoder { QuicByteCount current_type_field_length_; // Remaining length that's needed for the frame's type field. QuicByteCount remaining_type_field_length_; + // Length of PUSH_PROMISE frame's push id. + QuicByteCount current_push_id_length_; + // Remaining length that's needed for PUSH_PROMISE frame's push id field. + QuicByteCount remaining_push_id_length_; // Last error. QuicErrorCode error_; // The issue which caused |error_| @@ -221,6 +220,8 @@ class QUIC_EXPORT_PRIVATE HttpDecoder { std::array<char, sizeof(uint64_t)> length_buffer_; // Remaining unparsed type field data. std::array<char, sizeof(uint64_t)> type_buffer_; + // Remaining unparsed push id data. + std::array<char, sizeof(uint64_t)> push_id_buffer_; }; } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/http/http_decoder_test.cc b/chromium/net/third_party/quiche/src/quic/core/http/http_decoder_test.cc index 635268bb368..6090cadee06 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/http_decoder_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/http/http_decoder_test.cc @@ -34,29 +34,31 @@ class MockVisitor : public HttpDecoder::Visitor { // Called if an error is detected. MOCK_METHOD1(OnError, void(HttpDecoder* decoder)); - MOCK_METHOD1(OnPriorityFrameStart, bool(Http3FrameLengths frame_lengths)); + MOCK_METHOD1(OnPriorityFrameStart, bool(QuicByteCount header_length)); MOCK_METHOD1(OnPriorityFrame, bool(const PriorityFrame& frame)); MOCK_METHOD1(OnCancelPushFrame, bool(const CancelPushFrame& frame)); MOCK_METHOD1(OnMaxPushIdFrame, bool(const MaxPushIdFrame& frame)); MOCK_METHOD1(OnGoAwayFrame, bool(const GoAwayFrame& frame)); - MOCK_METHOD1(OnSettingsFrameStart, bool(Http3FrameLengths frame_lengths)); + MOCK_METHOD1(OnSettingsFrameStart, bool(QuicByteCount header_length)); MOCK_METHOD1(OnSettingsFrame, bool(const SettingsFrame& frame)); MOCK_METHOD1(OnDuplicatePushFrame, bool(const DuplicatePushFrame& frame)); - MOCK_METHOD1(OnDataFrameStart, bool(Http3FrameLengths frame_lengths)); + MOCK_METHOD1(OnDataFrameStart, bool(QuicByteCount header_length)); MOCK_METHOD1(OnDataFramePayload, bool(QuicStringPiece payload)); MOCK_METHOD0(OnDataFrameEnd, bool()); - MOCK_METHOD1(OnHeadersFrameStart, bool(Http3FrameLengths frame_lengths)); + MOCK_METHOD1(OnHeadersFrameStart, bool(QuicByteCount header_length)); MOCK_METHOD1(OnHeadersFramePayload, bool(QuicStringPiece payload)); MOCK_METHOD0(OnHeadersFrameEnd, bool()); - MOCK_METHOD2(OnPushPromiseFrameStart, - bool(PushId push_id, Http3FrameLengths frame_lengths)); + MOCK_METHOD3(OnPushPromiseFrameStart, + bool(PushId push_id, + QuicByteCount header_length, + QuicByteCount push_id_length)); MOCK_METHOD1(OnPushPromiseFramePayload, bool(QuicStringPiece payload)); MOCK_METHOD0(OnPushPromiseFrameEnd, bool()); - MOCK_METHOD2(OnUnknownFrameStart, bool(uint64_t, Http3FrameLengths)); + MOCK_METHOD2(OnUnknownFrameStart, bool(uint64_t, QuicByteCount)); MOCK_METHOD1(OnUnknownFramePayload, bool(QuicStringPiece)); MOCK_METHOD0(OnUnknownFrameEnd, bool()); }; @@ -78,7 +80,7 @@ class HttpDecoderTest : public QuicTest { ON_CALL(visitor_, OnHeadersFrameStart(_)).WillByDefault(Return(true)); ON_CALL(visitor_, OnHeadersFramePayload(_)).WillByDefault(Return(true)); ON_CALL(visitor_, OnHeadersFrameEnd()).WillByDefault(Return(true)); - ON_CALL(visitor_, OnPushPromiseFrameStart(_, _)) + ON_CALL(visitor_, OnPushPromiseFrameStart(_, _, _)) .WillByDefault(Return(true)); ON_CALL(visitor_, OnPushPromiseFramePayload(_)).WillByDefault(Return(true)); ON_CALL(visitor_, OnPushPromiseFrameEnd()).WillByDefault(Return(true)); @@ -156,9 +158,7 @@ TEST_F(HttpDecoderTest, UnknownFrame) { writer.WriteStringPiece(data); } - EXPECT_CALL(visitor_, OnUnknownFrameStart( - frame_type, Http3FrameLengths(header_length, - payload_length))); + EXPECT_CALL(visitor_, OnUnknownFrameStart(frame_type, header_length)); if (payload_length > 0) { EXPECT_CALL(visitor_, OnUnknownFramePayload(Eq(data))); } @@ -202,19 +202,27 @@ TEST_F(HttpDecoderTest, CancelPush) { TEST_F(HttpDecoderTest, PushPromiseFrame) { InSequence s; - std::string input = - "\x05" // type (PUSH_PROMISE) - "\x08" // length - "\x01" // Push Id - "Headers"; // Header Block + std::string input( + "\x05" // type (PUSH PROMISE) + "\x0f" // length + "\xC0" + "\x00" + "\x00" + "\x00" + "\x00" + "\x00" + "\x01" + "\x01" // push id 257. + "Headers", + 17); // Visitor pauses processing. - EXPECT_CALL(visitor_, OnPushPromiseFrameStart(1, Http3FrameLengths(2, 8))) + EXPECT_CALL(visitor_, OnPushPromiseFrameStart(257, 2, 8)) .WillOnce(Return(false)); QuicStringPiece remaining_input(input); QuicByteCount processed_bytes = ProcessInputWithGarbageAppended(remaining_input); - EXPECT_EQ(3u, processed_bytes); + EXPECT_EQ(10u, processed_bytes); remaining_input = remaining_input.substr(processed_bytes); EXPECT_CALL(visitor_, OnPushPromiseFramePayload(QuicStringPiece("Headers"))) @@ -228,7 +236,7 @@ TEST_F(HttpDecoderTest, PushPromiseFrame) { EXPECT_EQ("", decoder_.error_detail()); // Process the full frame. - EXPECT_CALL(visitor_, OnPushPromiseFrameStart(1, Http3FrameLengths(2, 8))); + EXPECT_CALL(visitor_, OnPushPromiseFrameStart(257, 2, 8)); EXPECT_CALL(visitor_, OnPushPromiseFramePayload(QuicStringPiece("Headers"))); EXPECT_CALL(visitor_, OnPushPromiseFrameEnd()); EXPECT_EQ(input.size(), ProcessInput(input)); @@ -236,7 +244,7 @@ TEST_F(HttpDecoderTest, PushPromiseFrame) { EXPECT_EQ("", decoder_.error_detail()); // Process the frame incrementally. - EXPECT_CALL(visitor_, OnPushPromiseFrameStart(1, Http3FrameLengths(2, 8))); + EXPECT_CALL(visitor_, OnPushPromiseFrameStart(257, 2, 8)); EXPECT_CALL(visitor_, OnPushPromiseFramePayload(QuicStringPiece("H"))); EXPECT_CALL(visitor_, OnPushPromiseFramePayload(QuicStringPiece("e"))); EXPECT_CALL(visitor_, OnPushPromiseFramePayload(QuicStringPiece("a"))); @@ -248,6 +256,15 @@ TEST_F(HttpDecoderTest, PushPromiseFrame) { ProcessInputCharByChar(input); EXPECT_EQ(QUIC_NO_ERROR, decoder_.error()); EXPECT_EQ("", decoder_.error_detail()); + + // Process push id incrementally and append headers with last byte of push id. + EXPECT_CALL(visitor_, OnPushPromiseFrameStart(257, 2, 8)); + EXPECT_CALL(visitor_, OnPushPromiseFramePayload(QuicStringPiece("Headers"))); + EXPECT_CALL(visitor_, OnPushPromiseFrameEnd()); + ProcessInputCharByChar(input.substr(0, 9)); + EXPECT_EQ(8u, ProcessInput(input.substr(9))); + EXPECT_EQ(QUIC_NO_ERROR, decoder_.error()); + EXPECT_EQ("", decoder_.error_detail()); } TEST_F(HttpDecoderTest, MaxPushId) { @@ -323,8 +340,7 @@ TEST_F(HttpDecoderTest, PriorityFrame) { frame.weight = 0xFF; // Visitor pauses processing. - EXPECT_CALL(visitor_, OnPriorityFrameStart(Http3FrameLengths(2, 4))) - .WillOnce(Return(false)); + EXPECT_CALL(visitor_, OnPriorityFrameStart(2)).WillOnce(Return(false)); QuicStringPiece remaining_input(input); QuicByteCount processed_bytes = ProcessInputWithGarbageAppended(remaining_input); @@ -338,14 +354,14 @@ TEST_F(HttpDecoderTest, PriorityFrame) { EXPECT_EQ("", decoder_.error_detail()); // Process the full frame. - EXPECT_CALL(visitor_, OnPriorityFrameStart(Http3FrameLengths(2, 4))); + EXPECT_CALL(visitor_, OnPriorityFrameStart(2)); EXPECT_CALL(visitor_, OnPriorityFrame(frame)); EXPECT_EQ(input.size(), ProcessInput(input)); EXPECT_EQ(QUIC_NO_ERROR, decoder_.error()); EXPECT_EQ("", decoder_.error_detail()); // Process the frame incrementally. - EXPECT_CALL(visitor_, OnPriorityFrameStart(Http3FrameLengths(2, 4))); + EXPECT_CALL(visitor_, OnPriorityFrameStart(2)); EXPECT_CALL(visitor_, OnPriorityFrame(frame)); ProcessInputCharByChar(input); EXPECT_EQ(QUIC_NO_ERROR, decoder_.error()); @@ -362,7 +378,7 @@ TEST_F(HttpDecoderTest, PriorityFrame) { frame2.exclusive = true; frame2.weight = 0xFF; - EXPECT_CALL(visitor_, OnPriorityFrameStart(Http3FrameLengths(2, 2))); + EXPECT_CALL(visitor_, OnPriorityFrameStart(2)); EXPECT_CALL(visitor_, OnPriorityFrame(frame2)); EXPECT_EQ(input2.size(), ProcessInput(input2)); EXPECT_EQ(QUIC_NO_ERROR, decoder_.error()); @@ -389,11 +405,11 @@ TEST_F(HttpDecoderTest, CorruptPriorityFrame) { {payload1, 0, "Unable to read PRIORITY frame flags."}, {payload1, 1, "Unable to read prioritized_element_id."}, {payload1, 2, "Unable to read element_dependency_id."}, - {payload1, 3, "Unable to read priority frame weight."}, - {payload1, 5, "Superfluous data in priority frame."}, + {payload1, 3, "Unable to read PRIORITY frame weight."}, + {payload1, 5, "Superfluous data in PRIORITY frame."}, {payload2, 0, "Unable to read PRIORITY frame flags."}, - {payload2, 1, "Unable to read priority frame weight."}, - {payload2, 3, "Superfluous data in priority frame."}, + {payload2, 1, "Unable to read PRIORITY frame weight."}, + {payload2, 3, "Superfluous data in PRIORITY frame."}, }; for (const auto& test_data : kTestData) { @@ -404,8 +420,8 @@ TEST_F(HttpDecoderTest, CorruptPriorityFrame) { input.append(test_data.payload, test_data.payload_length); HttpDecoder decoder(&visitor_); - EXPECT_CALL(visitor_, OnPriorityFrameStart(Http3FrameLengths( - header_length, test_data.payload_length))); + EXPECT_CALL(visitor_, OnPriorityFrameStart(header_length)); + EXPECT_CALL(visitor_, OnError(&decoder)); QuicByteCount processed_bytes = decoder.ProcessInput(input.data(), input.size()); @@ -435,8 +451,7 @@ TEST_F(HttpDecoderTest, SettingsFrame) { // Visitor pauses processing. QuicStringPiece remaining_input(input); - EXPECT_CALL(visitor_, OnSettingsFrameStart(Http3FrameLengths(2, 7))) - .WillOnce(Return(false)); + EXPECT_CALL(visitor_, OnSettingsFrameStart(2)).WillOnce(Return(false)); QuicByteCount processed_bytes = ProcessInputWithGarbageAppended(remaining_input); EXPECT_EQ(2u, processed_bytes); @@ -449,20 +464,73 @@ TEST_F(HttpDecoderTest, SettingsFrame) { EXPECT_EQ("", decoder_.error_detail()); // Process the full frame. - EXPECT_CALL(visitor_, OnSettingsFrameStart(Http3FrameLengths(2, 7))); + EXPECT_CALL(visitor_, OnSettingsFrameStart(2)); EXPECT_CALL(visitor_, OnSettingsFrame(frame)); EXPECT_EQ(input.size(), ProcessInput(input)); EXPECT_EQ(QUIC_NO_ERROR, decoder_.error()); EXPECT_EQ("", decoder_.error_detail()); // Process the frame incrementally. - EXPECT_CALL(visitor_, OnSettingsFrameStart(Http3FrameLengths(2, 7))); + EXPECT_CALL(visitor_, OnSettingsFrameStart(2)); EXPECT_CALL(visitor_, OnSettingsFrame(frame)); ProcessInputCharByChar(input); EXPECT_EQ(QUIC_NO_ERROR, decoder_.error()); EXPECT_EQ("", decoder_.error_detail()); } +TEST_F(HttpDecoderTest, CorruptSettingsFrame) { + const char* const kPayload = + "\x42\x11" // two-byte id + "\x80\x22\x33\x44" // four-byte value + "\x58\x39" // two-byte id + "\xf0\x22\x33\x44\x55\x66\x77\x88"; // eight-byte value + struct { + size_t payload_length; + const char* const error_message; + } kTestData[] = { + {1, "Unable to read settings frame identifier"}, + {5, "Unable to read settings frame content"}, + {7, "Unable to read settings frame identifier"}, + {12, "Unable to read settings frame content"}, + }; + + for (const auto& test_data : kTestData) { + std::string input; + input.push_back(4u); // type SETTINGS + input.push_back(test_data.payload_length); + const size_t header_length = input.size(); + input.append(kPayload, test_data.payload_length); + + HttpDecoder decoder(&visitor_); + EXPECT_CALL(visitor_, OnSettingsFrameStart(header_length)); + EXPECT_CALL(visitor_, OnError(&decoder)); + + QuicByteCount processed_bytes = + decoder.ProcessInput(input.data(), input.size()); + EXPECT_EQ(input.size(), processed_bytes); + EXPECT_EQ(QUIC_INVALID_FRAME_DATA, decoder.error()); + EXPECT_EQ(test_data.error_message, decoder.error_detail()); + } +} + +TEST_F(HttpDecoderTest, DuplicateSettingsIdentifier) { + std::string input = + "\x04" // type (SETTINGS) + "\x04" // length + "\x01" // identifier + "\x01" // content + "\x01" // identifier + "\x02"; // content + + EXPECT_CALL(visitor_, OnSettingsFrameStart(2)); + EXPECT_CALL(visitor_, OnError(&decoder_)); + + EXPECT_EQ(input.size(), ProcessInput(input)); + + EXPECT_EQ(QUIC_INVALID_FRAME_DATA, decoder_.error()); + EXPECT_EQ("Duplicate SETTINGS identifier.", decoder_.error_detail()); +} + TEST_F(HttpDecoderTest, DataFrame) { InSequence s; std::string input( @@ -472,8 +540,7 @@ TEST_F(HttpDecoderTest, DataFrame) { 7); // Visitor pauses processing. - EXPECT_CALL(visitor_, OnDataFrameStart(Http3FrameLengths(2, 5))) - .WillOnce(Return(false)); + EXPECT_CALL(visitor_, OnDataFrameStart(2)).WillOnce(Return(false)); QuicStringPiece remaining_input(input); QuicByteCount processed_bytes = ProcessInputWithGarbageAppended(remaining_input); @@ -491,7 +558,7 @@ TEST_F(HttpDecoderTest, DataFrame) { EXPECT_EQ("", decoder_.error_detail()); // Process the full frame. - EXPECT_CALL(visitor_, OnDataFrameStart(Http3FrameLengths(2, 5))); + EXPECT_CALL(visitor_, OnDataFrameStart(2)); EXPECT_CALL(visitor_, OnDataFramePayload(QuicStringPiece("Data!"))); EXPECT_CALL(visitor_, OnDataFrameEnd()); EXPECT_EQ(input.size(), ProcessInput(input)); @@ -499,7 +566,7 @@ TEST_F(HttpDecoderTest, DataFrame) { EXPECT_EQ("", decoder_.error_detail()); // Process the frame incrementally. - EXPECT_CALL(visitor_, OnDataFrameStart(Http3FrameLengths(2, 5))); + EXPECT_CALL(visitor_, OnDataFrameStart(2)); EXPECT_CALL(visitor_, OnDataFramePayload(QuicStringPiece("D"))); EXPECT_CALL(visitor_, OnDataFramePayload(QuicStringPiece("a"))); EXPECT_CALL(visitor_, OnDataFramePayload(QuicStringPiece("t"))); @@ -526,7 +593,7 @@ TEST_F(HttpDecoderTest, FrameHeaderPartialDelivery) { EXPECT_EQ("", decoder_.error_detail()); // Send the rest of the header. - EXPECT_CALL(visitor_, OnDataFrameStart(Http3FrameLengths(3, 2048))); + EXPECT_CALL(visitor_, OnDataFrameStart(3)); EXPECT_EQ(header_length - 1, decoder_.ProcessInput(header.data() + 1, header_length - 1)); EXPECT_EQ(QUIC_NO_ERROR, decoder_.error()); @@ -544,22 +611,20 @@ TEST_F(HttpDecoderTest, PartialDeliveryOfLargeFrameType) { // Use a reserved type that takes four bytes as a varint. const uint64_t frame_type = 0x1f * 0x222 + 0x21; const QuicByteCount payload_length = 0; - const QuicByteCount total_length = + const QuicByteCount header_length = QuicDataWriter::GetVarInt62Len(frame_type) + QuicDataWriter::GetVarInt62Len(payload_length); - auto input = QuicMakeUnique<char[]>(total_length); - QuicDataWriter writer(total_length, input.get()); + auto input = QuicMakeUnique<char[]>(header_length); + QuicDataWriter writer(header_length, input.get()); writer.WriteVarInt62(frame_type); writer.WriteVarInt62(payload_length); - EXPECT_CALL(visitor_, - OnUnknownFrameStart( - frame_type, Http3FrameLengths(total_length, payload_length))); + EXPECT_CALL(visitor_, OnUnknownFrameStart(frame_type, header_length)); EXPECT_CALL(visitor_, OnUnknownFrameEnd()); auto raw_input = input.get(); - for (uint64_t i = 0; i < total_length; ++i) { + for (uint64_t i = 0; i < header_length; ++i) { char c = raw_input[i]; EXPECT_EQ(1u, decoder_.ProcessInput(&c, 1)); } @@ -604,8 +669,7 @@ TEST_F(HttpDecoderTest, HeadersFrame) { "Headers"; // headers // Visitor pauses processing. - EXPECT_CALL(visitor_, OnHeadersFrameStart(Http3FrameLengths(2, 7))) - .WillOnce(Return(false)); + EXPECT_CALL(visitor_, OnHeadersFrameStart(2)).WillOnce(Return(false)); QuicStringPiece remaining_input(input); QuicByteCount processed_bytes = ProcessInputWithGarbageAppended(remaining_input); @@ -623,7 +687,7 @@ TEST_F(HttpDecoderTest, HeadersFrame) { EXPECT_EQ("", decoder_.error_detail()); // Process the full frame. - EXPECT_CALL(visitor_, OnHeadersFrameStart(Http3FrameLengths(2, 7))); + EXPECT_CALL(visitor_, OnHeadersFrameStart(2)); EXPECT_CALL(visitor_, OnHeadersFramePayload(QuicStringPiece("Headers"))); EXPECT_CALL(visitor_, OnHeadersFrameEnd()); EXPECT_EQ(input.size(), ProcessInput(input)); @@ -631,7 +695,7 @@ TEST_F(HttpDecoderTest, HeadersFrame) { EXPECT_EQ("", decoder_.error_detail()); // Process the frame incrementally. - EXPECT_CALL(visitor_, OnHeadersFrameStart(Http3FrameLengths(2, 7))); + EXPECT_CALL(visitor_, OnHeadersFrameStart(2)); EXPECT_CALL(visitor_, OnHeadersFramePayload(QuicStringPiece("H"))); EXPECT_CALL(visitor_, OnHeadersFramePayload(QuicStringPiece("e"))); EXPECT_CALL(visitor_, OnHeadersFramePayload(QuicStringPiece("a"))); @@ -653,8 +717,7 @@ TEST_F(HttpDecoderTest, EmptyDataFrame) { 2); // Visitor pauses processing. - EXPECT_CALL(visitor_, OnDataFrameStart(Http3FrameLengths(2, 0))) - .WillOnce(Return(false)); + EXPECT_CALL(visitor_, OnDataFrameStart(2)).WillOnce(Return(false)); EXPECT_EQ(input.size(), ProcessInputWithGarbageAppended(input)); EXPECT_CALL(visitor_, OnDataFrameEnd()).WillOnce(Return(false)); @@ -663,14 +726,14 @@ TEST_F(HttpDecoderTest, EmptyDataFrame) { EXPECT_EQ("", decoder_.error_detail()); // Process the full frame. - EXPECT_CALL(visitor_, OnDataFrameStart(Http3FrameLengths(2, 0))); + EXPECT_CALL(visitor_, OnDataFrameStart(2)); EXPECT_CALL(visitor_, OnDataFrameEnd()); EXPECT_EQ(input.size(), ProcessInput(input)); EXPECT_EQ(QUIC_NO_ERROR, decoder_.error()); EXPECT_EQ("", decoder_.error_detail()); // Process the frame incrementally. - EXPECT_CALL(visitor_, OnDataFrameStart(Http3FrameLengths(2, 0))); + EXPECT_CALL(visitor_, OnDataFrameStart(2)); EXPECT_CALL(visitor_, OnDataFrameEnd()); ProcessInputCharByChar(input); EXPECT_EQ(QUIC_NO_ERROR, decoder_.error()); @@ -685,8 +748,7 @@ TEST_F(HttpDecoderTest, EmptyHeadersFrame) { 2); // Visitor pauses processing. - EXPECT_CALL(visitor_, OnHeadersFrameStart(Http3FrameLengths(2, 0))) - .WillOnce(Return(false)); + EXPECT_CALL(visitor_, OnHeadersFrameStart(2)).WillOnce(Return(false)); EXPECT_EQ(input.size(), ProcessInputWithGarbageAppended(input)); EXPECT_CALL(visitor_, OnHeadersFrameEnd()).WillOnce(Return(false)); @@ -695,14 +757,14 @@ TEST_F(HttpDecoderTest, EmptyHeadersFrame) { EXPECT_EQ("", decoder_.error_detail()); // Process the full frame. - EXPECT_CALL(visitor_, OnHeadersFrameStart(Http3FrameLengths(2, 0))); + EXPECT_CALL(visitor_, OnHeadersFrameStart(2)); EXPECT_CALL(visitor_, OnHeadersFrameEnd()); EXPECT_EQ(input.size(), ProcessInput(input)); EXPECT_EQ(QUIC_NO_ERROR, decoder_.error()); EXPECT_EQ("", decoder_.error_detail()); // Process the frame incrementally. - EXPECT_CALL(visitor_, OnHeadersFrameStart(Http3FrameLengths(2, 0))); + EXPECT_CALL(visitor_, OnHeadersFrameStart(2)); EXPECT_CALL(visitor_, OnHeadersFrameEnd()); ProcessInputCharByChar(input); EXPECT_EQ(QUIC_NO_ERROR, decoder_.error()); @@ -717,7 +779,7 @@ TEST_F(HttpDecoderTest, PushPromiseFrameNoHeaders) { "\x01"; // Push Id // Visitor pauses processing. - EXPECT_CALL(visitor_, OnPushPromiseFrameStart(1, Http3FrameLengths(2, 1))) + EXPECT_CALL(visitor_, OnPushPromiseFrameStart(1, 2, 1)) .WillOnce(Return(false)); EXPECT_EQ(input.size(), ProcessInputWithGarbageAppended(input)); @@ -727,14 +789,14 @@ TEST_F(HttpDecoderTest, PushPromiseFrameNoHeaders) { EXPECT_EQ("", decoder_.error_detail()); // Process the full frame. - EXPECT_CALL(visitor_, OnPushPromiseFrameStart(1, Http3FrameLengths(2, 1))); + EXPECT_CALL(visitor_, OnPushPromiseFrameStart(1, 2, 1)); EXPECT_CALL(visitor_, OnPushPromiseFrameEnd()); EXPECT_EQ(input.size(), ProcessInput(input)); EXPECT_EQ(QUIC_NO_ERROR, decoder_.error()); EXPECT_EQ("", decoder_.error_detail()); // Process the frame incrementally. - EXPECT_CALL(visitor_, OnPushPromiseFrameStart(1, Http3FrameLengths(2, 1))); + EXPECT_CALL(visitor_, OnPushPromiseFrameStart(1, 2, 1)); EXPECT_CALL(visitor_, OnPushPromiseFrameEnd()); ProcessInputCharByChar(input); EXPECT_EQ(QUIC_NO_ERROR, decoder_.error()); @@ -749,7 +811,7 @@ TEST_F(HttpDecoderTest, MalformedFrameWithOverlyLargePayload) { // Process the full frame. EXPECT_CALL(visitor_, OnError(&decoder_)); EXPECT_EQ(2u, ProcessInput(input)); - EXPECT_EQ(QUIC_INTERNAL_ERROR, decoder_.error()); + EXPECT_EQ(QUIC_INVALID_FRAME_DATA, decoder_.error()); EXPECT_EQ("Frame is too large", decoder_.error_detail()); } @@ -764,7 +826,7 @@ TEST_F(HttpDecoderTest, MalformedSettingsFrame) { writer.WriteStringPiece("Malformed payload"); EXPECT_CALL(visitor_, OnError(&decoder_)); EXPECT_EQ(5u, decoder_.ProcessInput(input, QUIC_ARRAYSIZE(input))); - EXPECT_EQ(QUIC_INTERNAL_ERROR, decoder_.error()); + EXPECT_EQ(QUIC_INVALID_FRAME_DATA, decoder_.error()); EXPECT_EQ("Frame is too large", decoder_.error_detail()); } @@ -780,7 +842,7 @@ TEST_F(HttpDecoderTest, HeadersPausedThenData) { 16); // Visitor pauses processing, maybe because header decompression is blocked. - EXPECT_CALL(visitor_, OnHeadersFrameStart(Http3FrameLengths(2, 7))); + EXPECT_CALL(visitor_, OnHeadersFrameStart(2)); EXPECT_CALL(visitor_, OnHeadersFramePayload(QuicStringPiece("Headers"))); EXPECT_CALL(visitor_, OnHeadersFrameEnd()).WillOnce(Return(false)); QuicStringPiece remaining_input(input); @@ -790,7 +852,7 @@ TEST_F(HttpDecoderTest, HeadersPausedThenData) { remaining_input = remaining_input.substr(processed_bytes); // Process DATA frame. - EXPECT_CALL(visitor_, OnDataFrameStart(Http3FrameLengths(2, 5))); + EXPECT_CALL(visitor_, OnDataFrameStart(2)); EXPECT_CALL(visitor_, OnDataFramePayload(QuicStringPiece("Data!"))); EXPECT_CALL(visitor_, OnDataFrameEnd()); @@ -801,6 +863,77 @@ TEST_F(HttpDecoderTest, HeadersPausedThenData) { EXPECT_EQ("", decoder_.error_detail()); } +TEST_F(HttpDecoderTest, CorruptFrame) { + InSequence s; + + struct { + const char* const input; + const char* const error_message; + } kTestData[] = {{"\x03" // type (CANCEL_PUSH) + "\x01" // length + "\x40", // first byte of two-byte varint push id + "Unable to read push_id"}, + {"\x03" // type (CANCEL_PUSH) + "\x04" // length + "\x05" // valid push id + "foo", // superfluous data + "Superfluous data in CANCEL_PUSH frame."}, + {"\x05" // type (PUSH_PROMISE) + "\x01" // length + "\x40", // first byte of two-byte varint push id + "PUSH_PROMISE frame malformed."}, + {"\x0D" // type (MAX_PUSH_ID) + "\x01" // length + "\x40", // first byte of two-byte varint push id + "Unable to read push_id"}, + {"\x0D" // type (MAX_PUSH_ID) + "\x04" // length + "\x05" // valid push id + "foo", // superfluous data + "Superfluous data in MAX_PUSH_ID frame."}, + {"\x0E" // type (DUPLICATE_PUSH) + "\x01" // length + "\x40", // first byte of two-byte varint push id + "Unable to read push_id"}, + {"\x0E" // type (DUPLICATE_PUSH) + "\x04" // length + "\x05" // valid push id + "foo", // superfluous data + "Superfluous data in DUPLICATE_PUSH frame."}, + {"\x07" // type (GOAWAY) + "\x01" // length + "\x40", // first byte of two-byte varint stream id + "Unable to read GOAWAY stream_id"}, + {"\x07" // type (GOAWAY) + "\x04" // length + "\x05" // valid stream id + "foo", // superfluous data + "Superfluous data in GOAWAY frame."}}; + + for (const auto& test_data : kTestData) { + { + HttpDecoder decoder(&visitor_); + EXPECT_CALL(visitor_, OnError(&decoder)); + + QuicStringPiece input(test_data.input); + decoder.ProcessInput(input.data(), input.size()); + EXPECT_EQ(QUIC_INVALID_FRAME_DATA, decoder.error()); + EXPECT_EQ(test_data.error_message, decoder.error_detail()); + } + { + HttpDecoder decoder(&visitor_); + EXPECT_CALL(visitor_, OnError(&decoder)); + + QuicStringPiece input(test_data.input); + for (auto c : input) { + decoder.ProcessInput(&c, 1); + } + EXPECT_EQ(QUIC_INVALID_FRAME_DATA, decoder.error()); + EXPECT_EQ(test_data.error_message, decoder.error_detail()); + } + } +} // namespace test + } // namespace test } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_headers_stream_test.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_headers_stream_test.cc index ebfa6cc7e3a..2035d47a4ff 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/quic_headers_stream_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_headers_stream_test.cc @@ -166,6 +166,9 @@ std::vector<TestParams> GetTestParams() { std::vector<TestParams> params; ParsedQuicVersionVector all_supported_versions = AllSupportedVersions(); for (size_t i = 0; i < all_supported_versions.size(); ++i) { + if (VersionUsesQpack(all_supported_versions[i].transport_version)) { + continue; + } for (Perspective p : {Perspective::IS_SERVER, Perspective::IS_CLIENT}) { params.emplace_back(all_supported_versions[i], p); } @@ -284,7 +287,8 @@ class QuicHeadersStreamTest : public QuicTestWithParam<TestParams> { _, _, NO_FIN)) .WillOnce(WithArgs<2>(Invoke(this, &QuicHeadersStreamTest::SaveIov))); QuicSpdySessionPeer::WriteHeadersOnHeadersStream( - &session_, stream_id, headers_.Clone(), fin, priority, nullptr); + &session_, stream_id, headers_.Clone(), fin, + spdy::SpdyStreamPrecedence(priority), nullptr); // Parse the outgoing data and check that it matches was was written. if (is_request) { @@ -377,10 +381,6 @@ TEST_P(QuicHeadersStreamTest, StreamId) { } TEST_P(QuicHeadersStreamTest, WriteHeaders) { - if (VersionUsesQpack(transport_version())) { - return; - } - for (QuicStreamId stream_id = client_id_1_; stream_id < client_id_3_; stream_id += next_stream_id_) { for (bool fin : {false, true}) { @@ -397,9 +397,8 @@ TEST_P(QuicHeadersStreamTest, WriteHeaders) { } TEST_P(QuicHeadersStreamTest, WritePushPromises) { - if (GetParam().version.DoesNotHaveHeadersStream()) { - return; - } + session_.set_max_allowed_push_id(kMaxQuicStreamId); + for (QuicStreamId stream_id = client_id_1_; stream_id < client_id_3_; stream_id += next_stream_id_) { QuicStreamId promised_stream_id = NextPromisedStreamId(); @@ -435,10 +434,6 @@ TEST_P(QuicHeadersStreamTest, WritePushPromises) { } TEST_P(QuicHeadersStreamTest, ProcessRawData) { - if (VersionUsesQpack(transport_version())) { - return; - } - for (QuicStreamId stream_id = client_id_1_; stream_id < client_id_3_; stream_id += next_stream_id_) { for (bool fin : {false, true}) { @@ -451,7 +446,8 @@ TEST_P(QuicHeadersStreamTest, ProcessRawData) { headers_frame.set_has_priority(true); headers_frame.set_weight(Spdy3PriorityToHttp2Weight(0)); frame = framer_->SerializeFrame(headers_frame); - EXPECT_CALL(session_, OnStreamHeadersPriority(stream_id, 0)); + EXPECT_CALL(session_, OnStreamHeadersPriority( + stream_id, spdy::SpdyStreamPrecedence(0))); } else { SpdyHeadersIR headers_frame(stream_id, headers_.Clone()); headers_frame.set_fin(fin); @@ -471,9 +467,6 @@ TEST_P(QuicHeadersStreamTest, ProcessRawData) { } TEST_P(QuicHeadersStreamTest, ProcessPushPromise) { - if (GetParam().version.DoesNotHaveHeadersStream()) { - return; - } if (perspective() == Perspective::IS_SERVER) { return; } @@ -512,9 +505,6 @@ TEST_P(QuicHeadersStreamTest, ProcessPushPromise) { } TEST_P(QuicHeadersStreamTest, ProcessPriorityFrame) { - if (GetParam().version.DoesNotHaveHeadersStream()) { - return; - } QuicStreamId parent_stream_id = 0; for (SpdyPriority priority = 0; priority < 7; ++priority) { for (QuicStreamId stream_id = client_id_1_; stream_id < client_id_3_; @@ -536,7 +526,10 @@ TEST_P(QuicHeadersStreamTest, ProcessPriorityFrame) { .WillRepeatedly(InvokeWithoutArgs( this, &QuicHeadersStreamTest::TearDownLocalConnectionState)); } else { - EXPECT_CALL(session_, OnPriorityFrame(stream_id, priority)).Times(1); + EXPECT_CALL( + session_, + OnPriorityFrame(stream_id, spdy::SpdyStreamPrecedence(priority))) + .Times(1); } stream_frame_.data_buffer = frame.data(); stream_frame_.data_length = frame.size(); @@ -566,10 +559,6 @@ TEST_P(QuicHeadersStreamTest, ProcessPushPromiseDisabledSetting) { } TEST_P(QuicHeadersStreamTest, ProcessLargeRawData) { - if (VersionUsesQpack(transport_version())) { - return; - } - // We want to create a frame that is more than the SPDY Framer's max control // frame size, which is 16K, but less than the HPACK decoders max decode // buffer size, which is 32K. @@ -588,7 +577,8 @@ TEST_P(QuicHeadersStreamTest, ProcessLargeRawData) { headers_frame.set_has_priority(true); headers_frame.set_weight(Spdy3PriorityToHttp2Weight(0)); frame = framer_->SerializeFrame(headers_frame); - EXPECT_CALL(session_, OnStreamHeadersPriority(stream_id, 0)); + EXPECT_CALL(session_, OnStreamHeadersPriority( + stream_id, spdy::SpdyStreamPrecedence(0))); } else { SpdyHeadersIR headers_frame(stream_id, headers_.Clone()); headers_frame.set_fin(fin); @@ -743,10 +733,6 @@ TEST_P(QuicHeadersStreamTest, NoConnectionLevelFlowControl) { } TEST_P(QuicHeadersStreamTest, HpackDecoderDebugVisitor) { - if (VersionUsesQpack(transport_version())) { - return; - } - auto hpack_decoder_visitor = QuicMakeUnique<StrictMock<MockQuicHpackDebugVisitor>>(); { @@ -778,7 +764,8 @@ TEST_P(QuicHeadersStreamTest, HpackDecoderDebugVisitor) { headers_frame.set_has_priority(true); headers_frame.set_weight(Spdy3PriorityToHttp2Weight(0)); frame = framer_->SerializeFrame(headers_frame); - EXPECT_CALL(session_, OnStreamHeadersPriority(stream_id, 0)); + EXPECT_CALL(session_, OnStreamHeadersPriority( + stream_id, spdy::SpdyStreamPrecedence(0))); } else { SpdyHeadersIR headers_frame(stream_id, headers_.Clone()); headers_frame.set_fin(fin); @@ -799,10 +786,6 @@ TEST_P(QuicHeadersStreamTest, HpackDecoderDebugVisitor) { } TEST_P(QuicHeadersStreamTest, HpackEncoderDebugVisitor) { - if (VersionUsesQpack(transport_version())) { - return; - } - auto hpack_encoder_visitor = QuicMakeUnique<StrictMock<MockQuicHpackDebugVisitor>>(); if (perspective() == Perspective::IS_SERVER) { diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_receive_control_stream.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_receive_control_stream.cc index 93fae3a0111..0ff9e38baf6 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/quic_receive_control_stream.cc +++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_receive_control_stream.cc @@ -27,14 +27,14 @@ class QuicReceiveControlStream::HttpDecoderVisitor ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); } - bool OnPriorityFrameStart(Http3FrameLengths frame_lengths) override { + bool OnPriorityFrameStart(QuicByteCount header_length) override { if (stream_->session()->perspective() == Perspective::IS_CLIENT) { stream_->session()->connection()->CloseConnection( QUIC_HTTP_DECODER_ERROR, "Server must not send Priority frames.", ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); return false; } - return stream_->OnPriorityFrameStart(frame_lengths); + return stream_->OnPriorityFrameStart(header_length); } bool OnPriorityFrame(const PriorityFrame& frame) override { @@ -52,7 +52,13 @@ class QuicReceiveControlStream::HttpDecoderVisitor return false; } - bool OnMaxPushIdFrame(const MaxPushIdFrame& /*frame*/) override { + bool OnMaxPushIdFrame(const MaxPushIdFrame& frame) override { + if (stream_->session()->perspective() == Perspective::IS_SERVER) { + QuicSpdySession* spdy_session = + static_cast<QuicSpdySession*>(stream_->session()); + spdy_session->set_max_allowed_push_id(frame.push_id); + return true; + } CloseConnectionOnWrongFrame("Max Push Id"); return false; } @@ -62,8 +68,8 @@ class QuicReceiveControlStream::HttpDecoderVisitor return false; } - bool OnSettingsFrameStart(Http3FrameLengths frame_lengths) override { - return stream_->OnSettingsFrameStart(frame_lengths); + bool OnSettingsFrameStart(QuicByteCount header_length) override { + return stream_->OnSettingsFrameStart(header_length); } bool OnSettingsFrame(const SettingsFrame& frame) override { @@ -75,7 +81,7 @@ class QuicReceiveControlStream::HttpDecoderVisitor return false; } - bool OnDataFrameStart(Http3FrameLengths /*frame_lengths*/) override { + bool OnDataFrameStart(QuicByteCount /*header_length*/) override { CloseConnectionOnWrongFrame("Data"); return false; } @@ -90,7 +96,7 @@ class QuicReceiveControlStream::HttpDecoderVisitor return false; } - bool OnHeadersFrameStart(Http3FrameLengths /*frame_length*/) override { + bool OnHeadersFrameStart(QuicByteCount /*frame_length*/) override { CloseConnectionOnWrongFrame("Headers"); return false; } @@ -106,7 +112,8 @@ class QuicReceiveControlStream::HttpDecoderVisitor } bool OnPushPromiseFrameStart(PushId /*push_id*/, - Http3FrameLengths /*frame_length*/) override { + QuicByteCount /*frame_length*/, + QuicByteCount /*push_id_length*/) override { CloseConnectionOnWrongFrame("Push Promise"); return false; } @@ -122,7 +129,7 @@ class QuicReceiveControlStream::HttpDecoderVisitor } bool OnUnknownFrameStart(uint64_t /* frame_type */, - Http3FrameLengths /* frame_length */) override { + QuicByteCount /* frame_length */) override { // Ignore unknown frame types. return true; } @@ -138,11 +145,11 @@ class QuicReceiveControlStream::HttpDecoderVisitor } private: - void CloseConnectionOnWrongFrame(std::string frame_type) { + void CloseConnectionOnWrongFrame(QuicStringPiece frame_type) { // TODO(renjietang): Change to HTTP/3 error type. stream_->session()->connection()->CloseConnection( QUIC_HTTP_DECODER_ERROR, - frame_type + " frame received on control stream", + QuicStrCat(frame_type, " frame received on control stream"), ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); } @@ -189,7 +196,7 @@ void QuicReceiveControlStream::OnDataAvailable() { } bool QuicReceiveControlStream::OnSettingsFrameStart( - Http3FrameLengths /* frame_lengths */) { + QuicByteCount /* header_length */) { if (settings_frame_received_) { // TODO(renjietang): Change error code to HTTP_UNEXPECTED_FRAME. session()->connection()->CloseConnection( @@ -207,24 +214,14 @@ bool QuicReceiveControlStream::OnSettingsFrame(const SettingsFrame& settings) { QUIC_DVLOG(1) << "Control Stream " << id() << " received settings frame: " << settings; QuicSpdySession* spdy_session = static_cast<QuicSpdySession*>(session()); - for (auto& it : settings.values) { - uint64_t setting_id = it.first; - switch (setting_id) { - case kSettingsMaxHeaderListSize: - spdy_session->set_max_outbound_header_list_size(it.second); - break; - case kSettingsNumPlaceholders: - // TODO: Support placeholder setting - break; - default: - break; - } + for (const auto& setting : settings.values) { + spdy_session->OnSetting(setting.first, setting.second); } return true; } bool QuicReceiveControlStream::OnPriorityFrameStart( - Http3FrameLengths /* frame_lengths */) { + QuicByteCount /* header_length */) { DCHECK_EQ(Perspective::IS_SERVER, session()->perspective()); return true; } @@ -237,7 +234,7 @@ bool QuicReceiveControlStream::OnPriorityFrame(const PriorityFrame& priority) { // that the server is not permitted to open. In that case, simply drop the // frame. if (stream) { - stream->SetPriority(priority.weight); + stream->SetPriority(spdy::SpdyStreamPrecedence(priority.weight)); } return true; } diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_receive_control_stream.h b/chromium/net/third_party/quiche/src/quic/core/http/quic_receive_control_stream.h index f83b980b8b4..84217ce8fd2 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/quic_receive_control_stream.h +++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_receive_control_stream.h @@ -7,6 +7,7 @@ #include "net/third_party/quiche/src/quic/core/http/http_decoder.h" #include "net/third_party/quiche/src/quic/core/quic_stream.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" #include "net/third_party/quiche/src/quic/platform/api/quic_export.h" namespace quic { @@ -35,9 +36,9 @@ class QUIC_EXPORT_PRIVATE QuicReceiveControlStream : public QuicStream { class HttpDecoderVisitor; // Called from HttpDecoderVisitor. - bool OnSettingsFrameStart(Http3FrameLengths frame_lengths); + bool OnSettingsFrameStart(QuicByteCount header_length); bool OnSettingsFrame(const SettingsFrame& settings); - bool OnPriorityFrameStart(Http3FrameLengths frame_lengths); + bool OnPriorityFrameStart(QuicByteCount header_length); // TODO(renjietang): Decode Priority in HTTP/3 style. bool OnPriorityFrame(const PriorityFrame& priority); diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_receive_control_stream_test.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_receive_control_stream_test.cc index aaf60e9770e..e305102344f 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/quic_receive_control_stream_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_receive_control_stream_test.cc @@ -68,13 +68,19 @@ class QuicReceiveControlStreamTest : public QuicTestWithParam<TestParams> { SupportedVersions(GetParam().version))), session_(connection_) { session_.Initialize(); - auto pending = QuicMakeUnique<PendingStream>( - QuicUtils::GetFirstUnidirectionalStreamId( - GetParam().version.transport_version, - QuicUtils::InvertPerspective(perspective())), - &session_); + QuicStreamId id = perspective() == Perspective::IS_SERVER + ? GetNthClientInitiatedUnidirectionalStreamId( + session_.transport_version(), 3) + : GetNthServerInitiatedUnidirectionalStreamId( + session_.transport_version(), 3); + char type[] = {kControlStream}; + + QuicStreamFrame data1(id, false, 0, QuicStringPiece(type, 1)); + session_.OnStreamFrame(data1); + receive_control_stream_ = - QuicMakeUnique<QuicReceiveControlStream>(pending.get()); + QuicSpdySessionPeer::GetReceiveControlStream(&session_); + stream_ = new TestStream(GetNthClientInitiatedBidirectionalStreamId( GetParam().version.transport_version, 0), &session_); @@ -100,7 +106,7 @@ class QuicReceiveControlStreamTest : public QuicTestWithParam<TestParams> { } QuicStreamOffset NumBytesConsumed() { - return QuicStreamPeer::sequencer(receive_control_stream_.get()) + return QuicStreamPeer::sequencer(receive_control_stream_) ->NumBytesConsumed(); } @@ -108,7 +114,7 @@ class QuicReceiveControlStreamTest : public QuicTestWithParam<TestParams> { MockAlarmFactory alarm_factory_; StrictMock<MockQuicConnection>* connection_; StrictMock<MockQuicSpdySession> session_; - std::unique_ptr<QuicReceiveControlStream> receive_control_stream_; + QuicReceiveControlStream* receive_control_stream_; TestStream* stream_; }; @@ -128,10 +134,9 @@ TEST_P(QuicReceiveControlStreamTest, ResetControlStream) { TEST_P(QuicReceiveControlStreamTest, ReceiveSettings) { SettingsFrame settings; settings.values[3] = 2; - settings.values[kSettingsMaxHeaderListSize] = 5; + settings.values[SETTINGS_MAX_HEADER_LIST_SIZE] = 5; std::string data = EncodeSettings(settings); - QuicStreamFrame frame(receive_control_stream_->id(), false, 0, - QuicStringPiece(data)); + QuicStreamFrame frame(receive_control_stream_->id(), false, 1, data); EXPECT_NE(5u, session_.max_outbound_header_list_size()); receive_control_stream_->OnStreamFrame(frame); EXPECT_EQ(5u, session_.max_outbound_header_list_size()); @@ -148,15 +153,15 @@ TEST_P(QuicReceiveControlStreamTest, ReceiveSettingsTwice) { std::string settings_frame = EncodeSettings(settings); - EXPECT_EQ(0u, NumBytesConsumed()); + EXPECT_EQ(1u, NumBytesConsumed()); // Receive first SETTINGS frame. receive_control_stream_->OnStreamFrame( QuicStreamFrame(receive_control_stream_->id(), /* fin = */ false, - /* offset = */ 0, settings_frame)); + /* offset = */ 1, settings_frame)); // First SETTINGS frame is consumed. - EXPECT_EQ(settings_frame.size(), NumBytesConsumed()); + EXPECT_EQ(settings_frame.size() + 1, NumBytesConsumed()); // Second SETTINGS frame causes the connection to be closed. EXPECT_CALL(*connection_, @@ -168,28 +173,26 @@ TEST_P(QuicReceiveControlStreamTest, ReceiveSettingsTwice) { EXPECT_CALL(session_, OnConnectionClosed(_, _)); // Receive second SETTINGS frame. - receive_control_stream_->OnStreamFrame( - QuicStreamFrame(receive_control_stream_->id(), /* fin = */ false, - /* offset = */ settings_frame.size(), settings_frame)); + receive_control_stream_->OnStreamFrame(QuicStreamFrame( + receive_control_stream_->id(), /* fin = */ false, + /* offset = */ settings_frame.size() + 1, settings_frame)); // Frame header of second SETTINGS frame is consumed, but not frame payload. QuicByteCount settings_frame_header_length = 2; - EXPECT_EQ(settings_frame.size() + settings_frame_header_length, + EXPECT_EQ(settings_frame.size() + settings_frame_header_length + 1, NumBytesConsumed()); } TEST_P(QuicReceiveControlStreamTest, ReceiveSettingsFragments) { SettingsFrame settings; settings.values[3] = 2; - settings.values[kSettingsMaxHeaderListSize] = 5; + settings.values[SETTINGS_MAX_HEADER_LIST_SIZE] = 5; std::string data = EncodeSettings(settings); std::string data1 = data.substr(0, 1); std::string data2 = data.substr(1, data.length() - 1); - QuicStreamFrame frame(receive_control_stream_->id(), false, 0, - QuicStringPiece(data.data(), 1)); - QuicStreamFrame frame2(receive_control_stream_->id(), false, 1, - QuicStringPiece(data.data() + 1, data.length() - 1)); + QuicStreamFrame frame(receive_control_stream_->id(), false, 1, data1); + QuicStreamFrame frame2(receive_control_stream_->id(), false, 2, data2); EXPECT_NE(5u, session_.max_outbound_header_list_size()); receive_control_stream_->OnStreamFrame(frame); receive_control_stream_->OnStreamFrame(frame2); @@ -204,8 +207,7 @@ TEST_P(QuicReceiveControlStreamTest, ReceiveWrongFrame) { QuicByteCount header_length = encoder.SerializeGoAwayFrame(goaway, &buffer); std::string data = std::string(buffer.get(), header_length); - QuicStreamFrame frame(receive_control_stream_->id(), false, 0, - QuicStringPiece(data)); + QuicStreamFrame frame(receive_control_stream_->id(), false, 1, data); EXPECT_CALL(*connection_, CloseConnection(QUIC_HTTP_DECODER_ERROR, _, _)); receive_control_stream_->OnStreamFrame(frame); } @@ -220,12 +222,12 @@ TEST_P(QuicReceiveControlStreamTest, ReceivePriorityFrame) { frame.prioritized_element_id = stream_->id(); frame.weight = 1; std::string serialized_frame = PriorityFrame(frame); - QuicStreamFrame data(receive_control_stream_->id(), false, 0, - QuicStringPiece(serialized_frame)); + QuicStreamFrame data(receive_control_stream_->id(), false, 1, + serialized_frame); - EXPECT_EQ(3u, stream_->priority()); + EXPECT_EQ(3u, stream_->precedence().spdy3_priority()); receive_control_stream_->OnStreamFrame(data); - EXPECT_EQ(1u, stream_->priority()); + EXPECT_EQ(1u, stream_->precedence().spdy3_priority()); } TEST_P(QuicReceiveControlStreamTest, PushPromiseOnControlStreamShouldClose) { @@ -236,7 +238,7 @@ TEST_P(QuicReceiveControlStreamTest, PushPromiseOnControlStreamShouldClose) { HttpEncoder encoder; uint64_t length = encoder.SerializePushPromiseFrameWithOnlyPushId(push_promise, &buffer); - QuicStreamFrame frame(receive_control_stream_->id(), false, 0, buffer.get(), + QuicStreamFrame frame(receive_control_stream_->id(), false, 1, buffer.get(), length); // TODO(lassey) Check for HTTP_WRONG_STREAM error code. EXPECT_CALL(*connection_, CloseConnection(QUIC_HTTP_DECODER_ERROR, _, _)) @@ -251,13 +253,13 @@ TEST_P(QuicReceiveControlStreamTest, ConsumeUnknownFrame) { "03" // payload length "666f6f"); // payload "foo" - EXPECT_EQ(0u, NumBytesConsumed()); + EXPECT_EQ(1u, NumBytesConsumed()); receive_control_stream_->OnStreamFrame( QuicStreamFrame(receive_control_stream_->id(), /* fin = */ false, - /* offset = */ 0, unknown_frame)); + /* offset = */ 1, unknown_frame)); - EXPECT_EQ(unknown_frame.size(), NumBytesConsumed()); + EXPECT_EQ(unknown_frame.size() + 1, NumBytesConsumed()); } } // namespace diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_send_control_stream.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_send_control_stream.cc index 348f63d808c..507380d6c19 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/quic_send_control_stream.cc +++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_send_control_stream.cc @@ -13,9 +13,12 @@ namespace quic { QuicSendControlStream::QuicSendControlStream( QuicStreamId id, QuicSession* session, + uint64_t qpack_maximum_dynamic_table_capacity, uint64_t max_inbound_header_list_size) : QuicStream(id, session, /*is_static = */ true, WRITE_UNIDIRECTIONAL), settings_sent_(false), + qpack_maximum_dynamic_table_capacity_( + qpack_maximum_dynamic_table_capacity), max_inbound_header_list_size_(max_inbound_header_list_size) {} void QuicSendControlStream::OnStreamReset(const QuicRstStreamFrame& /*frame*/) { @@ -26,7 +29,7 @@ void QuicSendControlStream::OnStreamReset(const QuicRstStreamFrame& /*frame*/) { ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); } -void QuicSendControlStream::SendSettingsFrame() { +void QuicSendControlStream::MaybeSendSettingsFrame() { if (settings_sent_) { return; } @@ -40,7 +43,11 @@ void QuicSendControlStream::SendSettingsFrame() { nullptr); SettingsFrame settings; - settings.values[kSettingsMaxHeaderListSize] = max_inbound_header_list_size_; + settings.values[SETTINGS_MAX_HEADER_LIST_SIZE] = + max_inbound_header_list_size_; + settings.values[SETTINGS_QPACK_MAX_TABLE_CAPACITY] = + qpack_maximum_dynamic_table_capacity_; + std::unique_ptr<char[]> buffer; QuicByteCount frame_length = encoder_.SerializeSettingsFrame(settings, &buffer); @@ -53,9 +60,7 @@ void QuicSendControlStream::SendSettingsFrame() { void QuicSendControlStream::WritePriority(const PriorityFrame& priority) { QuicConnection::ScopedPacketFlusher flusher(session()->connection()); - if (!settings_sent_) { - SendSettingsFrame(); - } + MaybeSendSettingsFrame(); std::unique_ptr<char[]> buffer; QuicByteCount frame_length = encoder_.SerializePriorityFrame(priority, &buffer); @@ -64,4 +69,16 @@ void QuicSendControlStream::WritePriority(const PriorityFrame& priority) { nullptr); } +void QuicSendControlStream::SendMaxPushIdFrame(PushId max_push_id) { + QuicConnection::ScopedPacketFlusher flusher(session()->connection()); + + MaybeSendSettingsFrame(); + MaxPushIdFrame frame; + frame.push_id = max_push_id; + std::unique_ptr<char[]> buffer; + QuicByteCount frame_length = encoder_.SerializeMaxPushIdFrame(frame, &buffer); + WriteOrBufferData(QuicStringPiece(buffer.get(), frame_length), + /*fin = */ false, nullptr); +} + } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_send_control_stream.h b/chromium/net/third_party/quiche/src/quic/core/http/quic_send_control_stream.h index 566bcc8f6f1..2e8959b0f4d 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/quic_send_control_stream.h +++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_send_control_stream.h @@ -19,9 +19,10 @@ 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. - explicit QuicSendControlStream(QuicStreamId id, - QuicSession* session, - uint64_t max_inbound_header_list_size); + QuicSendControlStream(QuicStreamId id, + QuicSession* session, + uint64_t qpack_maximum_dynamic_table_capacity, + uint64_t max_inbound_header_list_size); QuicSendControlStream(const QuicSendControlStream&) = delete; QuicSendControlStream& operator=(const QuicSendControlStream&) = delete; ~QuicSendControlStream() override = default; @@ -30,9 +31,12 @@ class QUIC_EXPORT_PRIVATE QuicSendControlStream : public QuicStream { // closed before connection. void OnStreamReset(const QuicRstStreamFrame& frame) override; - // Consult the Spdy session to construct Settings frame and sends it on this - // stream. Settings frame must be the first frame sent on this stream. - void SendSettingsFrame(); + // Send SETTINGS frame if it hasn't been sent yet. Settings frame must be the + // first frame sent on this stream. + void MaybeSendSettingsFrame(); + + // Construct a MAX_PUSH_ID frame and send it on this stream. + void SendMaxPushIdFrame(PushId max_push_id); // Send |Priority| on this stream. It must be sent after settings. void WritePriority(const PriorityFrame& priority); @@ -46,7 +50,9 @@ class QUIC_EXPORT_PRIVATE QuicSendControlStream : public QuicStream { // Track if a settings frame is already sent. bool settings_sent_; - // Max inbound header list size that will send as setting. + // SETTINGS_QPACK_MAX_TABLE_CAPACITY value to send. + const uint64_t qpack_maximum_dynamic_table_capacity_; + // SETTINGS_MAX_HEADER_LIST_SIZE value to send. const uint64_t max_inbound_header_list_size_; }; diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_send_control_stream_test.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_send_control_stream_test.cc index 7c0ee125366..49cc0ede0b3 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/quic_send_control_stream_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_send_control_stream_test.cc @@ -4,6 +4,7 @@ #include "net/third_party/quiche/src/quic/core/http/quic_send_control_stream.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h" #include "net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.h" #include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" @@ -11,6 +12,7 @@ namespace quic { namespace test { namespace { + using ::testing::_; using ::testing::Invoke; using ::testing::StrictMock; @@ -53,14 +55,15 @@ class QuicSendControlStreamTest : public QuicTestWithParam<TestParams> { perspective(), SupportedVersions(GetParam().version))), session_(connection_) { - session_.Initialize(); - send_control_stream_ = QuicMakeUnique<QuicSendControlStream>( - QuicSpdySessionPeer::GetNextOutgoingUnidirectionalStreamId(&session_), - &session_, /* max_inbound_header_list_size = */ 100); ON_CALL(session_, WritevData(_, _, _, _, _)) .WillByDefault(Invoke(MockQuicSession::ConsumeData)); } + void Initialize() { + session_.Initialize(); + send_control_stream_ = QuicSpdySessionPeer::GetSendControlStream(&session_); + } + Perspective perspective() const { return GetParam().perspective; } MockQuicConnectionHelper helper_; @@ -68,27 +71,76 @@ class QuicSendControlStreamTest : public QuicTestWithParam<TestParams> { StrictMock<MockQuicConnection>* connection_; StrictMock<MockQuicSpdySession> session_; HttpEncoder encoder_; - std::unique_ptr<QuicSendControlStream> send_control_stream_; + QuicSendControlStream* send_control_stream_; }; INSTANTIATE_TEST_SUITE_P(Tests, QuicSendControlStreamTest, ::testing::ValuesIn(GetTestParams())); -TEST_P(QuicSendControlStreamTest, WriteSettingsOnlyForOnce) { +TEST_P(QuicSendControlStreamTest, WriteSettings) { if (GetParam().version.handshake_protocol == PROTOCOL_TLS1_3) { // TODO(nharper, b/112643533): Figure out why this test fails when TLS is // enabled and fix it. return; } + + session_.set_qpack_maximum_dynamic_table_capacity(255); + session_.set_max_inbound_header_list_size(1024); + + Initialize(); + testing::InSequence s; + + std::string expected_write_data = QuicTextUtils::HexDecode( + "00" // stream type: control stream + "04" // frame type: SETTINGS frame + "06" // frame length + "01" // SETTINGS_QPACK_MAX_TABLE_CAPACITY + "40ff" // 255 + "06" // SETTINGS_MAX_HEADER_LIST_SIZE + "4400"); // 1024 + + auto buffer = QuicMakeUnique<char[]>(expected_write_data.size()); + QuicDataWriter writer(expected_write_data.size(), buffer.get()); + + // A lambda to save and consume stream data when QuicSession::WritevData() is + // called. + auto save_write_data = [&writer](QuicStream* stream, QuicStreamId /*id*/, + size_t write_length, QuicStreamOffset offset, + StreamSendingState /*state*/) { + stream->WriteStreamData(offset, write_length, &writer); + return QuicConsumedData(/* bytes_consumed = */ write_length, + /* fin_consumed = */ false); + }; + + EXPECT_CALL(session_, WritevData(send_control_stream_, _, 1, _, _)) + .WillOnce(Invoke(save_write_data)); + EXPECT_CALL(session_, WritevData(send_control_stream_, _, + expected_write_data.size() - 1, _, _)) + .WillOnce(Invoke(save_write_data)); + + send_control_stream_->MaybeSendSettingsFrame(); + EXPECT_EQ(expected_write_data, + QuicStringPiece(writer.data(), writer.length())); +} + +TEST_P(QuicSendControlStreamTest, WriteSettingsOnlyOnce) { + if (GetParam().version.handshake_protocol == PROTOCOL_TLS1_3) { + // TODO(nharper, b/112643533): Figure out why this test fails when TLS is + // enabled and fix it. + return; + } + + Initialize(); testing::InSequence s; - EXPECT_CALL(session_, WritevData(_, _, 1, _, _)); - EXPECT_CALL(session_, WritevData(_, _, _, _, _)); - send_control_stream_->SendSettingsFrame(); + EXPECT_CALL(session_, WritevData(send_control_stream_, _, 1, _, _)); + EXPECT_CALL(session_, WritevData(send_control_stream_, _, _, _, _)); + send_control_stream_->MaybeSendSettingsFrame(); - // No data should be written the sencond time SendSettingsFrame() is called. - send_control_stream_->SendSettingsFrame(); + // No data should be written the sencond time MaybeSendSettingsFrame() is + // called. + send_control_stream_->MaybeSendSettingsFrame(); } TEST_P(QuicSendControlStreamTest, WritePriorityBeforeSettings) { @@ -98,20 +150,21 @@ TEST_P(QuicSendControlStreamTest, WritePriorityBeforeSettings) { return; } + Initialize(); testing::InSequence s; // The first write will trigger the control stream to write stream type and a // Settings frame before the Priority frame. - EXPECT_CALL(session_, WritevData(_, send_control_stream_->id(), _, _, _)) - .Times(3); + EXPECT_CALL(session_, WritevData(send_control_stream_, _, _, _, _)).Times(3); PriorityFrame frame; send_control_stream_->WritePriority(frame); - EXPECT_CALL(session_, WritevData(_, send_control_stream_->id(), _, _, _)); + EXPECT_CALL(session_, WritevData(send_control_stream_, _, _, _, _)); send_control_stream_->WritePriority(frame); } TEST_P(QuicSendControlStreamTest, ResetControlStream) { + Initialize(); QuicRstStreamFrame rst_frame(kInvalidControlFrameId, send_control_stream_->id(), QUIC_STREAM_CANCELLED, 1234); @@ -120,6 +173,7 @@ TEST_P(QuicSendControlStreamTest, ResetControlStream) { } TEST_P(QuicSendControlStreamTest, ReceiveDataOnSendControlStream) { + Initialize(); QuicStreamFrame frame(send_control_stream_->id(), false, 0, "test"); EXPECT_CALL( *connection_, diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_server_session_base_test.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_server_session_base_test.cc index 0041c85baf6..2236d42dfb9 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/quic_server_session_base_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_server_session_base_test.cc @@ -474,6 +474,11 @@ class MockQuicCryptoServerStream : public QuicCryptoServerStream { }; TEST_P(QuicServerSessionBaseTest, BandwidthEstimates) { + if (GetParam().handshake_protocol == PROTOCOL_TLS1_3) { + // TODO(nharper, b/112643533): Figure out why this test fails when TLS is + // enabled and fix it. + return; + } // Test that bandwidth estimate updates are sent to the client, only when // bandwidth resumption is enabled, the bandwidth estimate has changed // sufficiently, enough time has passed, @@ -493,17 +498,22 @@ TEST_P(QuicServerSessionBaseTest, BandwidthEstimates) { const std::string serving_region = "not a real region"; session_->set_serving_region(serving_region); - session_->UnregisterStreamPriority( - QuicUtils::GetHeadersStreamId(connection_->transport_version()), - /*is_static=*/true); + if (!VersionUsesQpack(transport_version())) { + session_->UnregisterStreamPriority( + QuicUtils::GetHeadersStreamId(connection_->transport_version()), + /*is_static=*/true); + } QuicServerSessionBasePeer::SetCryptoStream(session_.get(), nullptr); MockQuicCryptoServerStream* crypto_stream = new MockQuicCryptoServerStream(&crypto_config_, &compressed_certs_cache_, session_.get(), &stream_helper_); QuicServerSessionBasePeer::SetCryptoStream(session_.get(), crypto_stream); - session_->RegisterStreamPriority( - QuicUtils::GetHeadersStreamId(connection_->transport_version()), - /*is_static=*/true, QuicStream::kDefaultPriority); + if (!VersionUsesQpack(transport_version())) { + session_->RegisterStreamPriority( + QuicUtils::GetHeadersStreamId(connection_->transport_version()), + /*is_static=*/true, + spdy::SpdyStreamPrecedence(QuicStream::kDefaultPriority)); + } // Set some initial bandwidth values. QuicSentPacketManager* sent_packet_manager = @@ -521,8 +531,14 @@ TEST_P(QuicServerSessionBaseTest, BandwidthEstimates) { &bandwidth_recorder, max_bandwidth_estimate_kbytes_per_second, max_bandwidth_estimate_timestamp); // Queue up some pending data. - session_->MarkConnectionLevelWriteBlocked( - QuicUtils::GetHeadersStreamId(connection_->transport_version())); + if (!VersionUsesQpack(transport_version())) { + session_->MarkConnectionLevelWriteBlocked( + QuicUtils::GetHeadersStreamId(connection_->transport_version())); + } else { + session_->MarkConnectionLevelWriteBlocked( + QuicUtils::GetFirstUnidirectionalStreamId( + connection_->transport_version(), Perspective::IS_SERVER)); + } EXPECT_TRUE(session_->HasDataToWrite()); // There will be no update sent yet - not enough time has passed. diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session_base.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session_base.cc index f27411aec34..a2a9aaebd87 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session_base.cc +++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session_base.cc @@ -24,8 +24,7 @@ QuicSpdyClientSessionBase::QuicSpdyClientSessionBase( : QuicSpdySession(connection, nullptr, config, supported_versions), push_promise_index_(push_promise_index), largest_promised_stream_id_( - QuicUtils::GetInvalidStreamId(connection->transport_version())), - max_allowed_push_id_(0) {} + QuicUtils::GetInvalidStreamId(connection->transport_version())) {} QuicSpdyClientSessionBase::~QuicSpdyClientSessionBase() { // all promised streams for this session @@ -43,6 +42,10 @@ void QuicSpdyClientSessionBase::OnConfigNegotiated() { void QuicSpdyClientSessionBase::OnCryptoHandshakeEvent( CryptoHandshakeEvent event) { QuicSpdySession::OnCryptoHandshakeEvent(event); + if (event == HANDSHAKE_CONFIRMED && max_allowed_push_id() > 0 && + VersionHasIetfQuicFrames(connection()->transport_version())) { + SendMaxPushId(max_allowed_push_id()); + } } void QuicSpdyClientSessionBase::OnInitialHeadersComplete( @@ -96,6 +99,7 @@ void QuicSpdyClientSessionBase::OnPromiseHeaderList( QUIC_INVALID_STREAM_ID, "Received push stream id higher than MAX_PUSH_ID.", ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return; } largest_promised_stream_id_ = promised_stream_id; @@ -192,8 +196,12 @@ void QuicSpdyClientSessionBase::DeletePromised( push_promise_index_->promised_by_url()->erase(promised->url()); // Since promised_by_id_ contains the unique_ptr, this will destroy // promised. + // ToDo: Consider implementing logic to send a new MAX_PUSH_ID frame to allow + // another stream to be promised. promised_by_id_.erase(promised->id()); - headers_stream()->MaybeReleaseSequencerBuffer(); + if (!VersionUsesQpack(connection()->transport_version())) { + headers_stream()->MaybeReleaseSequencerBuffer(); + } } void QuicSpdyClientSessionBase::OnPushStreamTimedOut( @@ -211,16 +219,13 @@ void QuicSpdyClientSessionBase::ResetPromised( void QuicSpdyClientSessionBase::CloseStreamInner(QuicStreamId stream_id, bool locally_reset) { QuicSpdySession::CloseStreamInner(stream_id, locally_reset); - headers_stream()->MaybeReleaseSequencerBuffer(); + if (!VersionUsesQpack(connection()->transport_version())) { + headers_stream()->MaybeReleaseSequencerBuffer(); + } } bool QuicSpdyClientSessionBase::ShouldReleaseHeadersStreamSequencerBuffer() { return !HasActiveRequestStreams() && promised_by_id_.empty(); } -void QuicSpdyClientSessionBase::set_max_allowed_push_id( - QuicStreamId max_allowed_push_id) { - max_allowed_push_id_ = max_allowed_push_id; -} - } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session_base.h b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session_base.h index 98b85899c40..aec5e75947f 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session_base.h +++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session_base.h @@ -111,10 +111,6 @@ class QUIC_EXPORT_PRIVATE QuicSpdyClientSessionBase // Returns true if there are no active requests and no promised streams. bool ShouldReleaseHeadersStreamSequencerBuffer() override; - void set_max_allowed_push_id(QuicStreamId max_allowed_push_id); - - QuicStreamId max_allowed_push_id() { return max_allowed_push_id_; } - size_t get_max_promises() const { return max_open_incoming_unidirectional_streams() * kMaxPromisedStreamsMultiplier; @@ -138,7 +134,6 @@ class QUIC_EXPORT_PRIVATE QuicSpdyClientSessionBase QuicClientPushPromiseIndex* push_promise_index_; QuicPromisedByIdMap promised_by_id_; QuicStreamId largest_promised_stream_id_; - QuicStreamId max_allowed_push_id_; }; } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session_test.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session_test.cc index 070acdfa6e3..3601873df57 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session_test.cc @@ -31,6 +31,7 @@ using spdy::SpdyHeaderBlock; using testing::_; using testing::AnyNumber; +using testing::AtMost; using testing::Invoke; using testing::Truly; @@ -155,7 +156,8 @@ class QuicSpdyClientSessionTest : public QuicTestWithParam<ParsedQuicVersion> { QuicConfig config = DefaultQuicConfig(); if (VersionHasIetfQuicFrames(connection_->transport_version())) { config.SetMaxIncomingUnidirectionalStreamsToSend( - server_max_incoming_streams); + server_max_incoming_streams + + session_->num_expected_unidirectional_static_streams()); config.SetMaxIncomingBidirectionalStreamsToSend( server_max_incoming_streams); } else { @@ -243,9 +245,6 @@ TEST_P(QuicSpdyClientSessionTest, MaxNumStreamsWithNoFinOrRst) { EXPECT_CALL(*connection_, OnStreamReset(_, _)).Times(AnyNumber()); uint32_t kServerMaxIncomingStreams = 1; - if (VersionLacksHeadersStream(GetParam().transport_version)) { - kServerMaxIncomingStreams = 0; - } CompleteCryptoHandshake(kServerMaxIncomingStreams); QuicSpdyClientStream* stream = session_->CreateOutgoingBidirectionalStream(); @@ -261,22 +260,9 @@ TEST_P(QuicSpdyClientSessionTest, MaxNumStreamsWithNoFinOrRst) { EXPECT_FALSE(stream); if (VersionHasIetfQuicFrames(GetParam().transport_version)) { - if (VersionLacksHeadersStream(GetParam().transport_version)) { - EXPECT_EQ(1u, - QuicSessionPeer::v99_bidirectional_stream_id_manager(&*session_) - ->outgoing_stream_count()); - - } else { - // Ensure that we have/have had 3 open streams, crypto, header, and the - // 1 test stream. Primary purpose of this is to fail when crypto - // no longer uses a normal stream. Some above constants will then need - // to be changed. - EXPECT_EQ(QuicSessionPeer::v99_bidirectional_stream_id_manager(&*session_) - ->outgoing_static_stream_count() + - 1, - QuicSessionPeer::v99_bidirectional_stream_id_manager(&*session_) - ->outgoing_stream_count()); - } + EXPECT_EQ(1u, + QuicSessionPeer::v99_bidirectional_stream_id_manager(&*session_) + ->outgoing_stream_count()); } } @@ -291,9 +277,6 @@ TEST_P(QuicSpdyClientSessionTest, MaxNumStreamsWithRst) { EXPECT_CALL(*connection_, OnStreamReset(_, _)).Times(AnyNumber()); uint32_t kServerMaxIncomingStreams = 1; - if (VersionLacksHeadersStream(GetParam().transport_version)) { - kServerMaxIncomingStreams = 0; - } CompleteCryptoHandshake(kServerMaxIncomingStreams); QuicSpdyClientStream* stream = session_->CreateOutgoingBidirectionalStream(); @@ -310,26 +293,15 @@ TEST_P(QuicSpdyClientSessionTest, MaxNumStreamsWithRst) { // In V99 the stream limit increases only if we get a MAX_STREAMS // frame; pretend we got one. - // Note that this is to be the second stream created, hence - // the stream count is 3 (the two streams created as a part of - // the test plus the header stream, internally created). - QuicMaxStreamsFrame frame( - 0, - QuicSessionPeer::v99_bidirectional_stream_id_manager(&*session_) - ->outgoing_static_stream_count() + - 2, - /*unidirectional=*/false); + QuicMaxStreamsFrame frame(0, 2, + /*unidirectional=*/false); session_->OnMaxStreamsFrame(frame); } stream = session_->CreateOutgoingBidirectionalStream(); EXPECT_NE(nullptr, stream); if (VersionHasIetfQuicFrames(GetParam().transport_version)) { - // Ensure that we have/have had three open streams: two test streams and the - // header stream. - QuicStreamCount expected_stream_count = 3; - if (VersionLacksHeadersStream(GetParam().transport_version)) { - expected_stream_count = 2; - } + // Ensure that we have 2 total streams, 1 open and 1 closed. + QuicStreamCount expected_stream_count = 2; EXPECT_EQ(expected_stream_count, QuicSessionPeer::v99_bidirectional_stream_id_manager(&*session_) ->outgoing_stream_count()); @@ -348,9 +320,6 @@ TEST_P(QuicSpdyClientSessionTest, ResetAndTrailers) { // the client should result in all outstanding stream state being tidied up // (including flow control, and number of available outgoing streams). uint32_t kServerMaxIncomingStreams = 1; - if (VersionLacksHeadersStream(GetParam().transport_version)) { - kServerMaxIncomingStreams = 0; - } CompleteCryptoHandshake(kServerMaxIncomingStreams); QuicSpdyClientStream* stream = session_->CreateOutgoingBidirectionalStream(); @@ -396,27 +365,16 @@ TEST_P(QuicSpdyClientSessionTest, ResetAndTrailers) { // be able to create a new outgoing stream. EXPECT_EQ(0u, session_->GetNumOpenOutgoingStreams()); if (VersionHasIetfQuicFrames(GetParam().transport_version)) { - // Note that this is to be the second stream created, hence - // the stream count is 3 (the two streams created as a part of - // the test plus the header stream, internally created). - QuicMaxStreamsFrame frame( - 0, - QuicSessionPeer::v99_bidirectional_stream_id_manager(&*session_) - ->outgoing_static_stream_count() + - 2, - /*unidirectional=*/false); + QuicMaxStreamsFrame frame(0, 2, + /*unidirectional=*/false); session_->OnMaxStreamsFrame(frame); } stream = session_->CreateOutgoingBidirectionalStream(); EXPECT_NE(nullptr, stream); if (VersionHasIetfQuicFrames(GetParam().transport_version)) { - // Ensure that we have/have had three open streams: two test streams and the - // header stream. - QuicStreamCount expected_stream_count = 3; - if (VersionLacksHeadersStream(GetParam().transport_version)) { - expected_stream_count = 2; - } + // Ensure that we have 2 open streams. + QuicStreamCount expected_stream_count = 2; EXPECT_EQ(expected_stream_count, QuicSessionPeer::v99_bidirectional_stream_id_manager(&*session_) ->outgoing_stream_count()); @@ -458,10 +416,24 @@ TEST_P(QuicSpdyClientSessionTest, OnStreamHeaderListWithStaticStream) { trailers.OnHeader(kFinalOffsetHeaderKey, "0"); trailers.OnHeaderBlockEnd(0, 0); - EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(1); - session_->OnStreamHeaderList( - QuicUtils::GetHeadersStreamId(connection_->transport_version()), - /*fin=*/false, 0, trailers); + // Initialize H/3 control stream. + QuicStreamId id; + if (VersionUsesQpack(connection_->transport_version())) { + id = GetNthServerInitiatedUnidirectionalStreamId( + connection_->transport_version(), 3); + char type[] = {0x00}; + + QuicStreamFrame data1(id, false, 0, QuicStringPiece(type, 1)); + session_->OnStreamFrame(data1); + } else { + id = QuicUtils::GetHeadersStreamId(connection_->transport_version()); + } + + EXPECT_CALL(*connection_, CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA, + "stream is static", _)) + .Times(1); + session_->OnStreamHeaderList(id, + /*fin=*/false, 0, trailers); } TEST_P(QuicSpdyClientSessionTest, OnPromiseHeaderListWithStaticStream) { @@ -474,10 +446,22 @@ TEST_P(QuicSpdyClientSessionTest, OnPromiseHeaderListWithStaticStream) { trailers.OnHeader(kFinalOffsetHeaderKey, "0"); trailers.OnHeaderBlockEnd(0, 0); - EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(1); - session_->OnPromiseHeaderList( - QuicUtils::GetHeadersStreamId(connection_->transport_version()), - promised_stream_id_, 0, trailers); + // Initialize H/3 control stream. + QuicStreamId id; + if (VersionUsesQpack(connection_->transport_version())) { + id = GetNthServerInitiatedUnidirectionalStreamId( + connection_->transport_version(), 3); + char type[] = {0x00}; + + QuicStreamFrame data1(id, false, 0, QuicStringPiece(type, 1)); + session_->OnStreamFrame(data1); + } else { + id = QuicUtils::GetHeadersStreamId(connection_->transport_version()); + } + EXPECT_CALL(*connection_, CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA, + "stream is static", _)) + .Times(1); + session_->OnPromiseHeaderList(id, promised_stream_id_, 0, trailers); } TEST_P(QuicSpdyClientSessionTest, GoAwayReceived) { @@ -521,10 +505,7 @@ TEST_P(QuicSpdyClientSessionTest, InvalidPacketReceived) { QuicReceivedPacket valid_packet(buf, 2, QuicTime::Zero(), false); // Close connection shouldn't be called. EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(0); - if (connection_->transport_version() > QUIC_VERSION_44) { - // Illegal fixed bit value. - EXPECT_CALL(*connection_, OnError(_)).Times(1); - } + EXPECT_CALL(*connection_, OnError(_)).Times(AtMost(1)); session_->ProcessUdpPacket(client_address, server_address, valid_packet); // Verify that a non-decryptable packet doesn't close the connection. @@ -533,10 +514,6 @@ TEST_P(QuicSpdyClientSessionTest, InvalidPacketReceived) { ParsedQuicVersionVector versions = SupportedVersions(GetParam()); QuicConnectionId destination_connection_id = EmptyQuicConnectionId(); QuicConnectionId source_connection_id = connection_id; - if (!GetQuicRestartFlag(quic_do_not_override_connection_id)) { - destination_connection_id = connection_id; - source_connection_id = EmptyQuicConnectionId(); - } std::unique_ptr<QuicEncryptedPacket> packet(ConstructEncryptedPacket( destination_connection_id, source_connection_id, false, false, 100, "data", CONNECTION_ID_ABSENT, CONNECTION_ID_ABSENT, @@ -602,6 +579,9 @@ TEST_P(QuicSpdyClientSessionTest, PushPromiseOnPromiseHeaders) { // Initialize crypto before the client session will create a stream. CompleteCryptoHandshake(); + session_->set_max_allowed_push_id(GetNthServerInitiatedUnidirectionalStreamId( + connection_->transport_version(), 10)); + MockQuicSpdyClientStream* stream = static_cast<MockQuicSpdyClientStream*>( session_->CreateOutgoingBidirectionalStream()); @@ -661,6 +641,9 @@ TEST_P(QuicSpdyClientSessionTest, PushPromiseOutOfOrder) { // Initialize crypto before the client session will create a stream. CompleteCryptoHandshake(); + session_->set_max_allowed_push_id(GetNthServerInitiatedUnidirectionalStreamId( + connection_->transport_version(), 10)); + MockQuicSpdyClientStream* stream = static_cast<MockQuicSpdyClientStream*>( session_->CreateOutgoingBidirectionalStream()); diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_session.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_session.cc index e306062ba75..719b3dcb7a3 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_session.cc +++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_session.cc @@ -12,12 +12,14 @@ #include "net/third_party/quiche/src/quic/core/http/http_constants.h" #include "net/third_party/quiche/src/quic/core/http/quic_headers_stream.h" #include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/core/quic_versions.h" #include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" #include "net/third_party/quiche/src/quic/platform/api/quic_fallthrough.h" #include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h" #include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" #include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_stack_trace.h" #include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h" #include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h" #include "net/third_party/quiche/src/spdy/core/http2_frame_decoder_adapter.h" @@ -26,9 +28,6 @@ using http2::Http2DecoderAdapter; using spdy::HpackEntry; using spdy::HpackHeaderTable; using spdy::Http2WeightToSpdy3Priority; -using spdy::SETTINGS_ENABLE_PUSH; -using spdy::SETTINGS_HEADER_TABLE_SIZE; -using spdy::SETTINGS_MAX_HEADER_LIST_SIZE; using spdy::Spdy3PriorityToHttp2Weight; using spdy::SpdyErrorCode; using spdy::SpdyFramer; @@ -52,7 +51,7 @@ namespace quic { namespace { -// TODO(renjietang): remove this once HTTP/3 error codes are adopted. +// TODO(b/124216424): remove this once HTTP/3 error codes are adopted. const uint16_t kHttpUnknownStreamType = 0x0D; class HeaderTableDebugVisitor : public HpackHeaderTable::DebugVisitorInterface { @@ -97,10 +96,12 @@ class QuicSpdySession::SpdyFramerVisitor SpdyHeadersHandlerInterface* OnHeaderFrameStart( SpdyStreamId /* stream_id */) override { + DCHECK(!VersionUsesQpack(session_->transport_version())); return &header_list_; } void OnHeaderFrameEnd(SpdyStreamId /* stream_id */) override { + DCHECK(!VersionUsesQpack(session_->transport_version())); if (session_->IsConnected()) { session_->OnHeaderList(header_list_); } @@ -110,6 +111,7 @@ class QuicSpdySession::SpdyFramerVisitor void OnStreamFrameData(SpdyStreamId /*stream_id*/, const char* /*data*/, size_t /*len*/) override { + DCHECK(!VersionUsesQpack(session_->transport_version())); CloseConnection("SPDY DATA frame received.", QUIC_INVALID_HEADERS_STREAM_DATA); } @@ -142,6 +144,7 @@ class QuicSpdySession::SpdyFramerVisitor void OnDataFrameHeader(SpdyStreamId /*stream_id*/, size_t /*length*/, bool /*fin*/) override { + DCHECK(!VersionUsesQpack(session_->transport_version())); CloseConnection("SPDY DATA frame received.", QUIC_INVALID_HEADERS_STREAM_DATA); } @@ -153,39 +156,13 @@ class QuicSpdySession::SpdyFramerVisitor } void OnSetting(SpdySettingsId id, uint32_t value) override { - switch (id) { - case SETTINGS_HEADER_TABLE_SIZE: - session_->UpdateHeaderEncoderTableSize(value); - break; - case SETTINGS_ENABLE_PUSH: - if (session_->perspective() == Perspective::IS_SERVER) { - // See rfc7540, Section 6.5.2. - if (value > 1) { - CloseConnection( - QuicStrCat("Invalid value for SETTINGS_ENABLE_PUSH: ", value), - QUIC_INVALID_HEADERS_STREAM_DATA); - return; - } - session_->UpdateEnableServerPush(value > 0); - break; - } else { - CloseConnection( - QuicStrCat("Unsupported field of HTTP/2 SETTINGS frame: ", id), - QUIC_INVALID_HEADERS_STREAM_DATA); - } - break; - // TODO(fayang): Need to support SETTINGS_MAX_HEADER_LIST_SIZE when - // clients are actually sending it. - case SETTINGS_MAX_HEADER_LIST_SIZE: - break; - default: - CloseConnection( - QuicStrCat("Unsupported field of HTTP/2 SETTINGS frame: ", id), - QUIC_INVALID_HEADERS_STREAM_DATA); - } + DCHECK(!VersionUsesQpack(session_->transport_version())); + session_->OnSetting(id, value); } - void OnSettingsEnd() override {} + void OnSettingsEnd() override { + DCHECK(!VersionUsesQpack(session_->transport_version())); + } void OnPing(SpdyPingId /*unique_id*/, bool /*is_ack*/) override { CloseConnection("SPDY PING frame received.", @@ -201,8 +178,8 @@ class QuicSpdySession::SpdyFramerVisitor void OnHeaders(SpdyStreamId stream_id, bool has_priority, int weight, - SpdyStreamId /*parent_stream_id*/, - bool /*exclusive*/, + SpdyStreamId parent_stream_id, + bool exclusive, bool fin, bool /*end*/) override { if (!session_->IsConnected()) { @@ -215,11 +192,23 @@ class QuicSpdySession::SpdyFramerVisitor return; } - // TODO(mpw): avoid down-conversion and plumb SpdyStreamPrecedence through - // QuicHeadersStream. + QUIC_BUG_IF(session_->destruction_indicator() != 123456789) + << "QuicSpdyStream use after free. " + << session_->destruction_indicator() << QuicStackTrace(); + + if (session_->use_http2_priority_write_scheduler()) { + session_->OnHeaders( + stream_id, has_priority, + spdy::SpdyStreamPrecedence(parent_stream_id, weight, exclusive), fin); + QUIC_RELOADABLE_FLAG_COUNT_N(quic_use_http2_priority_write_scheduler, 1, + 3); + return; + } + SpdyPriority priority = has_priority ? Http2WeightToSpdy3Priority(weight) : 0; - session_->OnHeaders(stream_id, has_priority, priority, fin); + session_->OnHeaders(stream_id, has_priority, + spdy::SpdyStreamPrecedence(priority), fin); } void OnWindowUpdate(SpdyStreamId /*stream_id*/, @@ -231,6 +220,7 @@ class QuicSpdySession::SpdyFramerVisitor void OnPushPromise(SpdyStreamId stream_id, SpdyStreamId promised_stream_id, bool /*end*/) override { + DCHECK(!VersionUsesQpack(session_->transport_version())); if (!session_->supports_push_promise()) { CloseConnection("PUSH_PROMISE not supported.", QUIC_INVALID_HEADERS_STREAM_DATA); @@ -245,9 +235,10 @@ class QuicSpdySession::SpdyFramerVisitor void OnContinuation(SpdyStreamId /*stream_id*/, bool /*end*/) override {} void OnPriority(SpdyStreamId stream_id, - SpdyStreamId /*parent_id*/, + SpdyStreamId parent_id, int weight, - bool /*exclusive*/) override { + bool exclusive) override { + DCHECK(!VersionUsesQpack(session_->transport_version())); if (session_->connection()->transport_version() <= QUIC_VERSION_39) { CloseConnection("SPDY PRIORITY frame received.", QUIC_INVALID_HEADERS_STREAM_DATA); @@ -256,10 +247,15 @@ class QuicSpdySession::SpdyFramerVisitor if (!session_->IsConnected()) { return; } - // TODO (wangyix): implement real HTTP/2 weights and dependencies instead of - // converting to SpdyPriority. + if (session_->use_http2_priority_write_scheduler()) { + session_->OnPriority( + stream_id, spdy::SpdyStreamPrecedence(parent_id, weight, exclusive)); + QUIC_RELOADABLE_FLAG_COUNT_N(quic_use_http2_priority_write_scheduler, 2, + 3); + return; + } SpdyPriority priority = Http2WeightToSpdy3Priority(weight); - session_->OnPriority(stream_id, priority); + session_->OnPriority(stream_id, spdy::SpdyStreamPrecedence(priority)); } bool OnUnknownFrame(SpdyStreamId /*stream_id*/, @@ -315,7 +311,21 @@ QuicSpdySession::QuicSpdySession( QuicSession::Visitor* visitor, const QuicConfig& config, const ParsedQuicVersionVector& supported_versions) - : QuicSession(connection, visitor, config, supported_versions), + : QuicSession(connection, + visitor, + config, + supported_versions, + /*num_expected_unidirectional_static_streams = */ + VersionUsesQpack(connection->transport_version()) ? 3 : 0), + send_control_stream_(nullptr), + receive_control_stream_(nullptr), + qpack_encoder_receive_stream_(nullptr), + qpack_decoder_receive_stream_(nullptr), + qpack_encoder_send_stream_(nullptr), + qpack_decoder_send_stream_(nullptr), + qpack_maximum_dynamic_table_capacity_( + kDefaultQpackMaxDynamicTableCapacity), + qpack_maximum_blocked_streams_(kDefaultMaximumBlockedStreams), max_inbound_header_list_size_(kDefaultMaxUncompressedHeaderSize), max_outbound_header_list_size_(kDefaultMaxUncompressedHeaderSize), server_push_enabled_(true), @@ -325,16 +335,20 @@ QuicSpdySession::QuicSpdySession( QuicUtils::GetInvalidStreamId(connection->transport_version())), fin_(false), frame_len_(0), - uncompressed_frame_len_(0), supports_push_promise_(perspective() == Perspective::IS_CLIENT), spdy_framer_(SpdyFramer::ENABLE_COMPRESSION), - spdy_framer_visitor_(new SpdyFramerVisitor(this)) { + spdy_framer_visitor_(new SpdyFramerVisitor(this)), + max_allowed_push_id_(0), + destruction_indicator_(123456789) { h2_deframer_.set_visitor(spdy_framer_visitor_.get()); h2_deframer_.set_debug_visitor(spdy_framer_visitor_.get()); spdy_framer_.set_debug_visitor(spdy_framer_visitor_.get()); } QuicSpdySession::~QuicSpdySession() { + QUIC_BUG_IF(destruction_indicator_ != 123456789) + << "QuicSpdyStream use after free. " << destruction_indicator_ + << QuicStackTrace(); // Set the streams' session pointers in closed and dynamic stream lists // to null to avoid subsequent use of this session. for (auto& stream : *closed_streams()) { @@ -348,12 +362,13 @@ QuicSpdySession::~QuicSpdySession() { static_cast<QuicSpdyStream*>(kv.second.get())->ClearSession(); } } + destruction_indicator_ = 987654321; } void QuicSpdySession::Initialize() { QuicSession::Initialize(); - if (!connection()->version().DoesNotHaveHeadersStream()) { + if (!VersionUsesQpack(connection()->transport_version())) { if (perspective() == Perspective::IS_SERVER) { set_largest_peer_created_stream_id( QuicUtils::GetHeadersStreamId(connection()->transport_version())); @@ -362,34 +377,22 @@ void QuicSpdySession::Initialize() { DCHECK_EQ(headers_stream_id, QuicUtils::GetHeadersStreamId( connection()->transport_version())); } - } + auto headers_stream = QuicMakeUnique<QuicHeadersStream>((this)); + DCHECK_EQ(QuicUtils::GetHeadersStreamId(connection()->transport_version()), + headers_stream->id()); - if (VersionUsesQpack(connection()->transport_version())) { - qpack_encoder_ = - QuicMakeUnique<QpackEncoder>(this, &encoder_stream_sender_delegate_); + headers_stream_ = headers_stream.get(); + RegisterStaticStream(std::move(headers_stream)); + } else { + ConfigureMaxIncomingDynamicStreamsToSend( + config()->GetMaxIncomingUnidirectionalStreamsToSend()); + qpack_encoder_ = QuicMakeUnique<QpackEncoder>(this); qpack_decoder_ = - QuicMakeUnique<QpackDecoder>(this, &decoder_stream_sender_delegate_); - // TODO(b/112770235): Send SETTINGS_QPACK_MAX_TABLE_CAPACITY with value - // kDefaultQpackMaxDynamicTableCapacity. - qpack_decoder_->SetMaximumDynamicTableCapacity( - kDefaultQpackMaxDynamicTableCapacity); - } - - auto headers_stream = QuicMakeUnique<QuicHeadersStream>((this)); - DCHECK_EQ(QuicUtils::GetHeadersStreamId(connection()->transport_version()), - headers_stream->id()); - - headers_stream_ = headers_stream.get(); - RegisterStaticStream(std::move(headers_stream), - /*stream_already_counted = */ false); - - if (VersionHasStreamType(connection()->transport_version())) { - auto send_control = QuicMakeUnique<QuicSendControlStream>( - GetNextOutgoingUnidirectionalStreamId(), this, - max_inbound_header_list_size_); - send_control_stream_ = send_control.get(); - RegisterStaticStream(std::move(send_control), - /*stream_already_counted = */ false); + QuicMakeUnique<QpackDecoder>(qpack_maximum_dynamic_table_capacity_, + qpack_maximum_blocked_streams_, this); + MaybeInitializeHttp3UnidirectionalStreams(); + // TODO(b/112770235): Send SETTINGS_QPACK_BLOCKED_STREAMS with value + // qpack_maximum_blocked_streams_. } spdy_framer_visitor_->set_max_header_list_size(max_inbound_header_list_size_); @@ -413,14 +416,15 @@ void QuicSpdySession::OnEncoderStreamError(QuicStringPiece /*error_message*/) { QUIC_NOTREACHED(); } -void QuicSpdySession::OnStreamHeadersPriority(QuicStreamId stream_id, - SpdyPriority priority) { +void QuicSpdySession::OnStreamHeadersPriority( + QuicStreamId stream_id, + const spdy::SpdyStreamPrecedence& precedence) { QuicSpdyStream* stream = GetSpdyDataStream(stream_id); if (!stream) { // It's quite possible to receive headers after a stream has been reset. return; } - stream->OnStreamHeadersPriority(priority); + stream->OnStreamHeadersPriority(precedence); } void QuicSpdySession::OnStreamHeaderList(QuicStreamId stream_id, @@ -461,18 +465,22 @@ void QuicSpdySession::OnStreamHeaderList(QuicStreamId stream_id, stream->OnStreamHeaderList(fin, frame_len, header_list); } -void QuicSpdySession::OnPriorityFrame(QuicStreamId stream_id, - SpdyPriority priority) { +void QuicSpdySession::OnPriorityFrame( + QuicStreamId stream_id, + const spdy::SpdyStreamPrecedence& precedence) { QuicSpdyStream* stream = GetSpdyDataStream(stream_id); if (!stream) { // It's quite possible to receive a PRIORITY frame after a stream has been // reset. return; } - stream->OnPriorityFrame(priority); + stream->OnPriorityFrame(precedence); } size_t QuicSpdySession::ProcessHeaderData(const struct iovec& iov) { + QUIC_BUG_IF(destruction_indicator_ != 123456789) + << "QuicSpdyStream use after free. " << destruction_indicator_ + << QuicStackTrace(); return h2_deframer_.ProcessInput(static_cast<char*>(iov.iov_base), iov.iov_len); } @@ -481,13 +489,14 @@ size_t QuicSpdySession::WriteHeadersOnHeadersStream( QuicStreamId id, SpdyHeaderBlock headers, bool fin, - SpdyPriority priority, + const spdy::SpdyStreamPrecedence& precedence, QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) { DCHECK(!VersionUsesQpack(connection()->transport_version())); return WriteHeadersOnHeadersStreamImpl( id, std::move(headers), fin, - /* parent_stream_id = */ 0, Spdy3PriorityToHttp2Weight(priority), + /* parent_stream_id = */ 0, + Spdy3PriorityToHttp2Weight(precedence.spdy3_priority()), /* exclusive = */ false, std::move(ack_listener)); } @@ -495,6 +504,7 @@ size_t QuicSpdySession::WritePriority(QuicStreamId id, QuicStreamId parent_stream_id, int weight, bool exclusive) { + DCHECK(!VersionUsesQpack(connection()->transport_version())); if (connection()->transport_version() <= QUIC_VERSION_39) { return 0; } @@ -510,6 +520,8 @@ void QuicSpdySession::WriteH3Priority(const PriorityFrame& priority) { DCHECK(perspective() == Perspective::IS_CLIENT) << "Server must not send priority"; + QuicConnection::ScopedPacketFlusher flusher(connection()); + SendMaxHeaderListSize(max_inbound_header_list_size_); send_control_stream_->WritePriority(priority); } @@ -521,22 +533,51 @@ void QuicSpdySession::WritePushPromise(QuicStreamId original_stream_id, return; } - SpdyPushPromiseIR push_promise(original_stream_id, promised_stream_id, - std::move(headers)); - // PUSH_PROMISE must not be the last frame sent out, at least followed by - // response headers. - push_promise.set_fin(false); + if (VersionHasIetfQuicFrames(connection()->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 (!VersionHasStreamType(connection()->transport_version())) { + SpdyPushPromiseIR push_promise(original_stream_id, promised_stream_id, + std::move(headers)); + // PUSH_PROMISE must not be the last frame sent out, at least followed by + // response headers. + push_promise.set_fin(false); - SpdySerializedFrame frame(spdy_framer_.SerializeFrame(push_promise)); - headers_stream()->WriteOrBufferData( - QuicStringPiece(frame.data(), frame.size()), false, nullptr); + SpdySerializedFrame frame(spdy_framer_.SerializeFrame(push_promise)); + headers_stream()->WriteOrBufferData( + QuicStringPiece(frame.data(), frame.size()), false, nullptr); + return; + } + + // Encode header list. + std::string encoded_headers = + qpack_encoder_->EncodeHeaderList(original_stream_id, headers); + PushPromiseFrame frame; + frame.push_id = promised_stream_id; + frame.headers = encoded_headers; + QuicSpdyStream* stream = GetSpdyDataStream(original_stream_id); + stream->WritePushPromise(frame); } void QuicSpdySession::SendMaxHeaderListSize(size_t value) { if (VersionHasStreamType(connection()->transport_version())) { - send_control_stream_->SendSettingsFrame(); + QuicConnection::ScopedPacketFlusher flusher(connection()); + send_control_stream_->MaybeSendSettingsFrame(); + // TODO(renjietang): Remove this once stream id manager can take dynamically + // created HTTP/3 unidirectional streams. + qpack_encoder_send_stream_->MaybeSendStreamType(); + qpack_decoder_send_stream_->MaybeSendStreamType(); return; } + if (GetQuicReloadableFlag(quic_do_not_send_settings)) { + QUIC_RELOADABLE_FLAG_COUNT(quic_do_not_send_settings); + return; + } + SpdySettingsIR settings_frame; settings_frame.AddSetting(SETTINGS_MAX_HEADER_LIST_SIZE, value); @@ -582,6 +623,10 @@ QuicSpdyStream* QuicSpdySession::GetSpdyDataStream( void QuicSpdySession::OnCryptoHandshakeEvent(CryptoHandshakeEvent event) { QuicSession::OnCryptoHandshakeEvent(event); + if (VersionUsesQpack(transport_version()) && event == HANDSHAKE_CONFIRMED) { + SendMaxHeaderListSize(max_inbound_header_list_size_); + return; + } if (event == HANDSHAKE_CONFIRMED && config()->SupportMaxHeaderListSize()) { SendMaxHeaderListSize(max_inbound_header_list_size_); } @@ -589,9 +634,10 @@ void QuicSpdySession::OnCryptoHandshakeEvent(CryptoHandshakeEvent event) { // True if there are open HTTP requests. bool QuicSpdySession::ShouldKeepConnectionAlive() const { - // Change to check if there are open HTTP requests. - // When IETF QUIC control and QPACK streams are used, those will need to be - // subtracted from this count to ensure only request streams are counted. + if (GetQuicReloadableFlag(quic_aggressive_connection_aliveness)) { + QUIC_RELOADABLE_FLAG_COUNT(quic_aggressive_connection_aliveness); + return GetNumActiveStreams() > 0; + } return GetNumOpenDynamicStreams() > 0; } @@ -639,13 +685,104 @@ void QuicSpdySession::OnPromiseHeaderList( ConnectionCloseBehavior::SILENT_CLOSE); } +void QuicSpdySession::OnSetting(uint64_t id, uint64_t value) { + if (VersionHasStreamType(connection()->transport_version())) { + // SETTINGS frame received on the control stream. + switch (id) { + case SETTINGS_QPACK_MAX_TABLE_CAPACITY: + QUIC_DVLOG(1) + << "SETTINGS_QPACK_MAX_TABLE_CAPACITY received with value " + << value; + // Communicate |value| to encoder, because it is used for encoding + // Required Insert Count. + qpack_encoder_->SetMaximumDynamicTableCapacity(value); + // However, limit the dynamic table capacity to + // |qpack_maximum_dynamic_table_capacity_|. + qpack_encoder_->SetDynamicTableCapacity( + std::min(value, qpack_maximum_dynamic_table_capacity_)); + break; + case SETTINGS_MAX_HEADER_LIST_SIZE: + QUIC_DVLOG(1) << "SETTINGS_MAX_HEADER_LIST_SIZE received with value " + << value; + max_outbound_header_list_size_ = value; + break; + case SETTINGS_QPACK_BLOCKED_STREAMS: + QUIC_DVLOG(1) << "SETTINGS_QPACK_BLOCKED_STREAMS received with value " + << value; + qpack_encoder_->SetMaximumBlockedStreams(value); + break; + case SETTINGS_NUM_PLACEHOLDERS: + QUIC_DVLOG(1) << "SETTINGS_NUM_PLACEHOLDERS received with value " + << value; + // TODO: Support placeholder setting. + break; + default: + QUIC_DVLOG(1) << "Unknown setting identifier " << id + << " received with value " << value; + // Ignore unknown settings. + break; + } + return; + } + + // SETTINGS frame received on the headers stream. + switch (id) { + case spdy::SETTINGS_HEADER_TABLE_SIZE: + QUIC_DVLOG(1) << "SETTINGS_HEADER_TABLE_SIZE received with value " + << value; + spdy_framer_.UpdateHeaderEncoderTableSize(value); + break; + case spdy::SETTINGS_ENABLE_PUSH: + if (perspective() == Perspective::IS_SERVER) { + // See rfc7540, Section 6.5.2. + if (value > 1) { + QUIC_DLOG(ERROR) << "Invalid value " << value + << " received for SETTINGS_ENABLE_PUSH."; + if (IsConnected()) { + CloseConnectionWithDetails( + QUIC_INVALID_HEADERS_STREAM_DATA, + QuicStrCat("Invalid value for SETTINGS_ENABLE_PUSH: ", value)); + } + return; + } + QUIC_DVLOG(1) << "SETTINGS_ENABLE_PUSH received with value " << value; + server_push_enabled_ = value; + break; + } else { + QUIC_DLOG(ERROR) + << "Invalid SETTINGS_ENABLE_PUSH received by client with value " + << value; + if (IsConnected()) { + CloseConnectionWithDetails( + QUIC_INVALID_HEADERS_STREAM_DATA, + QuicStrCat("Unsupported field of HTTP/2 SETTINGS frame: ", id)); + } + } + break; + // TODO(fayang): Need to support SETTINGS_MAX_HEADER_LIST_SIZE when + // clients are actually sending it. + case spdy::SETTINGS_MAX_HEADER_LIST_SIZE: + QUIC_DVLOG(1) << "SETTINGS_MAX_HEADER_LIST_SIZE received with value " + << value; + break; + default: + QUIC_DLOG(ERROR) << "Unknown setting identifier " << id + << " received with value " << value; + if (IsConnected()) { + CloseConnectionWithDetails( + QUIC_INVALID_HEADERS_STREAM_DATA, + QuicStrCat("Unsupported field of HTTP/2 SETTINGS frame: ", id)); + } + } +} + bool QuicSpdySession::ShouldReleaseHeadersStreamSequencerBuffer() { return false; } void QuicSpdySession::OnHeaders(SpdyStreamId stream_id, bool has_priority, - SpdyPriority priority, + const spdy::SpdyStreamPrecedence& precedence, bool fin) { if (has_priority) { if (perspective() == Perspective::IS_CLIENT) { @@ -653,7 +790,7 @@ void QuicSpdySession::OnHeaders(SpdyStreamId stream_id, "Server must not send priorities."); return; } - OnStreamHeadersPriority(stream_id, priority); + OnStreamHeadersPriority(stream_id, precedence); } else { if (perspective() == Perspective::IS_SERVER) { CloseConnectionWithDetails(QUIC_INVALID_HEADERS_STREAM_DATA, @@ -682,18 +819,23 @@ void QuicSpdySession::OnPushPromise(SpdyStreamId stream_id, // TODO (wangyix): Why is SpdyStreamId used instead of QuicStreamId? // This occurs in many places in this file. void QuicSpdySession::OnPriority(SpdyStreamId stream_id, - SpdyPriority priority) { + const spdy::SpdyStreamPrecedence& precedence) { if (perspective() == Perspective::IS_CLIENT) { CloseConnectionWithDetails(QUIC_INVALID_HEADERS_STREAM_DATA, "Server must not send PRIORITY frames."); return; } - OnPriorityFrame(stream_id, priority); + OnPriorityFrame(stream_id, precedence); } void QuicSpdySession::OnHeaderList(const QuicHeaderList& header_list) { QUIC_DVLOG(1) << "Received header list for stream " << stream_id_ << ": " << header_list.DebugString(); + // This code path is only executed for push promise in IETF QUIC. + if (VersionUsesQpack(connection()->transport_version())) { + DCHECK(promised_stream_id_ != + QuicUtils::GetInvalidStreamId(connection()->transport_version())); + } if (promised_stream_id_ == QuicUtils::GetInvalidStreamId(connection()->transport_version())) { OnStreamHeaderList(stream_id_, fin_, frame_len_, header_list); @@ -707,7 +849,6 @@ void QuicSpdySession::OnHeaderList(const QuicHeaderList& header_list) { stream_id_ = QuicUtils::GetInvalidStreamId(connection()->transport_version()); fin_ = false; frame_len_ = 0; - uncompressed_frame_len_ = 0; } void QuicSpdySession::OnCompressedFrameSize(size_t frame_len) { @@ -728,14 +869,6 @@ void QuicSpdySession::SetHpackDecoderDebugVisitor( connection()->helper()->GetClock(), std::move(visitor))); } -void QuicSpdySession::UpdateHeaderEncoderTableSize(uint32_t value) { - spdy_framer_.UpdateHeaderEncoderTableSize(value); -} - -void QuicSpdySession::UpdateEnableServerPush(bool value) { - set_server_push_enabled(value); -} - void QuicSpdySession::CloseConnectionWithDetails(QuicErrorCode error, const std::string& details) { connection()->CloseConnection( @@ -743,9 +876,14 @@ void QuicSpdySession::CloseConnectionWithDetails(QuicErrorCode error, } bool QuicSpdySession::HasActiveRequestStreams() const { - // In the case where session is destructed by calling - // stream_map().clear(), we will have incorrect accounting here. - // TODO(renjietang): Modify destructors and make this a DCHECK. + if (GetQuicReloadableFlag(quic_active_streams_never_negative)) { + QUIC_RELOADABLE_FLAG_COUNT(quic_active_streams_never_negative); + DCHECK(static_cast<size_t>(stream_map().size()) >= + num_incoming_static_streams() + num_outgoing_static_streams()); + return stream_map().size() - num_incoming_static_streams() - + num_outgoing_static_streams() > + 0; + } if (static_cast<size_t>(stream_map().size()) > num_incoming_static_streams() + num_outgoing_static_streams()) { return stream_map().size() - num_incoming_static_streams() - @@ -768,17 +906,27 @@ bool QuicSpdySession::ProcessPendingStream(PendingStream* pending) { uint8_t stream_type_length = reader.PeekVarInt62Length(); uint64_t stream_type = 0; if (!reader.ReadVarInt62(&stream_type)) { + if (pending->sequencer()->NumBytesBuffered() == + pending->sequencer()->close_offset()) { + // Stream received FIN but there are not enough bytes for stream type. + // Mark all bytes consumed in order to close stream. + pending->MarkConsumed(pending->sequencer()->close_offset()); + } return false; } pending->MarkConsumed(stream_type_length); switch (stream_type) { case kControlStream: { // HTTP/3 control stream. + if (receive_control_stream_) { + CloseConnectionOnDuplicateHttp3UnidirectionalStreams("Control"); + return false; + } auto receive_stream = QuicMakeUnique<QuicReceiveControlStream>(pending); receive_control_stream_ = receive_stream.get(); - RegisterStaticStream(std::move(receive_stream), - /*stream_already_counted = */ true); + RegisterStaticStream(std::move(receive_stream)); receive_control_stream_->SetUnblocked(); + QUIC_DVLOG(1) << "Receive Control stream is created"; return true; } case kServerPushStream: { // Push Stream. @@ -786,16 +934,105 @@ bool QuicSpdySession::ProcessPendingStream(PendingStream* pending) { stream->SetUnblocked(); return true; } - case kQpackEncoderStream: // QPACK encoder stream. - // TODO(bnc): Create QPACK encoder stream. - break; - case kQpackDecoderStream: // QPACK decoder stream. - // TODO(bnc): Create QPACK decoder stream. - break; + case kQpackEncoderStream: { // QPACK encoder stream. + if (qpack_encoder_receive_stream_) { + CloseConnectionOnDuplicateHttp3UnidirectionalStreams("QPACK encoder"); + return false; + } + auto encoder_receive = QuicMakeUnique<QpackReceiveStream>( + pending, qpack_decoder_->encoder_stream_receiver()); + qpack_encoder_receive_stream_ = encoder_receive.get(); + RegisterStaticStream(std::move(encoder_receive)); + qpack_encoder_receive_stream_->SetUnblocked(); + QUIC_DVLOG(1) << "Receive QPACK Encoder stream is created"; + return true; + } + case kQpackDecoderStream: { // QPACK decoder stream. + if (qpack_decoder_receive_stream_) { + CloseConnectionOnDuplicateHttp3UnidirectionalStreams("QPACK decoder"); + return false; + } + auto decoder_receive = QuicMakeUnique<QpackReceiveStream>( + pending, qpack_encoder_->decoder_stream_receiver()); + qpack_decoder_receive_stream_ = decoder_receive.get(); + RegisterStaticStream(std::move(decoder_receive)); + qpack_decoder_receive_stream_->SetUnblocked(); + QUIC_DVLOG(1) << "Receive Qpack Decoder stream is created"; + return true; + } default: SendStopSending(kHttpUnknownStreamType, pending->id()); + pending->StopReading(); } return false; } +void QuicSpdySession::MaybeInitializeHttp3UnidirectionalStreams() { + DCHECK(VersionHasStreamType(connection()->transport_version())); + if (!send_control_stream_ && CanOpenNextOutgoingUnidirectionalStream()) { + auto send_control = QuicMakeUnique<QuicSendControlStream>( + GetNextOutgoingUnidirectionalStreamId(), this, + qpack_maximum_dynamic_table_capacity_, max_inbound_header_list_size_); + send_control_stream_ = send_control.get(); + RegisterStaticStream(std::move(send_control)); + } + + if (!qpack_decoder_send_stream_ && + CanOpenNextOutgoingUnidirectionalStream()) { + auto decoder_send = QuicMakeUnique<QpackSendStream>( + GetNextOutgoingUnidirectionalStreamId(), this, kQpackDecoderStream); + qpack_decoder_send_stream_ = decoder_send.get(); + RegisterStaticStream(std::move(decoder_send)); + qpack_decoder_->set_qpack_stream_sender_delegate( + qpack_decoder_send_stream_); + } + + if (!qpack_encoder_send_stream_ && + CanOpenNextOutgoingUnidirectionalStream()) { + auto encoder_send = QuicMakeUnique<QpackSendStream>( + GetNextOutgoingUnidirectionalStreamId(), this, kQpackEncoderStream); + qpack_encoder_send_stream_ = encoder_send.get(); + RegisterStaticStream(std::move(encoder_send)); + qpack_encoder_->set_qpack_stream_sender_delegate( + qpack_encoder_send_stream_); + } +} + +void QuicSpdySession::OnCanCreateNewOutgoingStream(bool unidirectional) { + if (unidirectional && + VersionHasStreamType(connection()->transport_version())) { + MaybeInitializeHttp3UnidirectionalStreams(); + } +} + +void QuicSpdySession::set_max_allowed_push_id( + QuicStreamId max_allowed_push_id) { + if (VersionHasIetfQuicFrames(connection()->transport_version()) && + perspective() == Perspective::IS_SERVER && + max_allowed_push_id > max_allowed_push_id_) { + OnCanCreateNewOutgoingStream(true); + } + + max_allowed_push_id_ = max_allowed_push_id; + + if (VersionHasIetfQuicFrames(connection()->transport_version()) && + perspective() == Perspective::IS_CLIENT && IsHandshakeConfirmed()) { + SendMaxPushId(max_allowed_push_id); + } +} + +void QuicSpdySession::SendMaxPushId(QuicStreamId max_allowed_push_id) { + DCHECK(VersionHasStreamType(connection()->transport_version())); + send_control_stream_->SendMaxPushIdFrame(max_allowed_push_id); +} + +void QuicSpdySession::CloseConnectionOnDuplicateHttp3UnidirectionalStreams( + QuicStringPiece type) { + QUIC_PEER_BUG << QuicStrCat("Received a duplicate ", type, + " stream: Closing connection."); + // TODO(b/124216424): Change to HTTP_STREAM_CREATION_ERROR. + CloseConnectionWithDetails(QUIC_INVALID_STREAM_ID, + QuicStrCat(type, " stream is received twice.")); +} + } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_session.h b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_session.h index aa2c27d067f..0ee36327f26 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_session.h +++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_session.h @@ -18,6 +18,8 @@ #include "net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_sender.h" #include "net/third_party/quiche/src/quic/core/qpack/qpack_encoder.h" #include "net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_sender.h" +#include "net/third_party/quiche/src/quic/core/qpack/qpack_receive_stream.h" +#include "net/third_party/quiche/src/quic/core/qpack/qpack_send_stream.h" #include "net/third_party/quiche/src/quic/core/qpack/qpack_utils.h" #include "net/third_party/quiche/src/quic/core/quic_session.h" #include "net/third_party/quiche/src/quic/core/quic_versions.h" @@ -50,7 +52,7 @@ class QUIC_EXPORT_PRIVATE QuicHpackDebugVisitor { virtual void OnUseEntry(QuicTime::Delta elapsed) = 0; }; -// A QUIC session with a headers stream. +// A QUIC session for HTTP. class QUIC_EXPORT_PRIVATE QuicSpdySession : public QuicSession, public QpackEncoder::DecoderStreamErrorDelegate, @@ -76,8 +78,9 @@ class QUIC_EXPORT_PRIVATE QuicSpdySession // Called by |headers_stream_| when headers with a priority have been // received for a stream. This method will only be called for server streams. - virtual void OnStreamHeadersPriority(QuicStreamId stream_id, - spdy::SpdyPriority priority); + virtual void OnStreamHeadersPriority( + QuicStreamId stream_id, + const spdy::SpdyStreamPrecedence& precedence); // Called by |headers_stream_| when headers have been completely received // for a stream. |fin| will be true if the fin flag was set in the headers @@ -98,7 +101,7 @@ class QUIC_EXPORT_PRIVATE QuicSpdySession // Called by |headers_stream_| when a PRIORITY frame has been received for a // stream. This method will only be called for server streams. virtual void OnPriorityFrame(QuicStreamId stream_id, - spdy::SpdyPriority priority); + const spdy::SpdyStreamPrecedence& precedence); // Sends contents of |iov| to h2_deframer_, returns number of bytes processed. size_t ProcessHeaderData(const struct iovec& iov); @@ -111,7 +114,7 @@ class QUIC_EXPORT_PRIVATE QuicSpdySession QuicStreamId id, spdy::SpdyHeaderBlock headers, bool fin, - spdy::SpdyPriority priority, + const spdy::SpdyStreamPrecedence& precedence, QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener); // Writes a PRIORITY frame the to peer. Returns the size in bytes of the @@ -142,9 +145,8 @@ class QUIC_EXPORT_PRIVATE QuicSpdySession bool server_push_enabled() const { return server_push_enabled_; } - // Called by |QuicHeadersStream::UpdateEnableServerPush()| with - // value from SETTINGS_ENABLE_PUSH. - void set_server_push_enabled(bool enable) { server_push_enabled_ = enable; } + // Called when a setting is parsed from an incoming SETTINGS frame. + void OnSetting(uint64_t id, uint64_t value); // Return true if this session wants to release headers stream's buffer // aggressively. @@ -153,14 +155,25 @@ class QUIC_EXPORT_PRIVATE QuicSpdySession void CloseConnectionWithDetails(QuicErrorCode error, const std::string& details); - // Must be called before Initialize(). + // Must not be called after Initialize(). // TODO(bnc): Move to constructor argument. - void set_max_inbound_header_list_size(size_t max_inbound_header_list_size) { - max_inbound_header_list_size_ = max_inbound_header_list_size; + void set_qpack_maximum_dynamic_table_capacity( + uint64_t qpack_maximum_dynamic_table_capacity) { + qpack_maximum_dynamic_table_capacity_ = + qpack_maximum_dynamic_table_capacity; + } + + // Must not be called after Initialize(). + // TODO(bnc): Move to constructor argument. + void set_qpack_maximum_blocked_streams( + uint64_t qpack_maximum_blocked_streams) { + qpack_maximum_blocked_streams_ = qpack_maximum_blocked_streams; } - void set_max_outbound_header_list_size(size_t max_outbound_header_list_size) { - max_outbound_header_list_size_ = max_outbound_header_list_size; + // Must not be called after Initialize(). + // TODO(bnc): Move to constructor argument. + void set_max_inbound_header_list_size(size_t max_inbound_header_list_size) { + max_inbound_header_list_size_ = max_inbound_header_list_size; } size_t max_outbound_header_list_size() const { @@ -174,6 +187,28 @@ class QUIC_EXPORT_PRIVATE QuicSpdySession // Returns true if the session has active request streams. bool HasActiveRequestStreams() const; + // Called when the size of the compressed frame payload is available. + void OnCompressedFrameSize(size_t frame_len); + + // Called when a PUSH_PROMISE frame has been received. + void OnPushPromise(spdy::SpdyStreamId stream_id, + spdy::SpdyStreamId promised_stream_id); + + // Called when the complete list of headers is available. + void OnHeaderList(const QuicHeaderList& header_list); + + QuicStreamId promised_stream_id() const { return promised_stream_id_; } + + // Initialze HTTP/3 unidirectional streams if |unidirectional| is true and + // those streams are not initialized yet. + void OnCanCreateNewOutgoingStream(bool unidirectional) override; + + void set_max_allowed_push_id(QuicStreamId max_allowed_push_id); + + QuicStreamId max_allowed_push_id() { return max_allowed_push_id_; } + + int32_t destruction_indicator() const { return destruction_indicator_; } + protected: // Override CreateIncomingStream(), CreateOutgoingBidirectionalStream() and // CreateOutgoingUnidirectionalStream() with QuicSpdyStream return type to @@ -233,6 +268,18 @@ class QUIC_EXPORT_PRIVATE QuicSpdySession bool IsConnected() { return connection()->connected(); } + const QuicReceiveControlStream* receive_control_stream() const { + return receive_control_stream_; + } + + // Initializes HTTP/3 unidirectional streams if not yet initialzed. + virtual void MaybeInitializeHttp3UnidirectionalStreams(); + + void set_max_uncompressed_header_bytes( + size_t set_max_uncompressed_header_bytes); + + void SendMaxPushId(QuicStreamId max_allowed_push_id); + private: friend class test::QuicSpdySessionPeer; @@ -243,21 +290,15 @@ class QUIC_EXPORT_PRIVATE QuicSpdySession // Called when a HEADERS frame has been received. void OnHeaders(spdy::SpdyStreamId stream_id, bool has_priority, - spdy::SpdyPriority priority, + const spdy::SpdyStreamPrecedence& precedence, bool fin); - // Called when a PUSH_PROMISE frame has been received. - void OnPushPromise(spdy::SpdyStreamId stream_id, - spdy::SpdyStreamId promised_stream_id); - // Called when a PRIORITY frame has been received. - void OnPriority(spdy::SpdyStreamId stream_id, spdy::SpdyPriority priority); - - // Called when the complete list of headers is available. - void OnHeaderList(const QuicHeaderList& header_list); + void OnPriority(spdy::SpdyStreamId stream_id, + const spdy::SpdyStreamPrecedence& precedence); - // Called when the size of the compressed frame payload is available. - void OnCompressedFrameSize(size_t frame_len); + void CloseConnectionOnDuplicateHttp3UnidirectionalStreams( + QuicStringPiece type); std::unique_ptr<QpackEncoder> qpack_encoder_; std::unique_ptr<QpackDecoder> qpack_decoder_; @@ -265,13 +306,32 @@ class QUIC_EXPORT_PRIVATE QuicSpdySession // Pointer to the header stream in stream_map_. QuicHeadersStream* headers_stream_; - // HTTP/3 control streams. They are owned by QuicSession inside dynamic + // HTTP/3 control streams. They are owned by QuicSession inside // stream map, and can be accessed by those unowned pointers below. QuicSendControlStream* send_control_stream_; QuicReceiveControlStream* receive_control_stream_; + // Pointers to HTTP/3 QPACK streams in stream map. + QpackReceiveStream* qpack_encoder_receive_stream_; + QpackReceiveStream* qpack_decoder_receive_stream_; + QpackSendStream* qpack_encoder_send_stream_; + QpackSendStream* qpack_decoder_send_stream_; + + // Maximum dynamic table capacity as defined at + // https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#maximum-dynamic-table-capacity + // for the decoding context. Value will be sent via + // SETTINGS_QPACK_MAX_TABLE_CAPACITY. + uint64_t qpack_maximum_dynamic_table_capacity_; + + // Maximum number of blocked streams as defined at + // https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#blocked-streams + // for the decoding context. Value will be sent via + // SETTINGS_QPACK_BLOCKED_STREAMS. + uint64_t qpack_maximum_blocked_streams_; + // The maximum size of a header block that will be accepted from the peer, // defined per spec as key + value + overhead per field (uncompressed). + // Value will be sent via SETTINGS_MAX_HEADER_LIST_SIZE. size_t max_inbound_header_list_size_; // The maximum size of a header block that can be sent to the peer. This field @@ -288,17 +348,18 @@ class QUIC_EXPORT_PRIVATE QuicSpdySession QuicStreamId promised_stream_id_; bool fin_; size_t frame_len_; - size_t uncompressed_frame_len_; bool supports_push_promise_; spdy::SpdyFramer spdy_framer_; http2::Http2DecoderAdapter h2_deframer_; std::unique_ptr<SpdyFramerVisitor> spdy_framer_visitor_; + QuicStreamId max_allowed_push_id_; - // TODO(renjietang): Replace these two members with actual QPACK send streams. - NoopQpackStreamSenderDelegate encoder_stream_sender_delegate_; - NoopQpackStreamSenderDelegate decoder_stream_sender_delegate_; + // An integer used for live check. The indicator is assigned a value in + // constructor. As long as it is not the assigned value, that would indicate + // an use-after-free. + int32_t destruction_indicator_; }; } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_session_test.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_session_test.cc index 2042f88019e..55de8b68475 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_session_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_session_test.cc @@ -11,12 +11,15 @@ #include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h" #include "net/third_party/quiche/src/quic/core/crypto/null_encrypter.h" +#include "net/third_party/quiche/src/quic/core/frames/quic_stream_frame.h" #include "net/third_party/quiche/src/quic/core/http/http_constants.h" #include "net/third_party/quiche/src/quic/core/quic_crypto_stream.h" #include "net/third_party/quiche/src/quic/core/quic_data_writer.h" #include "net/third_party/quiche/src/quic/core/quic_packets.h" #include "net/third_party/quiche/src/quic/core/quic_stream.h" #include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/core/quic_versions.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" #include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h" #include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" #include "net/third_party/quiche/src/quic/platform/api/quic_map_util.h" @@ -24,6 +27,9 @@ #include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h" #include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" #include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/qpack_encoder_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/qpack_header_table_peer.h" #include "net/third_party/quiche/src/quic/test_tools/quic_config_peer.h" #include "net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h" #include "net/third_party/quiche/src/quic/test_tools/quic_flow_controller_peer.h" @@ -52,6 +58,11 @@ namespace quic { namespace test { namespace { +bool VerifyAndClearStopSendingFrame(const QuicFrame& frame) { + EXPECT_EQ(STOP_SENDING_FRAME, frame.type); + return ClearControlFrame(frame); +} + class TestCryptoStream : public QuicCryptoStream, public QuicCryptoHandshaker { public: explicit TestCryptoStream(QuicSession* session) @@ -293,6 +304,11 @@ class QuicSpdySessionTestBase : public QuicTestWithParam<ParsedQuicVersion> { kInitialStreamFlowControlWindowForTest); session_.config()->SetInitialSessionFlowControlWindowToSend( kInitialSessionFlowControlWindowForTest); + if (VersionUsesQpack(transport_version())) { + QuicConfigPeer::SetReceivedMaxIncomingUnidirectionalStreams( + session_.config(), + session_.num_expected_unidirectional_static_streams()); + } connection_->AdvanceTime(QuicTime::Delta::FromSeconds(1)); TestCryptoStream* crypto_stream = session_.GetMutableCryptoStream(); EXPECT_CALL(*crypto_stream, HasPendingRetransmission()) @@ -406,7 +422,14 @@ TEST_P(QuicSpdySessionTestServer, SelfAddress) { } TEST_P(QuicSpdySessionTestServer, IsCryptoHandshakeConfirmed) { - EXPECT_CALL(*connection_, CloseConnection(_, _, _)); + if (!GetQuicReloadableFlag(quic_do_not_send_settings) || + VersionUsesQpack(transport_version())) { + MockPacketWriter* writer = static_cast<MockPacketWriter*>( + QuicConnectionPeer::GetWriter(session_.connection())); + EXPECT_CALL(*writer, WritePacket(_, _, _, _, _)) + .Times(1) + .WillRepeatedly(Return(WriteResult(WRITE_STATUS_OK, 0))); + } EXPECT_FALSE(session_.IsCryptoHandshakeConfirmed()); CryptoHandshakeMessage message; session_.GetMutableCryptoStream()->OnHandshakeMessage(message); @@ -477,12 +500,9 @@ TEST_P(QuicSpdySessionTestServer, MaximumAvailableOpenedStreams) { // stream ID, the next ID should fail. Since the actual limit // is not the number of open streams, we allocate the max and the max+2. // Get the max allowed stream ID, this should succeed. - QuicStreamCount headers_stream_offset = - VersionLacksHeadersStream(QUIC_VERSION_99) ? 1 : 0; QuicStreamId stream_id = StreamCountToId( QuicSessionPeer::v99_streamid_manager(&session_) - ->actual_max_allowed_incoming_bidirectional_streams() - - headers_stream_offset, + ->actual_max_allowed_incoming_bidirectional_streams(), Perspective::IS_CLIENT, // Client initates stream, allocs stream id. /*bidirectional=*/true); EXPECT_NE(nullptr, session_.GetOrCreateStream(stream_id)); @@ -497,7 +517,7 @@ TEST_P(QuicSpdySessionTestServer, MaximumAvailableOpenedStreams) { stream_id = StreamCountToId( QuicSessionPeer::v99_streamid_manager(&session_) ->actual_max_allowed_incoming_bidirectional_streams() + - 1 - headers_stream_offset, + 1, Perspective::IS_CLIENT, /*bidirectional=*/true); EXPECT_EQ(nullptr, session_.GetOrCreateStream(stream_id)); @@ -654,7 +674,7 @@ TEST_P(QuicSpdySessionTestServer, TestBatchedWrites) { // Now let stream 4 do the 2nd of its 3 writes, but add a block for a high // priority stream 6. 4 should be preempted. 6 will write but *not* block so // will cede back to 4. - stream6->SetPriority(kV3HighestPriority); + stream6->SetPriority(spdy::SpdyStreamPrecedence(kV3HighestPriority)); EXPECT_CALL(*stream4, OnCanWrite()) .WillOnce(Invoke([this, stream4, stream6]() { session_.SendLargeFakeData(stream4, 6000); @@ -687,13 +707,16 @@ TEST_P(QuicSpdySessionTestServer, OnCanWriteBundlesStreams) { .WillRepeatedly(Invoke( this, &QuicSpdySessionTestServer::ClearMaxStreamsControlFrame)); } + // Encryption needs to be established before data can be sent. CryptoHandshakeMessage msg; MockPacketWriter* writer = static_cast<MockPacketWriter*>( QuicConnectionPeer::GetWriter(session_.connection())); EXPECT_CALL(*writer, WritePacket(_, _, _, _, _)) - .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 0))); + .Times(testing::AnyNumber()) + .WillRepeatedly(Return(WriteResult(WRITE_STATUS_OK, 0))); session_.GetMutableCryptoStream()->OnHandshakeMessage(msg); + testing::Mock::VerifyAndClearExpectations(writer); // Drive congestion control manually. MockSendAlgorithm* send_algorithm = new StrictMock<MockSendAlgorithm>; @@ -927,14 +950,16 @@ TEST_P(QuicSpdySessionTestServer, TestCryptoStream* crypto_stream = session_.GetMutableCryptoStream(); EXPECT_CALL(*crypto_stream, OnCanWrite()); } - TestHeadersStream* headers_stream; - QuicSpdySessionPeer::SetHeadersStream(&session_, nullptr); - headers_stream = new TestHeadersStream(&session_); - QuicSpdySessionPeer::SetHeadersStream(&session_, headers_stream); - session_.MarkConnectionLevelWriteBlocked( - QuicUtils::GetHeadersStreamId(connection_->transport_version())); - EXPECT_CALL(*headers_stream, OnCanWrite()); + if (!VersionUsesQpack(connection_->transport_version())) { + TestHeadersStream* headers_stream; + QuicSpdySessionPeer::SetHeadersStream(&session_, nullptr); + headers_stream = new TestHeadersStream(&session_); + QuicSpdySessionPeer::SetHeadersStream(&session_, headers_stream); + session_.MarkConnectionLevelWriteBlocked( + QuicUtils::GetHeadersStreamId(connection_->transport_version())); + EXPECT_CALL(*headers_stream, OnCanWrite()); + } // After the crypto and header streams perform a write, the connection will be // blocked by the flow control, hence it should become application-limited. @@ -949,6 +974,7 @@ TEST_P(QuicSpdySessionTestServer, SendGoAway) { // GoAway frames are not in version 99 return; } + connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); MockPacketWriter* writer = static_cast<MockPacketWriter*>( QuicConnectionPeer::GetWriter(session_.connection())); EXPECT_CALL(*writer, WritePacket(_, _, _, _, _)) @@ -1010,13 +1036,20 @@ TEST_P(QuicSpdySessionTestServer, ServerReplyToConnecitivityProbe) { connection_->OnPathChallengeFrame( QuicPathChallengeFrame(0, {{0, 1, 2, 3, 4, 5, 6, 7}})); } - session_.OnConnectivityProbeReceived(session_.self_address(), - new_peer_address); + session_.OnPacketReceived(session_.self_address(), new_peer_address, + /*is_connectivity_probe=*/true); EXPECT_EQ(old_peer_address, session_.peer_address()); } TEST_P(QuicSpdySessionTestServer, IncreasedTimeoutAfterCryptoHandshake) { - EXPECT_CALL(*connection_, CloseConnection(_, _, _)); + if (!GetQuicReloadableFlag(quic_do_not_send_settings) || + VersionUsesQpack(transport_version())) { + MockPacketWriter* writer = static_cast<MockPacketWriter*>( + QuicConnectionPeer::GetWriter(session_.connection())); + EXPECT_CALL(*writer, WritePacket(_, _, _, _, _)) + .Times(1) + .WillRepeatedly(Return(WriteResult(WRITE_STATUS_OK, 0))); + } EXPECT_EQ(kInitialIdleTimeoutSecs + 3, QuicConnectionPeer::GetNetworkTimeout(connection_).ToSeconds()); CryptoHandshakeMessage msg; @@ -1066,10 +1099,20 @@ TEST_P(QuicSpdySessionTestServer, RstStreamBeforeHeadersDecompressed) { } TEST_P(QuicSpdySessionTestServer, OnStreamFrameFinStaticStreamId) { + QuicStreamId id; + // Initialize HTTP/3 control stream. + if (VersionUsesQpack(transport_version())) { + id = GetNthClientInitiatedUnidirectionalStreamId(transport_version(), 3); + char type[] = {kControlStream}; + + QuicStreamFrame data1(id, false, 0, QuicStringPiece(type, 1)); + session_.OnStreamFrame(data1); + } else { + id = QuicUtils::GetHeadersStreamId(connection_->transport_version()); + } + // Send two bytes of payload. - QuicStreamFrame data1( - QuicUtils::GetHeadersStreamId(connection_->transport_version()), true, 0, - QuicStringPiece("HT")); + QuicStreamFrame data1(id, true, 0, QuicStringPiece("HT")); EXPECT_CALL(*connection_, CloseConnection( QUIC_INVALID_STREAM_ID, "Attempt to close a static stream", @@ -1078,11 +1121,21 @@ TEST_P(QuicSpdySessionTestServer, OnStreamFrameFinStaticStreamId) { } TEST_P(QuicSpdySessionTestServer, OnRstStreamStaticStreamId) { + QuicStreamId id; + // Initialize HTTP/3 control stream. + if (VersionUsesQpack(transport_version())) { + id = GetNthClientInitiatedUnidirectionalStreamId(transport_version(), 3); + char type[] = {kControlStream}; + + QuicStreamFrame data1(id, false, 0, QuicStringPiece(type, 1)); + session_.OnStreamFrame(data1); + } else { + id = QuicUtils::GetHeadersStreamId(connection_->transport_version()); + } + // Send two bytes of payload. - QuicRstStreamFrame rst1( - kInvalidControlFrameId, - QuicUtils::GetHeadersStreamId(connection_->transport_version()), - QUIC_ERROR_PROCESSING_STREAM, 0); + QuicRstStreamFrame rst1(kInvalidControlFrameId, id, + QUIC_ERROR_PROCESSING_STREAM, 0); EXPECT_CALL(*connection_, CloseConnection( QUIC_INVALID_STREAM_ID, "Attempt to reset a static stream", @@ -1253,14 +1306,15 @@ TEST_P(QuicSpdySessionTestServer, EXPECT_FALSE(session_.IsStreamFlowControlBlocked()); headers["header"] = QuicStrCat(random.RandUint64(), random.RandUint64(), random.RandUint64()); - session_.WriteHeadersOnHeadersStream(stream_id, headers.Clone(), true, 0, + session_.WriteHeadersOnHeadersStream(stream_id, headers.Clone(), true, + spdy::SpdyStreamPrecedence(0), nullptr); stream_id += IdDelta(); } // Write once more to ensure that the headers stream has buffered data. The // random headers may have exactly filled the flow control window. - session_.WriteHeadersOnHeadersStream(stream_id, std::move(headers), true, 0, - nullptr); + session_.WriteHeadersOnHeadersStream(stream_id, std::move(headers), true, + spdy::SpdyStreamPrecedence(0), nullptr); EXPECT_TRUE(headers_stream->HasBufferedData()); EXPECT_TRUE(headers_stream->flow_controller()->IsBlocked()); @@ -1494,11 +1548,10 @@ TEST_P(QuicSpdySessionTestServer, FlowControlWithInvalidFinalOffset) { } TEST_P(QuicSpdySessionTestServer, WindowUpdateUnblocksHeadersStream) { - if (GetParam().handshake_protocol == PROTOCOL_TLS1_3) { - // TODO(nharper, b/112643533): Figure out why this test fails when TLS is - // enabled and fix it. + if (VersionUsesQpack(GetParam().transport_version)) { return; } + // Test that a flow control blocked headers stream gets unblocked on recipt of // a WINDOW_UPDATE frame. @@ -1583,7 +1636,7 @@ TEST_P(QuicSpdySessionTestServer, *connection_, CloseConnection(QUIC_INVALID_STREAM_ID, testing::MatchesRegex( - "Stream id \\d+ would exceed stream count limit 6"), + "Stream id \\d+ would exceed stream count limit 5"), _)); } // Create one more data streams to exceed limit of open stream. @@ -1690,8 +1743,6 @@ TEST_P(QuicSpdySessionTestClient, TooLargeHeadersMustNotCauseWriteAfterReset) { return; } - SetQuicReloadableFlag(quic_avoid_empty_frame_after_empty_headers, true); - TestStream* stream = session_.CreateOutgoingBidirectionalStream(); // Write headers with FIN set to close write side of stream. @@ -1785,24 +1836,27 @@ TEST_P(QuicSpdySessionTestClient, Http3ServerPush) { return; } - char type[] = {0x01}; - std::string data = std::string(type, 1) + "header"; EXPECT_EQ(0u, session_.GetNumOpenIncomingStreams()); + + // Push unidirectional stream is type 0x01. + std::string frame_type1 = QuicTextUtils::HexDecode("01"); QuicStreamId stream_id1 = GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 0); - QuicStreamFrame data1(stream_id1, false, 0, QuicStringPiece(data)); - session_.OnStreamFrame(data1); + session_.OnStreamFrame(QuicStreamFrame(stream_id1, /* fin = */ false, + /* offset = */ 0, frame_type1)); + EXPECT_EQ(1u, session_.GetNumOpenIncomingStreams()); QuicStream* stream = session_.GetOrCreateStream(stream_id1); EXPECT_EQ(1u, stream->flow_controller()->bytes_consumed()); EXPECT_EQ(1u, session_.flow_controller()->bytes_consumed()); - char unoptimized_type[] = {0x80, 0x00, 0x00, 0x01}; - data = std::string(unoptimized_type, 4) + "header"; + // The same stream type can be encoded differently. + std::string frame_type2 = QuicTextUtils::HexDecode("80000001"); QuicStreamId stream_id2 = GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 1); - QuicStreamFrame data2(stream_id2, false, 0, QuicStringPiece(data)); - session_.OnStreamFrame(data2); + session_.OnStreamFrame(QuicStreamFrame(stream_id2, /* fin = */ false, + /* offset = */ 0, frame_type2)); + EXPECT_EQ(2u, session_.GetNumOpenIncomingStreams()); stream = session_.GetOrCreateStream(stream_id2); EXPECT_EQ(4u, stream->flow_controller()->bytes_consumed()); @@ -1814,19 +1868,30 @@ TEST_P(QuicSpdySessionTestClient, Http3ServerPushOutofOrderFrame) { return; } - char type[] = {0x01}; - EXPECT_EQ(0u, session_.GetNumOpenIncomingStreams()); - QuicStreamFrame data1( - GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 0), - false, 1, QuicStringPiece("header")); - session_.OnStreamFrame(data1); EXPECT_EQ(0u, session_.GetNumOpenIncomingStreams()); - QuicStreamFrame data2( - GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 0), - false, 0, QuicStringPiece(type, 1)); + // Push unidirectional stream is type 0x01. + std::string frame_type = QuicTextUtils::HexDecode("01"); + // The first field of a push stream is the Push ID. + std::string push_id = QuicTextUtils::HexDecode("4000"); + + QuicStreamId stream_id = + GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 0); + + QuicStreamFrame data1(stream_id, + /* fin = */ false, /* offset = */ 0, frame_type); + QuicStreamFrame data2(stream_id, + /* fin = */ false, /* offset = */ frame_type.size(), + push_id); + + // Receiving some stream data without stream type does not open the stream. session_.OnStreamFrame(data2); + EXPECT_EQ(0u, session_.GetNumOpenIncomingStreams()); + + session_.OnStreamFrame(data1); EXPECT_EQ(1u, session_.GetNumOpenIncomingStreams()); + QuicStream* stream = session_.GetOrCreateStream(stream_id); + EXPECT_EQ(3u, stream->flow_controller()->highest_received_byte_offset()); } TEST_P(QuicSpdySessionTestServer, ZombieStreams) { @@ -2006,49 +2071,86 @@ TEST_P(QuicSpdySessionTestServer, RetransmitFrames) { TEST_P(QuicSpdySessionTestServer, OnPriorityFrame) { QuicStreamId stream_id = GetNthClientInitiatedBidirectionalId(0); TestStream* stream = session_.CreateIncomingStream(stream_id); - session_.OnPriorityFrame(stream_id, kV3HighestPriority); - EXPECT_EQ(kV3HighestPriority, stream->priority()); + session_.OnPriorityFrame(stream_id, + spdy::SpdyStreamPrecedence(kV3HighestPriority)); + EXPECT_EQ(spdy::SpdyStreamPrecedence(kV3HighestPriority), + stream->precedence()); } TEST_P(QuicSpdySessionTestServer, SimplePendingStreamType) { if (!VersionHasStreamType(transport_version())) { return; } - PendingStream pending(QuicUtils::GetFirstUnidirectionalStreamId( - transport_version(), Perspective::IS_CLIENT), - &session_); - char input[] = {// type - 0x04, - // data - 'a', 'b', 'c'}; - QuicStreamFrame data(pending.id(), true, 0, QuicStringPiece(input, 4)); - pending.OnStreamFrame(data); - - // A stop sending frame will be sent to indicate unknown type. - EXPECT_CALL(*connection_, SendControlFrame(_)); - session_.ProcessPendingStream(&pending); + + char input[] = {0x04, // type + 'a', 'b', 'c'}; // data + QuicStringPiece payload(input, QUIC_ARRAYSIZE(input)); + + // This is a server test with a client-initiated unidirectional stream. + QuicStreamId stream_id = QuicUtils::GetFirstUnidirectionalStreamId( + transport_version(), Perspective::IS_CLIENT); + + for (bool fin : {true, false}) { + QuicStreamFrame frame(stream_id, fin, /* offset = */ 0, payload); + + // A STOP_SENDING frame is sent in response to the unknown stream type. + EXPECT_CALL(*connection_, SendControlFrame(_)) + .WillOnce(Invoke(&VerifyAndClearStopSendingFrame)); + session_.OnStreamFrame(frame); + + PendingStream* pending = + QuicSessionPeer::GetPendingStream(&session_, stream_id); + if (fin) { + // Stream is closed if FIN is received. + EXPECT_FALSE(pending); + } else { + ASSERT_TRUE(pending); + // The pending stream must ignore read data. + EXPECT_TRUE(pending->sequencer()->ignore_read_data()); + } + + stream_id += QuicUtils::StreamIdDelta(transport_version()); + } } TEST_P(QuicSpdySessionTestServer, SimplePendingStreamTypeOutOfOrderDelivery) { if (!VersionHasStreamType(transport_version())) { return; } - PendingStream pending(QuicUtils::GetFirstUnidirectionalStreamId( - transport_version(), Perspective::IS_CLIENT), - &session_); - char input[] = {// type - 0x04, - // data - 'a', 'b', 'c'}; - QuicStreamFrame data1(pending.id(), true, 1, QuicStringPiece(&input[1], 3)); - pending.OnStreamFrame(data1); - session_.ProcessPendingStream(&pending); - QuicStreamFrame data2(pending.id(), false, 0, QuicStringPiece(input, 1)); - pending.OnStreamFrame(data2); + char input[] = {0x04, // type + 'a', 'b', 'c'}; // data + QuicStringPiece payload(input, QUIC_ARRAYSIZE(input)); - EXPECT_CALL(*connection_, SendControlFrame(_)); - session_.ProcessPendingStream(&pending); + // This is a server test with a client-initiated unidirectional stream. + QuicStreamId stream_id = QuicUtils::GetFirstUnidirectionalStreamId( + transport_version(), Perspective::IS_CLIENT); + + for (bool fin : {true, false}) { + QuicStreamFrame frame1(stream_id, /* fin = */ false, /* offset = */ 0, + payload.substr(0, 1)); + QuicStreamFrame frame2(stream_id, fin, /* offset = */ 1, payload.substr(1)); + + // Deliver frames out of order. + session_.OnStreamFrame(frame2); + // A STOP_SENDING frame is sent in response to the unknown stream type. + EXPECT_CALL(*connection_, SendControlFrame(_)) + .WillOnce(Invoke(&VerifyAndClearStopSendingFrame)); + session_.OnStreamFrame(frame1); + + PendingStream* pending = + QuicSessionPeer::GetPendingStream(&session_, stream_id); + if (fin) { + // Stream is closed if FIN is received. + EXPECT_FALSE(pending); + } else { + ASSERT_TRUE(pending); + // The pending stream must ignore read data. + EXPECT_TRUE(pending->sequencer()->ignore_read_data()); + } + + stream_id += QuicUtils::StreamIdDelta(transport_version()); + } } TEST_P(QuicSpdySessionTestServer, @@ -2056,34 +2158,51 @@ TEST_P(QuicSpdySessionTestServer, if (!VersionHasStreamType(transport_version())) { return; } - PendingStream pending(QuicUtils::GetFirstUnidirectionalStreamId( - transport_version(), Perspective::IS_CLIENT), - &session_); - char input[] = {// type (256) - 0x40 + 0x01, 0x00, - // data - 'a', 'b', 'c'}; - QuicStreamFrame data1(pending.id(), true, 2, QuicStringPiece(&input[2], 3)); - pending.OnStreamFrame(data1); - session_.ProcessPendingStream(&pending); + char input[] = {0x41, 0x00, // type (256) + 'a', 'b', 'c'}; // data + QuicStringPiece payload(input, QUIC_ARRAYSIZE(input)); - QuicStreamFrame data2(pending.id(), false, 0, QuicStringPiece(input, 1)); - pending.OnStreamFrame(data2); - session_.ProcessPendingStream(&pending); + // This is a server test with a client-initiated unidirectional stream. + QuicStreamId stream_id = QuicUtils::GetFirstUnidirectionalStreamId( + transport_version(), Perspective::IS_CLIENT); - QuicStreamFrame data3(pending.id(), false, 1, QuicStringPiece(&input[1], 1)); - pending.OnStreamFrame(data3); + for (bool fin : {true, false}) { + QuicStreamFrame frame1(stream_id, /* fin = */ false, /* offset = */ 0, + payload.substr(0, 1)); + QuicStreamFrame frame2(stream_id, /* fin = */ false, /* offset = */ 1, + payload.substr(1, 1)); + QuicStreamFrame frame3(stream_id, fin, /* offset = */ 2, payload.substr(2)); - EXPECT_CALL(*connection_, SendControlFrame(_)); - session_.ProcessPendingStream(&pending); + // Deliver frames out of order. + session_.OnStreamFrame(frame3); + // The first byte does not contain the entire type varint. + session_.OnStreamFrame(frame1); + // A STOP_SENDING frame is sent in response to the unknown stream type. + EXPECT_CALL(*connection_, SendControlFrame(_)) + .WillOnce(Invoke(&VerifyAndClearStopSendingFrame)); + session_.OnStreamFrame(frame2); + + PendingStream* pending = + QuicSessionPeer::GetPendingStream(&session_, stream_id); + if (fin) { + // Stream is closed if FIN is received. + EXPECT_FALSE(pending); + } else { + ASSERT_TRUE(pending); + // The pending stream must ignore read data. + EXPECT_TRUE(pending->sequencer()->ignore_read_data()); + } + + stream_id += QuicUtils::StreamIdDelta(transport_version()); + } } TEST_P(QuicSpdySessionTestServer, ReceiveControlStream) { if (!VersionHasStreamType(transport_version())) { return; } - // Use a arbitrary stream id. + // Use an arbitrary stream id. QuicStreamId stream_id = GetNthClientInitiatedUnidirectionalStreamId(transport_version(), 3); char type[] = {kControlStream}; @@ -2094,14 +2213,27 @@ TEST_P(QuicSpdySessionTestServer, ReceiveControlStream) { QuicSpdySessionPeer::GetReceiveControlStream(&session_)->id()); SettingsFrame settings; - settings.values[3] = 2; - settings.values[kSettingsMaxHeaderListSize] = 5; + settings.values[SETTINGS_QPACK_MAX_TABLE_CAPACITY] = 512; + settings.values[SETTINGS_MAX_HEADER_LIST_SIZE] = 5; + settings.values[SETTINGS_QPACK_BLOCKED_STREAMS] = 42; std::string data = EncodeSettings(settings); QuicStreamFrame frame(stream_id, false, 1, QuicStringPiece(data)); + QpackEncoder* qpack_encoder = session_.qpack_encoder(); + QpackHeaderTable* header_table = + QpackEncoderPeer::header_table(qpack_encoder); + + EXPECT_NE(512u, + QpackHeaderTablePeer::maximum_dynamic_table_capacity(header_table)); EXPECT_NE(5u, session_.max_outbound_header_list_size()); + EXPECT_NE(42u, QpackEncoderPeer::maximum_blocked_streams(qpack_encoder)); + session_.OnStreamFrame(frame); + + EXPECT_EQ(512u, + QpackHeaderTablePeer::maximum_dynamic_table_capacity(header_table)); EXPECT_EQ(5u, session_.max_outbound_header_list_size()); + EXPECT_EQ(42u, QpackEncoderPeer::maximum_blocked_streams(qpack_encoder)); } TEST_P(QuicSpdySessionTestServer, ReceiveControlStreamOutOfOrderDelivery) { @@ -2114,7 +2246,7 @@ TEST_P(QuicSpdySessionTestServer, ReceiveControlStreamOutOfOrderDelivery) { char type[] = {kControlStream}; SettingsFrame settings; settings.values[3] = 2; - settings.values[kSettingsMaxHeaderListSize] = 5; + settings.values[SETTINGS_MAX_HEADER_LIST_SIZE] = 5; std::string data = EncodeSettings(settings); QuicStreamFrame data1(stream_id, false, 1, QuicStringPiece(data)); @@ -2126,6 +2258,203 @@ TEST_P(QuicSpdySessionTestServer, ReceiveControlStreamOutOfOrderDelivery) { EXPECT_EQ(5u, session_.max_outbound_header_list_size()); } +TEST_P(QuicSpdySessionTestClient, ResetAfterInvalidIncomingStreamType) { + if (!VersionHasStreamType(transport_version())) { + return; + } + ASSERT_TRUE(session_.UsesPendingStreams()); + + const QuicStreamId stream_id = + GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 0); + + // Payload consists of two bytes. The first byte is an unknown unidirectional + // stream type. The second one would be the type of a push stream, but it + // must not be interpreted as stream type. + std::string payload = QuicTextUtils::HexDecode("3f01"); + QuicStreamFrame frame(stream_id, /* fin = */ false, /* offset = */ 0, + payload); + + // A STOP_SENDING frame is sent in response to the unknown stream type. + EXPECT_CALL(*connection_, SendControlFrame(_)) + .WillOnce(Invoke(&VerifyAndClearStopSendingFrame)); + session_.OnStreamFrame(frame); + + // There are no active streams. + EXPECT_EQ(0u, session_.GetNumOpenIncomingStreams()); + + // The pending stream is still around, because it did not receive a FIN. + PendingStream* pending = + QuicSessionPeer::GetPendingStream(&session_, stream_id); + ASSERT_TRUE(pending); + + // The pending stream must ignore read data. + EXPECT_TRUE(pending->sequencer()->ignore_read_data()); + + // If the stream frame is received again, it should be ignored. + session_.OnStreamFrame(frame); + + // Receive RESET_STREAM. + QuicRstStreamFrame rst_frame(kInvalidControlFrameId, stream_id, + QUIC_STREAM_CANCELLED, + /* bytes_written = */ payload.size()); + + // This will trigger the sending of two control frames: one RESET_STREAM with + // QUIC_RST_ACKNOWLEDGEMENT, and one STOP_SENDING. + EXPECT_CALL(*connection_, SendControlFrame(_)) + .Times(2) + .WillRepeatedly(Invoke(&ClearControlFrame)); + EXPECT_CALL(*connection_, OnStreamReset(stream_id, QUIC_RST_ACKNOWLEDGEMENT)); + session_.OnRstStream(rst_frame); + + // The stream is closed. + EXPECT_FALSE(QuicSessionPeer::GetPendingStream(&session_, stream_id)); +} + +TEST_P(QuicSpdySessionTestClient, FinAfterInvalidIncomingStreamType) { + if (!VersionHasStreamType(transport_version())) { + return; + } + ASSERT_TRUE(session_.UsesPendingStreams()); + + const QuicStreamId stream_id = + GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 0); + + // Payload consists of two bytes. The first byte is an unknown unidirectional + // stream type. The second one would be the type of a push stream, but it + // must not be interpreted as stream type. + std::string payload = QuicTextUtils::HexDecode("3f01"); + QuicStreamFrame frame(stream_id, /* fin = */ false, /* offset = */ 0, + payload); + + // A STOP_SENDING frame is sent in response to the unknown stream type. + EXPECT_CALL(*connection_, SendControlFrame(_)) + .WillOnce(Invoke(&VerifyAndClearStopSendingFrame)); + session_.OnStreamFrame(frame); + + // The pending stream is still around, because it did not receive a FIN. + PendingStream* pending = + QuicSessionPeer::GetPendingStream(&session_, stream_id); + EXPECT_TRUE(pending); + + // The pending stream must ignore read data. + EXPECT_TRUE(pending->sequencer()->ignore_read_data()); + + // If the stream frame is received again, it should be ignored. + session_.OnStreamFrame(frame); + + // Receive FIN. + session_.OnStreamFrame(QuicStreamFrame(stream_id, /* fin = */ true, + /* offset = */ payload.size(), "")); + + EXPECT_FALSE(QuicSessionPeer::GetPendingStream(&session_, stream_id)); +} + +TEST_P(QuicSpdySessionTestClient, ResetInMiddleOfStreamType) { + if (!VersionHasStreamType(transport_version())) { + return; + } + ASSERT_TRUE(session_.UsesPendingStreams()); + + const QuicStreamId stream_id = + GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 0); + + // Payload is the first byte of a two byte varint encoding. + std::string payload = QuicTextUtils::HexDecode("40"); + QuicStreamFrame frame(stream_id, /* fin = */ false, /* offset = */ 0, + payload); + + session_.OnStreamFrame(frame); + EXPECT_TRUE(QuicSessionPeer::GetPendingStream(&session_, stream_id)); + + // Receive RESET_STREAM. + QuicRstStreamFrame rst_frame(kInvalidControlFrameId, stream_id, + QUIC_STREAM_CANCELLED, + /* bytes_written = */ payload.size()); + + // This will trigger the sending of two control frames: one RESET_STREAM with + // QUIC_RST_ACKNOWLEDGEMENT, and one STOP_SENDING. + EXPECT_CALL(*connection_, SendControlFrame(_)) + .Times(2) + .WillRepeatedly(Invoke(&ClearControlFrame)); + EXPECT_CALL(*connection_, OnStreamReset(stream_id, QUIC_RST_ACKNOWLEDGEMENT)); + session_.OnRstStream(rst_frame); + + // The stream is closed. + EXPECT_FALSE(QuicSessionPeer::GetPendingStream(&session_, stream_id)); +} + +TEST_P(QuicSpdySessionTestClient, FinInMiddleOfStreamType) { + if (!VersionHasStreamType(transport_version())) { + return; + } + ASSERT_TRUE(session_.UsesPendingStreams()); + + const QuicStreamId stream_id = + GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 0); + + // Payload is the first byte of a two byte varint encoding with a FIN. + std::string payload = QuicTextUtils::HexDecode("40"); + QuicStreamFrame frame(stream_id, /* fin = */ true, /* offset = */ 0, payload); + + session_.OnStreamFrame(frame); + EXPECT_FALSE(QuicSessionPeer::GetPendingStream(&session_, stream_id)); +} + +TEST_P(QuicSpdySessionTestClient, DuplicateHttp3UnidirectionalStreams) { + if (!VersionHasStreamType(transport_version())) { + return; + } + QuicStreamId id1 = + GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 0); + char type1[] = {kControlStream}; + + QuicStreamFrame data1(id1, false, 0, QuicStringPiece(type1, 1)); + session_.OnStreamFrame(data1); + QuicStreamId id2 = + GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 1); + QuicStreamFrame data2(id2, false, 0, QuicStringPiece(type1, 1)); + EXPECT_CALL(*connection_, + CloseConnection(QUIC_INVALID_STREAM_ID, + "Control stream is received twice.", _)); + EXPECT_QUIC_PEER_BUG( + session_.OnStreamFrame(data2), + "Received a duplicate Control stream: Closing connection."); + + QuicStreamId id3 = + GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 2); + char type2[]{kQpackEncoderStream}; + + QuicStreamFrame data3(id3, false, 0, QuicStringPiece(type2, 1)); + session_.OnStreamFrame(data3); + + QuicStreamId id4 = + GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 3); + QuicStreamFrame data4(id4, false, 0, QuicStringPiece(type2, 1)); + EXPECT_CALL(*connection_, + CloseConnection(QUIC_INVALID_STREAM_ID, + "QPACK encoder stream is received twice.", _)); + EXPECT_QUIC_PEER_BUG( + session_.OnStreamFrame(data4), + "Received a duplicate QPACK encoder stream: Closing connection."); + + QuicStreamId id5 = + GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 4); + char type3[]{kQpackDecoderStream}; + + QuicStreamFrame data5(id5, false, 0, QuicStringPiece(type3, 1)); + session_.OnStreamFrame(data5); + + QuicStreamId id6 = + GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 5); + QuicStreamFrame data6(id6, false, 0, QuicStringPiece(type3, 1)); + EXPECT_CALL(*connection_, + CloseConnection(QUIC_INVALID_STREAM_ID, + "QPACK decoder stream is received twice.", _)); + EXPECT_QUIC_PEER_BUG( + session_.OnStreamFrame(data6), + "Received a duplicate QPACK decoder stream: Closing connection."); +} + } // namespace } // namespace test } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream.cc index 97991e8b42b..28bcdcfc4b9 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream.cc +++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream.cc @@ -47,7 +47,7 @@ class QuicSpdyStream::HttpDecoderVisitor : public HttpDecoder::Visitor { ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); } - bool OnPriorityFrameStart(Http3FrameLengths /*frame_lengths*/) override { + bool OnPriorityFrameStart(QuicByteCount /*header_length*/) override { CloseConnectionOnWrongFrame("Priority"); return false; } @@ -72,7 +72,7 @@ class QuicSpdyStream::HttpDecoderVisitor : public HttpDecoder::Visitor { return false; } - bool OnSettingsFrameStart(Http3FrameLengths /*frame_lengths*/) override { + bool OnSettingsFrameStart(QuicByteCount /*header_length*/) override { CloseConnectionOnWrongFrame("Settings"); return false; } @@ -88,8 +88,8 @@ class QuicSpdyStream::HttpDecoderVisitor : public HttpDecoder::Visitor { return false; } - bool OnDataFrameStart(Http3FrameLengths frame_lengths) override { - return stream_->OnDataFrameStart(frame_lengths); + bool OnDataFrameStart(QuicByteCount header_length) override { + return stream_->OnDataFrameStart(header_length); } bool OnDataFramePayload(QuicStringPiece payload) override { @@ -99,12 +99,12 @@ class QuicSpdyStream::HttpDecoderVisitor : public HttpDecoder::Visitor { bool OnDataFrameEnd() override { return stream_->OnDataFrameEnd(); } - bool OnHeadersFrameStart(Http3FrameLengths frame_length) override { + bool OnHeadersFrameStart(QuicByteCount header_length) override { if (!VersionUsesQpack(stream_->transport_version())) { CloseConnectionOnWrongFrame("Headers"); return false; } - return stream_->OnHeadersFrameStart(frame_length); + return stream_->OnHeadersFrameStart(header_length); } bool OnHeadersFramePayload(QuicStringPiece payload) override { @@ -124,42 +124,50 @@ class QuicSpdyStream::HttpDecoderVisitor : public HttpDecoder::Visitor { return stream_->OnHeadersFrameEnd(); } - bool OnPushPromiseFrameStart(PushId /*push_id*/, - Http3FrameLengths /*frame_length*/) override { - // TODO(b/137554973): Consume frame header. - CloseConnectionOnWrongFrame("Push Promise"); - return false; + bool OnPushPromiseFrameStart(PushId push_id, + QuicByteCount header_length, + QuicByteCount push_id_length) override { + if (!VersionHasStreamType(stream_->transport_version())) { + CloseConnectionOnWrongFrame("Push Promise"); + return false; + } + return stream_->OnPushPromiseFrameStart(push_id, header_length, + push_id_length); } bool OnPushPromiseFramePayload(QuicStringPiece payload) override { - // TODO(b/137554973): Consume frame payload. DCHECK(!payload.empty()); - CloseConnectionOnWrongFrame("Push Promise"); - return false; + if (!VersionUsesQpack(stream_->transport_version())) { + CloseConnectionOnWrongFrame("Push Promise"); + return false; + } + return stream_->OnPushPromiseFramePayload(payload); } bool OnPushPromiseFrameEnd() override { - CloseConnectionOnWrongFrame("Push Promise"); - return false; + if (!VersionUsesQpack(stream_->transport_version())) { + CloseConnectionOnWrongFrame("Push Promise"); + return false; + } + return stream_->OnPushPromiseFrameEnd(); } - bool OnUnknownFrameStart(uint64_t /* frame_type */, - Http3FrameLengths /* frame_length */) override { - // TODO(b/137554973): Consume frame header. - return true; + bool OnUnknownFrameStart(uint64_t frame_type, + QuicByteCount header_length) override { + return stream_->OnUnknownFrameStart(frame_type, header_length); } - bool OnUnknownFramePayload(QuicStringPiece /* payload */) override { - // TODO(b/137554973): Consume frame payload. - return true; + bool OnUnknownFramePayload(QuicStringPiece payload) override { + return stream_->OnUnknownFramePayload(payload); } - bool OnUnknownFrameEnd() override { return true; } + bool OnUnknownFrameEnd() override { return stream_->OnUnknownFrameEnd(); } private: - void CloseConnectionOnWrongFrame(std::string frame_type) { + void CloseConnectionOnWrongFrame(QuicStringPiece frame_type) { stream_->session()->connection()->CloseConnection( - QUIC_HTTP_DECODER_ERROR, frame_type + " frame received on data stream", + QUIC_HTTP_DECODER_ERROR, + QuicStrCat(frame_type, " frame received on data stream"), ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); } @@ -185,7 +193,6 @@ QuicSpdyStream::QuicSpdyStream(QuicStreamId id, trailers_decompressed_(false), trailers_consumed_(false), priority_sent_(false), - headers_bytes_to_be_marked_consumed_(0), http_decoder_visitor_(QuicMakeUnique<HttpDecoderVisitor>(this)), decoder_(http_decoder_visitor_.get()), sequencer_offset_(0), @@ -221,7 +228,6 @@ QuicSpdyStream::QuicSpdyStream(PendingStream* pending, trailers_decompressed_(false), trailers_consumed_(false), priority_sent_(false), - headers_bytes_to_be_marked_consumed_(0), http_decoder_visitor_(QuicMakeUnique<HttpDecoderVisitor>(this)), decoder_(http_decoder_visitor_.get()), sequencer_offset_(sequencer()->NumBytesConsumed()), @@ -344,6 +350,35 @@ size_t QuicSpdyStream::WriteTrailers( return bytes_written; } +void QuicSpdyStream::WritePushPromise(const PushPromiseFrame& frame) { + DCHECK(VersionUsesQpack(transport_version())); + std::unique_ptr<char[]> push_promise_frame_with_id; + const size_t push_promise_frame_length = + encoder_.SerializePushPromiseFrameWithOnlyPushId( + frame, &push_promise_frame_with_id); + + unacked_frame_headers_offsets_.Add(send_buffer().stream_offset(), + send_buffer().stream_offset() + + push_promise_frame_length + + frame.headers.length()); + + // Write Push Promise frame header and push id. + QUIC_DLOG(INFO) << "Stream " << id() + << " is writing Push Promise frame header of length " + << push_promise_frame_length << " , with promised id " + << frame.push_id; + WriteOrBufferData(QuicStringPiece(push_promise_frame_with_id.get(), + push_promise_frame_length), + /* fin = */ false, /* ack_listener = */ nullptr); + + // Write response headers. + QUIC_DLOG(INFO) << "Stream " << id() + << " is writing Push Promise request header of length " + << frame.headers.length(); + WriteOrBufferData(frame.headers, /* fin = */ false, + /* ack_listener = */ nullptr); +} + QuicConsumedData QuicSpdyStream::WritevBody(const struct iovec* iov, int count, bool fin) { @@ -396,13 +431,7 @@ size_t QuicSpdyStream::Readv(const struct iovec* iov, size_t iov_len) { return sequencer()->Readv(iov, iov_len); } size_t bytes_read = 0; - sequencer()->MarkConsumed(body_buffer_.ReadBody(iov, iov_len, &bytes_read)); - - if (VersionUsesQpack(transport_version())) { - // Maybe all DATA frame bytes have been read and some trailing HEADERS had - // already been processed, in which case MarkConsumed() should be called. - MaybeMarkHeadersBytesConsumed(); - } + sequencer()->MarkConsumed(body_manager_.ReadBody(iov, iov_len, &bytes_read)); return bytes_read; } @@ -412,7 +441,7 @@ int QuicSpdyStream::GetReadableRegions(iovec* iov, size_t iov_len) const { if (!VersionHasDataFrameHeader(transport_version())) { return sequencer()->GetReadableRegions(iov, iov_len); } - return body_buffer_.PeekBody(iov, iov_len); + return body_manager_.PeekBody(iov, iov_len); } void QuicSpdyStream::MarkConsumed(size_t num_bytes) { @@ -421,13 +450,8 @@ void QuicSpdyStream::MarkConsumed(size_t num_bytes) { sequencer()->MarkConsumed(num_bytes); return; } - sequencer()->MarkConsumed(body_buffer_.OnBodyConsumed(num_bytes)); - if (VersionUsesQpack(transport_version())) { - // Maybe all DATA frame bytes have been read and some trailing HEADERS had - // already been processed, in which case MarkConsumed() should be called. - MaybeMarkHeadersBytesConsumed(); - } + sequencer()->MarkConsumed(body_manager_.OnBodyConsumed(num_bytes)); } bool QuicSpdyStream::IsDoneReading() const { @@ -441,7 +465,7 @@ bool QuicSpdyStream::HasBytesToRead() const { if (!VersionHasDataFrameHeader(transport_version())) { return sequencer()->HasBytesToRead(); } - return body_buffer_.HasBytesToRead(); + return body_manager_.HasBytesToRead(); } void QuicSpdyStream::MarkTrailersConsumed() { @@ -450,7 +474,7 @@ void QuicSpdyStream::MarkTrailersConsumed() { uint64_t QuicSpdyStream::total_body_bytes_read() const { if (VersionHasDataFrameHeader(transport_version())) { - return body_buffer_.total_body_bytes_received(); + return body_manager_.total_body_bytes_received(); } return sequencer()->NumBytesConsumed(); } @@ -467,7 +491,7 @@ void QuicSpdyStream::ConsumeHeaderList() { return; } - if (body_buffer_.HasBytesToRead()) { + if (body_manager_.HasBytesToRead()) { OnBodyAvailable(); return; } @@ -479,9 +503,10 @@ void QuicSpdyStream::ConsumeHeaderList() { } } -void QuicSpdyStream::OnStreamHeadersPriority(SpdyPriority priority) { +void QuicSpdyStream::OnStreamHeadersPriority( + const spdy::SpdyStreamPrecedence& precedence) { DCHECK_EQ(Perspective::IS_SERVER, session()->connection()->perspective()); - SetPriority(priority); + SetPriority(precedence); } void QuicSpdyStream::OnStreamHeaderList(bool fin, @@ -555,14 +580,9 @@ void QuicSpdyStream::OnInitialHeadersComplete( return; } - if (fin) { - if (rst_sent() && - GetQuicReloadableFlag(quic_avoid_empty_frame_after_empty_headers)) { - QUIC_RELOADABLE_FLAG_COUNT(quic_avoid_empty_frame_after_empty_headers); - } else { - OnStreamFrame( - QuicStreamFrame(id(), fin, /* offset = */ 0, QuicStringPiece())); - } + if (fin && !rst_sent()) { + OnStreamFrame( + QuicStreamFrame(id(), fin, /* offset = */ 0, QuicStringPiece())); } if (FinishedReadingHeaders()) { sequencer()->SetUnblocked(); @@ -623,9 +643,10 @@ void QuicSpdyStream::OnTrailingHeadersComplete( } } -void QuicSpdyStream::OnPriorityFrame(SpdyPriority priority) { +void QuicSpdyStream::OnPriorityFrame( + const spdy::SpdyStreamPrecedence& precedence) { DCHECK_EQ(Perspective::IS_SERVER, session()->connection()->perspective()); - SetPriority(priority); + SetPriority(precedence); } void QuicSpdyStream::OnStreamReset(const QuicRstStreamFrame& frame) { @@ -684,7 +705,7 @@ void QuicSpdyStream::OnDataAvailable() { return; } - if (body_buffer_.HasBytesToRead()) { + if (body_manager_.HasBytesToRead()) { OnBodyAvailable(); return; } @@ -759,7 +780,7 @@ void QuicSpdyStream::ClearSession() { spdy_session_ = nullptr; } -bool QuicSpdyStream::OnDataFrameStart(Http3FrameLengths frame_lengths) { +bool QuicSpdyStream::OnDataFrameStart(QuicByteCount header_length) { DCHECK(VersionHasDataFrameHeader(transport_version())); if (!headers_decompressed_ || trailers_decompressed_) { // TODO(b/124216424): Change error code to HTTP_UNEXPECTED_FRAME. @@ -769,21 +790,23 @@ bool QuicSpdyStream::OnDataFrameStart(Http3FrameLengths frame_lengths) { return false; } - body_buffer_.OnDataHeader(frame_lengths); + sequencer()->MarkConsumed(body_manager_.OnNonBody(header_length)); + return true; } bool QuicSpdyStream::OnDataFramePayload(QuicStringPiece payload) { DCHECK(VersionHasDataFrameHeader(transport_version())); - body_buffer_.OnDataPayload(payload); + body_manager_.OnBody(payload); + return true; } bool QuicSpdyStream::OnDataFrameEnd() { DCHECK(VersionHasDataFrameHeader(transport_version())); QUIC_DVLOG(1) << "Reaches the end of a data frame. Total bytes received are " - << body_buffer_.total_body_bytes_received(); + << body_manager_.total_body_bytes_received(); return true; } @@ -822,16 +845,6 @@ void QuicSpdyStream::OnStreamFrameRetransmitted(QuicStreamOffset offset, } } -void QuicSpdyStream::MaybeMarkHeadersBytesConsumed() { - DCHECK(VersionUsesQpack(transport_version())); - - if (!body_buffer_.HasBytesToRead() && !reading_stopped() && - headers_bytes_to_be_marked_consumed_ > 0) { - sequencer()->MarkConsumed(headers_bytes_to_be_marked_consumed_); - headers_bytes_to_be_marked_consumed_ = 0; - } -} - QuicByteCount QuicSpdyStream::GetNumFrameHeadersInInterval( QuicStreamOffset offset, QuicByteCount data_length) const { @@ -844,7 +857,7 @@ QuicByteCount QuicSpdyStream::GetNumFrameHeadersInInterval( return header_acked_length; } -bool QuicSpdyStream::OnHeadersFrameStart(Http3FrameLengths frame_length) { +bool QuicSpdyStream::OnHeadersFrameStart(QuicByteCount header_length) { DCHECK(VersionUsesQpack(transport_version())); DCHECK(!qpack_decoded_headers_accumulator_); @@ -857,31 +870,28 @@ bool QuicSpdyStream::OnHeadersFrameStart(Http3FrameLengths frame_length) { return false; } - if (headers_decompressed_) { - trailers_payload_length_ = frame_length.payload_length; - } else { - headers_payload_length_ = frame_length.payload_length; - } + sequencer()->MarkConsumed(body_manager_.OnNonBody(header_length)); qpack_decoded_headers_accumulator_ = QuicMakeUnique<QpackDecodedHeadersAccumulator>( id(), spdy_session_->qpack_decoder(), this, spdy_session_->max_inbound_header_list_size()); - // Do not call MaybeMarkHeadersBytesConsumed() yet, because - // HEADERS frame header bytes might not have been parsed completely. - headers_bytes_to_be_marked_consumed_ += frame_length.header_length; - return true; } bool QuicSpdyStream::OnHeadersFramePayload(QuicStringPiece payload) { DCHECK(VersionUsesQpack(transport_version())); + if (headers_decompressed_) { + trailers_payload_length_ += payload.length(); + } else { + headers_payload_length_ += payload.length(); + } + const bool success = qpack_decoded_headers_accumulator_->Decode(payload); - headers_bytes_to_be_marked_consumed_ += payload.size(); - MaybeMarkHeadersBytesConsumed(); + sequencer()->MarkConsumed(body_manager_.OnNonBody(payload.size())); if (!success) { // TODO(124216424): Use HTTP_QPACK_DECOMPRESSION_FAILED error code. @@ -919,11 +929,69 @@ bool QuicSpdyStream::OnHeadersFrameEnd() { return !sequencer()->IsClosed() && !reading_stopped(); } +bool QuicSpdyStream::OnPushPromiseFrameStart(PushId push_id, + QuicByteCount header_length, + QuicByteCount push_id_length) { + DCHECK(VersionHasStreamType(transport_version())); + DCHECK(!qpack_decoded_headers_accumulator_); + + // TODO(renjietang): Check max push id and handle errors. + spdy_session_->OnPushPromise(id(), push_id); + sequencer()->MarkConsumed( + body_manager_.OnNonBody(header_length + push_id_length)); + + qpack_decoded_headers_accumulator_ = + QuicMakeUnique<QpackDecodedHeadersAccumulator>( + id(), spdy_session_->qpack_decoder(), this, + spdy_session_->max_inbound_header_list_size()); + + return true; +} + +bool QuicSpdyStream::OnPushPromiseFramePayload(QuicStringPiece payload) { + spdy_session_->OnCompressedFrameSize(payload.length()); + return OnHeadersFramePayload(payload); +} + +bool QuicSpdyStream::OnPushPromiseFrameEnd() { + DCHECK(VersionUsesQpack(transport_version())); + + return OnHeadersFrameEnd(); +} + +bool QuicSpdyStream::OnUnknownFrameStart(uint64_t frame_type, + QuicByteCount header_length) { + // Ignore unknown frames, but consume frame header. + QUIC_DVLOG(1) << "Discarding " << header_length + << " byte long frame header of frame of unknown type " + << frame_type << "."; + sequencer()->MarkConsumed(body_manager_.OnNonBody(header_length)); + return true; +} + +bool QuicSpdyStream::OnUnknownFramePayload(QuicStringPiece payload) { + // Ignore unknown frames, but consume frame payload. + QUIC_DVLOG(1) << "Discarding " << payload.size() + << " bytes of payload of frame of unknown type."; + sequencer()->MarkConsumed(body_manager_.OnNonBody(payload.size())); + return true; +} + +bool QuicSpdyStream::OnUnknownFrameEnd() { + return true; +} + void QuicSpdyStream::ProcessDecodedHeaders(const QuicHeaderList& headers) { - const QuicByteCount frame_length = headers_decompressed_ - ? trailers_payload_length_ - : headers_payload_length_; - OnStreamHeaderList(/* fin = */ false, frame_length, headers); + if (spdy_session_->promised_stream_id() == + QuicUtils::GetInvalidStreamId( + session()->connection()->transport_version())) { + const QuicByteCount frame_length = headers_decompressed_ + ? trailers_payload_length_ + : headers_payload_length_; + OnStreamHeaderList(/* fin = */ false, frame_length, headers); + } else { + spdy_session_->OnHeaderList(headers); + } qpack_decoded_headers_accumulator_.reset(); } @@ -933,7 +1001,7 @@ size_t QuicSpdyStream::WriteHeadersImpl( QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) { if (!VersionUsesQpack(transport_version())) { return spdy_session_->WriteHeadersOnHeadersStream( - id(), std::move(header_block), fin, priority(), + id(), std::move(header_block), fin, precedence(), std::move(ack_listener)); } @@ -946,7 +1014,7 @@ size_t QuicSpdyStream::WriteHeadersImpl( // Encode header list. std::string encoded_headers = - spdy_session_->qpack_encoder()->EncodeHeaderList(id(), &header_block); + spdy_session_->qpack_encoder()->EncodeHeaderList(id(), header_block); // Write HEADERS frame. std::unique_ptr<char[]> headers_frame_header; @@ -973,7 +1041,7 @@ size_t QuicSpdyStream::WriteHeadersImpl( } void QuicSpdyStream::PopulatePriorityFrame(PriorityFrame* frame) { - frame->weight = priority(); + frame->weight = precedence().spdy3_priority(); frame->dependency_type = ROOT_OF_TREE; frame->prioritized_type = REQUEST_STREAM; frame->prioritized_element_id = id(); diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream.h b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream.h index bdb87529d1c..42f76bb4e49 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream.h +++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream.h @@ -18,7 +18,7 @@ #include "net/third_party/quiche/src/quic/core/http/http_decoder.h" #include "net/third_party/quiche/src/quic/core/http/http_encoder.h" #include "net/third_party/quiche/src/quic/core/http/quic_header_list.h" -#include "net/third_party/quiche/src/quic/core/http/quic_spdy_stream_body_buffer.h" +#include "net/third_party/quiche/src/quic/core/http/quic_spdy_stream_body_manager.h" #include "net/third_party/quiche/src/quic/core/qpack/qpack_decoded_headers_accumulator.h" #include "net/third_party/quiche/src/quic/core/quic_packets.h" #include "net/third_party/quiche/src/quic/core/quic_stream.h" @@ -78,7 +78,8 @@ class QUIC_EXPORT_PRIVATE QuicSpdyStream // Called by the session when headers with a priority have been received // for this stream. This method will only be called for server streams. - virtual void OnStreamHeadersPriority(spdy::SpdyPriority priority); + virtual void OnStreamHeadersPriority( + const spdy::SpdyStreamPrecedence& precedence); // Called by the session when decompressed headers have been completely // delivered to this stream. If |fin| is true, then this stream @@ -95,7 +96,7 @@ class QUIC_EXPORT_PRIVATE QuicSpdyStream // Called by the session when a PRIORITY frame has been been received for this // stream. This method will only be called for server streams. - void OnPriorityFrame(spdy::SpdyPriority priority); + void OnPriorityFrame(const spdy::SpdyStreamPrecedence& precedence); // Override the base class to not discard response when receiving // QUIC_STREAM_NO_ERROR. @@ -125,6 +126,9 @@ class QUIC_EXPORT_PRIVATE QuicSpdyStream spdy::SpdyHeaderBlock trailer_block, QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener); + // Serializes |frame| and writes the encoded push promise data. + void WritePushPromise(const PushPromiseFrame& frame); + // Override to report newly acked bytes via ack_listener_. bool OnStreamFrameAcked(QuicStreamOffset offset, QuicByteCount data_length, @@ -139,12 +143,10 @@ class QUIC_EXPORT_PRIVATE QuicSpdyStream // Does the same thing as WriteOrBufferBody except this method takes iovec // as the data input. Right now it only calls WritevData. - // TODO(renjietang): Write data frame header before writing body. QuicConsumedData WritevBody(const struct iovec* iov, int count, bool fin); // Does the same thing as WriteOrBufferBody except this method takes // memslicespan as the data input. Right now it only calls WriteMemSlices. - // TODO(renjietang): Write data frame header before writing body. QuicConsumedData WriteBodySlices(QuicMemSliceSpan slices, bool fin); // Marks the trailers as consumed. This applies to the case where this object @@ -248,20 +250,24 @@ class QUIC_EXPORT_PRIVATE QuicSpdyStream class HttpDecoderVisitor; // Called by HttpDecoderVisitor. - bool OnDataFrameStart(Http3FrameLengths frame_lengths); + bool OnDataFrameStart(QuicByteCount header_length); bool OnDataFramePayload(QuicStringPiece payload); bool OnDataFrameEnd(); - bool OnHeadersFrameStart(Http3FrameLengths frame_length); + bool OnHeadersFrameStart(QuicByteCount header_length); bool OnHeadersFramePayload(QuicStringPiece payload); bool OnHeadersFrameEnd(); + bool OnPushPromiseFrameStart(PushId push_id, + QuicByteCount header_length, + QuicByteCount push_id_length); + bool OnPushPromiseFramePayload(QuicStringPiece payload); + bool OnPushPromiseFrameEnd(); + bool OnUnknownFrameStart(uint64_t frame_type, QuicByteCount header_length); + bool OnUnknownFramePayload(QuicStringPiece payload); + bool OnUnknownFrameEnd(); // Called internally when headers are decoded. void ProcessDecodedHeaders(const QuicHeaderList& headers); - // Call QuicStreamSequencer::MarkConsumed() with - // |headers_bytes_to_be_marked_consumed_| if appropriate. - void MaybeMarkHeadersBytesConsumed(); - // Given the interval marked by [|offset|, |offset| + |data_length|), return // the number of frame header bytes contained in it. QuicByteCount GetNumFrameHeadersInInterval(QuicStreamOffset offset, @@ -294,9 +300,6 @@ class QUIC_EXPORT_PRIVATE QuicSpdyStream // True if the stream has already sent an priority frame. bool priority_sent_; - // Number of bytes consumed while decoding HEADERS frames that cannot be - // marked consumed in QuicStreamSequencer until later. - QuicByteCount headers_bytes_to_be_marked_consumed_; // The parsed trailers received from the peer. spdy::SpdyHeaderBlock received_trailers_; @@ -309,8 +312,10 @@ class QUIC_EXPORT_PRIVATE QuicSpdyStream std::unique_ptr<HttpDecoderVisitor> http_decoder_visitor_; // HttpDecoder for processing raw incoming stream frames. HttpDecoder decoder_; - // Buffer that contains decoded data of the stream. - QuicSpdyStreamBodyBuffer body_buffer_; + // Object that manages references to DATA frame payload fragments buffered by + // the sequencer and calculates how much data should be marked consumed with + // the sequencer each time new stream data is processed. + QuicSpdyStreamBodyManager body_manager_; // Sequencer offset keeping track of how much data HttpDecoder has processed. // Initial value is zero for fresh streams, or sequencer()->NumBytesConsumed() diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream_body_buffer.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream_body_buffer.cc deleted file mode 100644 index c29c766ad59..00000000000 --- a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream_body_buffer.cc +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright (c) 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "net/third_party/quiche/src/quic/core/http/quic_spdy_stream_body_buffer.h" - -#include <algorithm> - -#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" - -namespace quic { - -QuicSpdyStreamBodyBuffer::QuicSpdyStreamBodyBuffer() - : bytes_remaining_(0), - total_body_bytes_readable_(0), - total_body_bytes_received_(0), - total_payload_lengths_(0) {} - -void QuicSpdyStreamBodyBuffer::OnDataHeader(Http3FrameLengths frame_lengths) { - frame_meta_.push_back(frame_lengths); - total_payload_lengths_ += frame_lengths.payload_length; -} - -void QuicSpdyStreamBodyBuffer::OnDataPayload(QuicStringPiece payload) { - DCHECK(!payload.empty()); - bodies_.push_back(payload); - total_body_bytes_received_ += payload.length(); - total_body_bytes_readable_ += payload.length(); - DCHECK_LE(total_body_bytes_received_, total_payload_lengths_); -} - -size_t QuicSpdyStreamBodyBuffer::OnBodyConsumed(size_t num_bytes) { - // Check if the stream has enough decoded data. - if (num_bytes > total_body_bytes_readable_) { - QUIC_BUG << "Invalid argument to OnBodyConsumed." - << " expect to consume: " << num_bytes - << ", but not enough bytes available. " - << "Total bytes readable are: " << total_body_bytes_readable_; - return 0; - } - // Discard references in the stream before the sequencer marks them consumed. - size_t remaining = num_bytes; - while (remaining > 0) { - if (bodies_.empty()) { - QUIC_BUG << "Failed to consume because body buffer is empty."; - return 0; - } - auto body = bodies_.front(); - bodies_.pop_front(); - if (body.length() <= remaining) { - remaining -= body.length(); - } else { - body = body.substr(remaining, body.length() - remaining); - bodies_.push_front(body); - remaining = 0; - } - } - - // Consume headers. - size_t bytes_to_consume = 0; - while (bytes_remaining_ < num_bytes) { - if (frame_meta_.empty()) { - QUIC_BUG << "Faild to consume because frame header buffer is empty."; - return 0; - } - auto meta = frame_meta_.front(); - frame_meta_.pop_front(); - bytes_remaining_ += meta.payload_length; - bytes_to_consume += meta.header_length; - } - bytes_to_consume += num_bytes; - - // Update accountings. - bytes_remaining_ -= num_bytes; - total_body_bytes_readable_ -= num_bytes; - - return bytes_to_consume; -} - -int QuicSpdyStreamBodyBuffer::PeekBody(iovec* iov, size_t iov_len) const { - DCHECK(iov != nullptr); - DCHECK_GT(iov_len, 0u); - - if (bodies_.empty()) { - iov[0].iov_base = nullptr; - iov[0].iov_len = 0; - return 0; - } - // Fill iovs with references from the stream. - size_t iov_filled = 0; - while (iov_filled < bodies_.size() && iov_filled < iov_len) { - QuicStringPiece body = bodies_[iov_filled]; - iov[iov_filled].iov_base = const_cast<char*>(body.data()); - iov[iov_filled].iov_len = body.size(); - iov_filled++; - } - return iov_filled; -} - -size_t QuicSpdyStreamBodyBuffer::ReadBody(const struct iovec* iov, - size_t iov_len, - size_t* total_bytes_read) { - *total_bytes_read = 0; - QuicByteCount total_remaining = total_body_bytes_readable_; - size_t index = 0; - size_t src_offset = 0; - for (size_t i = 0; i < iov_len && total_remaining > 0; ++i) { - char* dest = reinterpret_cast<char*>(iov[i].iov_base); - size_t dest_remaining = iov[i].iov_len; - while (dest_remaining > 0 && total_remaining > 0) { - auto body = bodies_[index]; - size_t bytes_to_copy = - std::min<size_t>(body.length() - src_offset, dest_remaining); - memcpy(dest, body.substr(src_offset, bytes_to_copy).data(), - bytes_to_copy); - dest += bytes_to_copy; - dest_remaining -= bytes_to_copy; - *total_bytes_read += bytes_to_copy; - total_remaining -= bytes_to_copy; - if (bytes_to_copy < body.length() - src_offset) { - src_offset += bytes_to_copy; - } else { - index++; - src_offset = 0; - } - } - } - - return OnBodyConsumed(*total_bytes_read); -} - -} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream_body_buffer.h b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream_body_buffer.h deleted file mode 100644 index a37f3bf0b80..00000000000 --- a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream_body_buffer.h +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright (c) 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef QUICHE_QUIC_CORE_HTTP_QUIC_SPDY_STREAM_BODY_BUFFER_H_ -#define QUICHE_QUIC_CORE_HTTP_QUIC_SPDY_STREAM_BODY_BUFFER_H_ - -#include "net/third_party/quiche/src/quic/core/http/http_decoder.h" -#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" -#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h" -#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" -#include "net/third_party/quiche/src/quic/platform/api/quic_iovec.h" -#include "net/third_party/quiche/src/quic/platform/api/quic_macros.h" - -namespace quic { - -class QuicStreamSequencer; - -// Buffer decoded body for QuicSpdyStream. It also talks to the sequencer to -// consume data. -class QUIC_EXPORT_PRIVATE QuicSpdyStreamBodyBuffer { - public: - QuicSpdyStreamBodyBuffer(); - ~QuicSpdyStreamBodyBuffer() = default; - - // Add metadata of the frame to accountings. - // Called when QuicSpdyStream receives data frame header. - void OnDataHeader(Http3FrameLengths frame_lengths); - - // Add new data payload to buffer. - // Called when QuicSpdyStream received data payload. - // Data pointed by payload must be alive until consumed by - // QuicStreamSequencer::MarkConsumed(). - void OnDataPayload(QuicStringPiece payload); - - // Internally marks |num_bytes| of DATA frame payload consumed, and returns - // the number of bytes that the caller should mark consumed with the - // sequencer, including DATA frame header bytes, if any. - QUIC_MUST_USE_RESULT size_t OnBodyConsumed(size_t num_bytes); - - // Fill up to |iov_len| with bodies available in buffer. No data is consumed. - // |iov|.iov_base will point to data in the buffer, and |iov|.iov_len will - // be set to the underlying data length accordingly. - // Returns the number of iov used. - int PeekBody(iovec* iov, size_t iov_len) const; - - // Copies from buffer into |iov| up to |iov_len|, and consume data in - // sequencer. |iov.iov_base| and |iov.iov_len| are preassigned and will not be - // changed. |*total_bytes_read| is set to the number of bytes read. Returns - // the number of bytes that should be marked consumed with the sequencer. - QUIC_MUST_USE_RESULT size_t ReadBody(const struct iovec* iov, - size_t iov_len, - size_t* total_bytes_read); - - bool HasBytesToRead() const { return !bodies_.empty(); } - - uint64_t total_body_bytes_received() const { - return total_body_bytes_received_; - } - - private: - // Storage for decoded data. - QuicDeque<QuicStringPiece> bodies_; - // Storage for header lengths. - QuicDeque<Http3FrameLengths> frame_meta_; - // Bytes in the first available data frame that are not consumed yet. - QuicByteCount bytes_remaining_; - // Total available body data in the stream. - QuicByteCount total_body_bytes_readable_; - // Total bytes read from the stream excluding headers. - QuicByteCount total_body_bytes_received_; - // Total length of payloads tracked by frame_meta_. - QuicByteCount total_payload_lengths_; -}; - -} // namespace quic - -#endif // QUICHE_QUIC_CORE_HTTP_QUIC_SPDY_STREAM_BODY_BUFFER_H_ diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream_body_buffer_test.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream_body_buffer_test.cc deleted file mode 100644 index d5e816119d9..00000000000 --- a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream_body_buffer_test.cc +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright (c) 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "net/third_party/quiche/src/quic/core/http/quic_spdy_stream_body_buffer.h" - -#include <string> - -#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h" -#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" -#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" - -namespace quic { - -namespace test { - -namespace { - -class QuicSpdyStreamBodyBufferTest : public QuicTest { - protected: - QuicSpdyStreamBodyBuffer body_buffer_; -}; - -TEST_F(QuicSpdyStreamBodyBufferTest, ReceiveBodies) { - std::string body(1024, 'a'); - EXPECT_FALSE(body_buffer_.HasBytesToRead()); - body_buffer_.OnDataHeader(Http3FrameLengths(3, 1024)); - body_buffer_.OnDataPayload(body); - EXPECT_EQ(1024u, body_buffer_.total_body_bytes_received()); - EXPECT_TRUE(body_buffer_.HasBytesToRead()); -} - -TEST_F(QuicSpdyStreamBodyBufferTest, PeekBody) { - std::string body(1024, 'a'); - body_buffer_.OnDataHeader(Http3FrameLengths(3, 1024)); - body_buffer_.OnDataPayload(body); - EXPECT_EQ(1024u, body_buffer_.total_body_bytes_received()); - iovec vec; - EXPECT_EQ(1, body_buffer_.PeekBody(&vec, 1)); - EXPECT_EQ(1024u, vec.iov_len); - EXPECT_EQ(body, - QuicStringPiece(static_cast<const char*>(vec.iov_base), 1024)); -} - -// Buffer only receives 1 frame. Stream consumes less or equal than a frame. -TEST_F(QuicSpdyStreamBodyBufferTest, MarkConsumedPartialSingleFrame) { - testing::InSequence seq; - std::string body(1024, 'a'); - const QuicByteCount header_length = 3; - Http3FrameLengths lengths(header_length, 1024); - body_buffer_.OnDataHeader(lengths); - body_buffer_.OnDataPayload(body); - EXPECT_EQ(header_length + 1024, body_buffer_.OnBodyConsumed(1024)); -} - -// Buffer received 2 frames. Stream consumes multiple times. -TEST_F(QuicSpdyStreamBodyBufferTest, MarkConsumedMultipleFrames) { - testing::InSequence seq; - // 1st frame. - std::string body1(1024, 'a'); - const QuicByteCount header_length1 = 2; - Http3FrameLengths lengths1(header_length1, 1024); - body_buffer_.OnDataHeader(lengths1); - body_buffer_.OnDataPayload(body1); - - // 2nd frame. - std::string body2(2048, 'b'); - const QuicByteCount header_length2 = 4; - Http3FrameLengths lengths2(header_length2, 2048); - body_buffer_.OnDataHeader(lengths2); - body_buffer_.OnDataPayload(body2); - - EXPECT_EQ(header_length1 + 512, body_buffer_.OnBodyConsumed(512)); - EXPECT_EQ(header_length2 + 2048, body_buffer_.OnBodyConsumed(2048)); - EXPECT_EQ(512u, body_buffer_.OnBodyConsumed(512)); -} - -TEST_F(QuicSpdyStreamBodyBufferTest, MarkConsumedMoreThanBuffered) { - std::string body(1024, 'a'); - Http3FrameLengths lengths(3, 1024); - body_buffer_.OnDataHeader(lengths); - body_buffer_.OnDataPayload(body); - size_t bytes_to_consume = 0; - EXPECT_QUIC_BUG( - bytes_to_consume = body_buffer_.OnBodyConsumed(2048), - "Invalid argument to OnBodyConsumed. expect to consume: 2048, but not " - "enough bytes available. Total bytes readable are: 1024"); - EXPECT_EQ(0u, bytes_to_consume); -} - -// Buffer receives 1 frame. Stream read from the buffer. -TEST_F(QuicSpdyStreamBodyBufferTest, ReadSingleBody) { - testing::InSequence seq; - std::string body(1024, 'a'); - const QuicByteCount header_length = 2; - Http3FrameLengths lengths(header_length, 1024); - body_buffer_.OnDataHeader(lengths); - body_buffer_.OnDataPayload(body); - - char base[1024]; - iovec iov = {&base[0], 1024}; - size_t total_bytes_read = 0; - EXPECT_EQ(header_length + 1024, - body_buffer_.ReadBody(&iov, 1, &total_bytes_read)); - EXPECT_EQ(1024u, total_bytes_read); - EXPECT_EQ(1024u, iov.iov_len); - EXPECT_EQ(body, - QuicStringPiece(static_cast<const char*>(iov.iov_base), 1024)); -} - -// Buffer receives 2 frames, stream read from the buffer multiple times. -TEST_F(QuicSpdyStreamBodyBufferTest, ReadMultipleBody) { - testing::InSequence seq; - // 1st frame. - std::string body1(1024, 'a'); - const QuicByteCount header_length1 = 2; - Http3FrameLengths lengths1(header_length1, 1024); - body_buffer_.OnDataHeader(lengths1); - body_buffer_.OnDataPayload(body1); - - // 2nd frame. - std::string body2(2048, 'b'); - const QuicByteCount header_length2 = 4; - Http3FrameLengths lengths2(header_length2, 2048); - body_buffer_.OnDataHeader(lengths2); - body_buffer_.OnDataPayload(body2); - - // First read of 512 bytes. - char base[512]; - iovec iov = {&base[0], 512}; - size_t total_bytes_read = 0; - EXPECT_EQ(header_length1 + 512, - body_buffer_.ReadBody(&iov, 1, &total_bytes_read)); - EXPECT_EQ(512u, total_bytes_read); - EXPECT_EQ(512u, iov.iov_len); - EXPECT_EQ(body1.substr(0, 512), - QuicStringPiece(static_cast<const char*>(iov.iov_base), 512)); - - // Second read of 2048 bytes. - char base2[2048]; - iovec iov2 = {&base2[0], 2048}; - EXPECT_EQ(header_length2 + 2048, - body_buffer_.ReadBody(&iov2, 1, &total_bytes_read)); - EXPECT_EQ(2048u, total_bytes_read); - EXPECT_EQ(2048u, iov2.iov_len); - EXPECT_EQ(body1.substr(512, 512) + body2.substr(0, 1536), - QuicStringPiece(static_cast<const char*>(iov2.iov_base), 2048)); - - // Third read of the rest 512 bytes. - char base3[512]; - iovec iov3 = {&base3[0], 512}; - EXPECT_EQ(512u, body_buffer_.ReadBody(&iov3, 1, &total_bytes_read)); - EXPECT_EQ(512u, total_bytes_read); - EXPECT_EQ(512u, iov3.iov_len); - EXPECT_EQ(body2.substr(1536, 512), - QuicStringPiece(static_cast<const char*>(iov3.iov_base), 512)); -} - -} // anonymous namespace - -} // namespace test - -} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream_body_manager.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream_body_manager.cc new file mode 100644 index 00000000000..798f55142ca --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream_body_manager.cc @@ -0,0 +1,139 @@ +// Copyright (c) 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/third_party/quiche/src/quic/core/http/quic_spdy_stream_body_manager.h" + +#include <algorithm> + +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" + +namespace quic { + +QuicSpdyStreamBodyManager::QuicSpdyStreamBodyManager() + : total_body_bytes_received_(0) {} + +size_t QuicSpdyStreamBodyManager::OnNonBody(QuicByteCount length) { + DCHECK_NE(0u, length); + + if (fragments_.empty()) { + // Non-body bytes can be consumed immediately, because all previously + // received body bytes have been read. + return length; + } + + // Non-body bytes will be consumed after last body fragment is read. + fragments_.back().trailing_non_body_byte_count += length; + return 0; +} + +void QuicSpdyStreamBodyManager::OnBody(QuicStringPiece body) { + DCHECK(!body.empty()); + + fragments_.push_back({body, 0}); + total_body_bytes_received_ += body.length(); +} + +size_t QuicSpdyStreamBodyManager::OnBodyConsumed(size_t num_bytes) { + QuicByteCount bytes_to_consume = 0; + size_t remaining_bytes = num_bytes; + + while (remaining_bytes > 0) { + if (fragments_.empty()) { + QUIC_BUG << "Not enough available body to consume."; + return 0; + } + + Fragment& fragment = fragments_.front(); + const QuicStringPiece body = fragment.body; + + if (body.length() > remaining_bytes) { + // Consume leading |remaining_bytes| bytes of body. + bytes_to_consume += remaining_bytes; + fragment.body = body.substr(remaining_bytes); + return bytes_to_consume; + } + + // Consume entire fragment and the following + // |trailing_non_body_byte_count| bytes. + remaining_bytes -= body.length(); + bytes_to_consume += body.length() + fragment.trailing_non_body_byte_count; + fragments_.pop_front(); + } + + return bytes_to_consume; +} + +int QuicSpdyStreamBodyManager::PeekBody(iovec* iov, size_t iov_len) const { + DCHECK(iov); + DCHECK_GT(iov_len, 0u); + + // TODO(bnc): Is this really necessary? + if (fragments_.empty()) { + iov[0].iov_base = nullptr; + iov[0].iov_len = 0; + return 0; + } + + size_t iov_filled = 0; + while (iov_filled < fragments_.size() && iov_filled < iov_len) { + QuicStringPiece body = fragments_[iov_filled].body; + iov[iov_filled].iov_base = const_cast<char*>(body.data()); + iov[iov_filled].iov_len = body.size(); + iov_filled++; + } + + return iov_filled; +} + +size_t QuicSpdyStreamBodyManager::ReadBody(const struct iovec* iov, + size_t iov_len, + size_t* total_bytes_read) { + *total_bytes_read = 0; + QuicByteCount bytes_to_consume = 0; + + // The index of iovec to write to. + size_t index = 0; + // Address to write to within current iovec. + char* dest = reinterpret_cast<char*>(iov[index].iov_base); + // Remaining space in current iovec. + size_t dest_remaining = iov[index].iov_len; + + while (!fragments_.empty()) { + Fragment& fragment = fragments_.front(); + const QuicStringPiece body = fragment.body; + + const size_t bytes_to_copy = + std::min<size_t>(body.length(), dest_remaining); + memcpy(dest, body.data(), bytes_to_copy); + bytes_to_consume += bytes_to_copy; + *total_bytes_read += bytes_to_copy; + + if (bytes_to_copy == body.length()) { + // Entire fragment read. + bytes_to_consume += fragment.trailing_non_body_byte_count; + fragments_.pop_front(); + } else { + // Consume leading |bytes_to_copy| bytes of body. + fragment.body = body.substr(bytes_to_copy); + } + + if (bytes_to_copy == dest_remaining) { + // Current iovec full. + ++index; + if (index == iov_len) { + break; + } + dest = reinterpret_cast<char*>(iov[index].iov_base); + dest_remaining = iov[index].iov_len; + } else { + // Advance destination parameters within this iovec. + dest += bytes_to_copy; + dest_remaining -= bytes_to_copy; + } + } + + return bytes_to_consume; +} + +} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream_body_manager.h b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream_body_manager.h new file mode 100644 index 00000000000..6b2dd62b02d --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream_body_manager.h @@ -0,0 +1,94 @@ +// Copyright (c) 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef QUICHE_QUIC_CORE_HTTP_QUIC_SPDY_STREAM_BODY_MANAGER_H_ +#define QUICHE_QUIC_CORE_HTTP_QUIC_SPDY_STREAM_BODY_MANAGER_H_ + +#include "net/third_party/quiche/src/quic/core/quic_constants.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_iovec.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_macros.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" + +namespace quic { + +// All data that a request stream receives falls into one of two categories: +// * "body", that is, DATA frame payload, which the QuicStreamSequencer must +// buffer until it is read; +// * everything else, which QuicSpdyStream immediately processes and thus could +// be marked as consumed with QuicStreamSequencer, unless there is some piece +// of body received prior that still needs to be buffered. +// QuicSpdyStreamBodyManager does two things: it keeps references to body +// fragments (owned by QuicStreamSequencer) and offers methods to read them; and +// it calculates the total number of bytes (including non-body bytes) the caller +// needs to mark consumed (with QuicStreamSequencer) when non-body bytes are +// received or when body is consumed. +class QUIC_EXPORT_PRIVATE QuicSpdyStreamBodyManager { + public: + QuicSpdyStreamBodyManager(); + ~QuicSpdyStreamBodyManager() = default; + + // One of the following two methods must be called every time data is received + // on the request stream. + + // Called when data that could immediately be marked consumed with the + // sequencer (provided that all previous body fragments are consumed) is + // received. |length| must be positive. Returns number of bytes the caller + // must mark consumed, which might be zero. + QUIC_MUST_USE_RESULT size_t OnNonBody(QuicByteCount length); + + // Called when body is received. |body| is added to |fragments_|. The data + // pointed to by |body| must be kept alive until an OnBodyConsumed() or + // ReadBody() call consumes it. |body| must not be empty. + void OnBody(QuicStringPiece body); + + // Internally marks |num_bytes| of body consumed. |num_bytes| might be zero. + // Returns the number of bytes that the caller should mark consumed with the + // sequencer, which is the sum of |num_bytes| for body, and the number of any + // interleaving or immediately trailing non-body bytes. + QUIC_MUST_USE_RESULT size_t OnBodyConsumed(size_t num_bytes); + + // Set up to |iov_len| elements of iov[] to point to available bodies: each + // iov[i].iov_base will point to a body fragment, and iov[i].iov_len will be + // set to its length. No data is copied, no data is consumed. Returns the + // number of iov set. + int PeekBody(iovec* iov, size_t iov_len) const; + + // Copies data from available bodies into at most |iov_len| elements of iov[]. + // Internally consumes copied body bytes as well as all interleaving and + // immediately trailing non-body bytes. |iov.iov_base| and |iov.iov_len| are + // preassigned and will not be changed. Returns the total number of bytes the + // caller shall mark consumed. Sets |*total_bytes_read| to the total number + // of body bytes read. + QUIC_MUST_USE_RESULT size_t ReadBody(const struct iovec* iov, + size_t iov_len, + size_t* total_bytes_read); + + bool HasBytesToRead() const { return !fragments_.empty(); } + + uint64_t total_body_bytes_received() const { + return total_body_bytes_received_; + } + + private: + // A Fragment instance represents a body fragment with a count of bytes + // received afterwards but before the next body fragment that can be marked + // consumed as soon as all of the body fragment is read. + struct Fragment { + // |body| must not be empty. + QuicStringPiece body; + // Might be zero. + QuicByteCount trailing_non_body_byte_count; + }; + // Queue of body fragments and trailing non-body byte counts. + QuicDeque<Fragment> fragments_; + // Total body bytes received. + QuicByteCount total_body_bytes_received_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_HTTP_QUIC_SPDY_STREAM_BODY_MANAGER_H_ diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream_body_manager_test.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream_body_manager_test.cc new file mode 100644 index 00000000000..3a5b720d277 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream_body_manager_test.cc @@ -0,0 +1,282 @@ +// Copyright (c) 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/third_party/quiche/src/quic/core/http/quic_spdy_stream_body_manager.h" + +#include <algorithm> +#include <numeric> +#include <string> + +#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" + +namespace quic { + +namespace test { + +namespace { + +class QuicSpdyStreamBodyManagerTest : public QuicTest { + protected: + QuicSpdyStreamBodyManager body_manager_; +}; + +TEST_F(QuicSpdyStreamBodyManagerTest, HasBytesToRead) { + EXPECT_FALSE(body_manager_.HasBytesToRead()); + EXPECT_EQ(0u, body_manager_.total_body_bytes_received()); + + const QuicByteCount header_length = 3; + EXPECT_EQ(header_length, body_manager_.OnNonBody(header_length)); + + EXPECT_FALSE(body_manager_.HasBytesToRead()); + EXPECT_EQ(0u, body_manager_.total_body_bytes_received()); + + std::string body(1024, 'a'); + body_manager_.OnBody(body); + + EXPECT_TRUE(body_manager_.HasBytesToRead()); + EXPECT_EQ(1024u, body_manager_.total_body_bytes_received()); +} + +TEST_F(QuicSpdyStreamBodyManagerTest, ConsumeMoreThanAvailable) { + std::string body(1024, 'a'); + body_manager_.OnBody(body); + size_t bytes_to_consume = 0; + EXPECT_QUIC_BUG(bytes_to_consume = body_manager_.OnBodyConsumed(2048), + "Not enough available body to consume."); + EXPECT_EQ(0u, bytes_to_consume); +} + +struct { + std::vector<QuicByteCount> frame_header_lengths; + std::vector<const char*> frame_payloads; + std::vector<QuicByteCount> body_bytes_to_read; + std::vector<QuicByteCount> expected_return_values; +} const kOnBodyConsumedTestData[] = { + // One frame consumed in one call. + {{2}, {"foobar"}, {6}, {6}}, + // Two frames consumed in one call. + {{3, 5}, {"foobar", "baz"}, {9}, {14}}, + // One frame consumed in two calls. + {{2}, {"foobar"}, {4, 2}, {4, 2}}, + // Two frames consumed in two calls matching frame boundaries. + {{3, 5}, {"foobar", "baz"}, {6, 3}, {11, 3}}, + // Two frames consumed in two calls, + // the first call only consuming part of the first frame. + {{3, 5}, {"foobar", "baz"}, {5, 4}, {5, 9}}, + // Two frames consumed in two calls, + // the first call consuming the entire first frame and part of the second. + {{3, 5}, {"foobar", "baz"}, {7, 2}, {12, 2}}, +}; + +TEST_F(QuicSpdyStreamBodyManagerTest, OnBodyConsumed) { + for (size_t test_case_index = 0; + test_case_index < QUIC_ARRAYSIZE(kOnBodyConsumedTestData); + ++test_case_index) { + const std::vector<QuicByteCount>& frame_header_lengths = + kOnBodyConsumedTestData[test_case_index].frame_header_lengths; + const std::vector<const char*>& frame_payloads = + kOnBodyConsumedTestData[test_case_index].frame_payloads; + const std::vector<QuicByteCount>& body_bytes_to_read = + kOnBodyConsumedTestData[test_case_index].body_bytes_to_read; + const std::vector<QuicByteCount>& expected_return_values = + kOnBodyConsumedTestData[test_case_index].expected_return_values; + + for (size_t frame_index = 0; frame_index < frame_header_lengths.size(); + ++frame_index) { + // Frame header of first frame can immediately be consumed, but not the + // other frames. Each test case start with an empty + // QuicSpdyStreamBodyManager. + EXPECT_EQ(frame_index == 0 ? frame_header_lengths[frame_index] : 0u, + body_manager_.OnNonBody(frame_header_lengths[frame_index])); + body_manager_.OnBody(frame_payloads[frame_index]); + } + + for (size_t call_index = 0; call_index < body_bytes_to_read.size(); + ++call_index) { + EXPECT_EQ(expected_return_values[call_index], + body_manager_.OnBodyConsumed(body_bytes_to_read[call_index])); + } + + EXPECT_FALSE(body_manager_.HasBytesToRead()); + } +} + +struct { + std::vector<QuicByteCount> frame_header_lengths; + std::vector<const char*> frame_payloads; + size_t iov_len; +} const kPeekBodyTestData[] = { + // No frames, more iovecs than frames. + {{}, {}, 1}, + // One frame, same number of iovecs. + {{3}, {"foobar"}, 1}, + // One frame, more iovecs than frames. + {{3}, {"foobar"}, 2}, + // Two frames, fewer iovecs than frames. + {{3, 5}, {"foobar", "baz"}, 1}, + // Two frames, same number of iovecs. + {{3, 5}, {"foobar", "baz"}, 2}, + // Two frames, more iovecs than frames. + {{3, 5}, {"foobar", "baz"}, 3}, +}; + +TEST_F(QuicSpdyStreamBodyManagerTest, PeekBody) { + for (size_t test_case_index = 0; + test_case_index < QUIC_ARRAYSIZE(kPeekBodyTestData); ++test_case_index) { + const std::vector<QuicByteCount>& frame_header_lengths = + kPeekBodyTestData[test_case_index].frame_header_lengths; + const std::vector<const char*>& frame_payloads = + kPeekBodyTestData[test_case_index].frame_payloads; + size_t iov_len = kPeekBodyTestData[test_case_index].iov_len; + + QuicSpdyStreamBodyManager body_manager; + + for (size_t frame_index = 0; frame_index < frame_header_lengths.size(); + ++frame_index) { + // Frame header of first frame can immediately be consumed, but not the + // other frames. Each test case uses a new QuicSpdyStreamBodyManager + // instance. + EXPECT_EQ(frame_index == 0 ? frame_header_lengths[frame_index] : 0u, + body_manager.OnNonBody(frame_header_lengths[frame_index])); + body_manager.OnBody(frame_payloads[frame_index]); + } + + std::vector<iovec> iovecs; + iovecs.resize(iov_len); + size_t iovs_filled = std::min(frame_payloads.size(), iov_len); + ASSERT_EQ(iovs_filled, + static_cast<size_t>(body_manager.PeekBody(&iovecs[0], iov_len))); + for (size_t iovec_index = 0; iovec_index < iovs_filled; ++iovec_index) { + EXPECT_EQ(frame_payloads[iovec_index], + QuicStringPiece( + static_cast<const char*>(iovecs[iovec_index].iov_base), + iovecs[iovec_index].iov_len)); + } + } +} + +struct { + std::vector<QuicByteCount> frame_header_lengths; + std::vector<const char*> frame_payloads; + std::vector<std::vector<QuicByteCount>> iov_lengths; + std::vector<QuicByteCount> expected_total_bytes_read; + std::vector<QuicByteCount> expected_return_values; +} const kReadBodyTestData[] = { + // One frame, one read with smaller iovec. + {{4}, {"foo"}, {{2}}, {2}, {2}}, + // One frame, one read with same size iovec. + {{4}, {"foo"}, {{3}}, {3}, {3}}, + // One frame, one read with larger iovec. + {{4}, {"foo"}, {{5}}, {3}, {3}}, + // One frame, one read with two iovecs, smaller total size. + {{4}, {"foobar"}, {{2, 3}}, {5}, {5}}, + // One frame, one read with two iovecs, same total size. + {{4}, {"foobar"}, {{2, 4}}, {6}, {6}}, + // One frame, one read with two iovecs, larger total size in last iovec. + {{4}, {"foobar"}, {{2, 6}}, {6}, {6}}, + // One frame, one read with extra iovecs, body ends at iovec boundary. + {{4}, {"foobar"}, {{2, 4, 4, 3}}, {6}, {6}}, + // One frame, one read with extra iovecs, body ends not at iovec boundary. + {{4}, {"foobar"}, {{2, 7, 4, 3}}, {6}, {6}}, + // One frame, two reads with two iovecs each, smaller total size. + {{4}, {"foobarbaz"}, {{2, 1}, {3, 2}}, {3, 5}, {3, 5}}, + // One frame, two reads with two iovecs each, same total size. + {{4}, {"foobarbaz"}, {{2, 1}, {4, 2}}, {3, 6}, {3, 6}}, + // One frame, two reads with two iovecs each, larger total size. + {{4}, {"foobarbaz"}, {{2, 1}, {4, 10}}, {3, 6}, {3, 6}}, + // Two frames, one read with smaller iovec. + {{4, 3}, {"foobar", "baz"}, {{8}}, {8}, {11}}, + // Two frames, one read with same size iovec. + {{4, 3}, {"foobar", "baz"}, {{9}}, {9}, {12}}, + // Two frames, one read with larger iovec. + {{4, 3}, {"foobar", "baz"}, {{10}}, {9}, {12}}, + // Two frames, one read with two iovecs, smaller total size. + {{4, 3}, {"foobar", "baz"}, {{4, 3}}, {7}, {10}}, + // Two frames, one read with two iovecs, same total size. + {{4, 3}, {"foobar", "baz"}, {{4, 5}}, {9}, {12}}, + // Two frames, one read with two iovecs, larger total size in last iovec. + {{4, 3}, {"foobar", "baz"}, {{4, 6}}, {9}, {12}}, + // Two frames, one read with extra iovecs, body ends at iovec boundary. + {{4, 3}, {"foobar", "baz"}, {{4, 6, 4, 3}}, {9}, {12}}, + // Two frames, one read with extra iovecs, body ends not at iovec boundary. + {{4, 3}, {"foobar", "baz"}, {{4, 7, 4, 3}}, {9}, {12}}, + // Two frames, two reads with two iovecs each, reads end on frame boundary. + {{4, 3}, {"foobar", "baz"}, {{2, 4}, {2, 1}}, {6, 3}, {9, 3}}, + // Three frames, three reads, extra iovecs, no iovec ends on frame boundary. + {{4, 3, 6}, + {"foobar", "bazquux", "qux"}, + {{4, 3}, {2, 3}, {5, 3}}, + {7, 5, 4}, + {10, 5, 10}}, +}; + +TEST_F(QuicSpdyStreamBodyManagerTest, ReadBody) { + for (size_t test_case_index = 0; + test_case_index < QUIC_ARRAYSIZE(kReadBodyTestData); ++test_case_index) { + const std::vector<QuicByteCount>& frame_header_lengths = + kReadBodyTestData[test_case_index].frame_header_lengths; + const std::vector<const char*>& frame_payloads = + kReadBodyTestData[test_case_index].frame_payloads; + const std::vector<std::vector<QuicByteCount>>& iov_lengths = + kReadBodyTestData[test_case_index].iov_lengths; + const std::vector<QuicByteCount>& expected_total_bytes_read = + kReadBodyTestData[test_case_index].expected_total_bytes_read; + const std::vector<QuicByteCount>& expected_return_values = + kReadBodyTestData[test_case_index].expected_return_values; + + QuicSpdyStreamBodyManager body_manager; + + std::string received_body; + + for (size_t frame_index = 0; frame_index < frame_header_lengths.size(); + ++frame_index) { + // Frame header of first frame can immediately be consumed, but not the + // other frames. Each test case uses a new QuicSpdyStreamBodyManager + // instance. + EXPECT_EQ(frame_index == 0 ? frame_header_lengths[frame_index] : 0u, + body_manager.OnNonBody(frame_header_lengths[frame_index])); + body_manager.OnBody(frame_payloads[frame_index]); + received_body.append(frame_payloads[frame_index]); + } + + std::string read_body; + + for (size_t call_index = 0; call_index < iov_lengths.size(); ++call_index) { + // Allocate single buffer for iovecs. + size_t total_iov_length = std::accumulate(iov_lengths[call_index].begin(), + iov_lengths[call_index].end(), + static_cast<size_t>(0)); + std::string buffer(total_iov_length, 'z'); + + // Construct iovecs pointing to contiguous areas in the buffer. + std::vector<iovec> iovecs; + size_t offset = 0; + for (size_t iov_length : iov_lengths[call_index]) { + CHECK(offset + iov_length <= buffer.size()); + iovecs.push_back({&buffer[offset], iov_length}); + offset += iov_length; + } + + // Make sure |total_bytes_read| differs from |expected_total_bytes_read|. + size_t total_bytes_read = expected_total_bytes_read[call_index] + 12; + EXPECT_EQ( + expected_return_values[call_index], + body_manager.ReadBody(&iovecs[0], iovecs.size(), &total_bytes_read)); + read_body.append(buffer.substr(0, total_bytes_read)); + } + + EXPECT_EQ(received_body.substr(0, read_body.size()), read_body); + EXPECT_EQ(read_body.size() < received_body.size(), + body_manager.HasBytesToRead()); + } +} + +} // anonymous namespace + +} // namespace test + +} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream_test.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream_test.cc index f9777ec420f..e09cbf46e16 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream_test.cc @@ -108,22 +108,26 @@ class TestStream : public QuicSpdyStream { class TestMockUpdateStreamSession : public MockQuicSpdySession { public: explicit TestMockUpdateStreamSession(QuicConnection* connection) - : MockQuicSpdySession(connection) {} + : MockQuicSpdySession(connection), + expected_precedence_( + spdy::SpdyStreamPrecedence(QuicStream::kDefaultPriority)) {} - void UpdateStreamPriority(QuicStreamId id, SpdyPriority priority) override { + void UpdateStreamPriority( + QuicStreamId id, + const spdy::SpdyStreamPrecedence& precedence) override { EXPECT_EQ(id, expected_stream_->id()); - EXPECT_EQ(expected_priority_, priority); - EXPECT_EQ(expected_priority_, expected_stream_->priority()); + EXPECT_EQ(expected_precedence_, precedence); + EXPECT_EQ(expected_precedence_, expected_stream_->precedence()); } void SetExpectedStream(QuicSpdyStream* stream) { expected_stream_ = stream; } - void SetExpectedPriority(SpdyPriority priority) { - expected_priority_ = priority; + void SetExpectedPriority(const spdy::SpdyStreamPrecedence& precedence) { + expected_precedence_ = precedence; } private: QuicSpdyStream* expected_stream_; - SpdyPriority expected_priority_; + spdy::SpdyStreamPrecedence expected_precedence_; }; class QuicSpdyStreamTest : public QuicTestWithParam<ParsedQuicVersion> { @@ -160,11 +164,27 @@ class QuicSpdyStreamTest : public QuicTestWithParam<ParsedQuicVersion> { ~QuicSpdyStreamTest() override = default; - std::string EncodeQpackHeaders(QuicStreamId id, SpdyHeaderBlock* header) { + // Return QPACK-encoded header block without using the dynamic table. + std::string EncodeQpackHeaders( + std::vector<std::pair<QuicStringPiece, QuicStringPiece>> headers) { + SpdyHeaderBlock header_block; + for (const auto& header_field : headers) { + header_block.AppendValueOrAddHeader(header_field.first, + header_field.second); + } + + return EncodeQpackHeaders(header_block); + } + + // Return QPACK-encoded header block without using the dynamic table. + std::string EncodeQpackHeaders(const SpdyHeaderBlock& header) { NoopQpackStreamSenderDelegate encoder_stream_sender_delegate; - auto qpack_encoder = QuicMakeUnique<QpackEncoder>( - session_.get(), &encoder_stream_sender_delegate); - return qpack_encoder->EncodeHeaderList(id, header); + auto qpack_encoder = QuicMakeUnique<QpackEncoder>(session_.get()); + qpack_encoder->set_qpack_stream_sender_delegate( + &encoder_stream_sender_delegate); + // QpackEncoder does not use the dynamic table by default, + // therefore the value of |stream_id| does not matter. + return qpack_encoder->EncodeHeaderList(/* stream_id = */ 0, header); } void Initialize(bool stream_should_process_data) { @@ -206,6 +226,20 @@ class QuicSpdyStreamTest : public QuicTestWithParam<ParsedQuicVersion> { return VersionHasDataFrameHeader(GetParam().transport_version); } + // Construct HEADERS frame with QPACK-encoded |headers| without using the + // dynamic table. + std::string HeadersFrame( + std::vector<std::pair<QuicStringPiece, QuicStringPiece>> headers) { + return HeadersFrame(EncodeQpackHeaders(headers)); + } + + // Construct HEADERS frame with QPACK-encoded |headers| without using the + // dynamic table. + std::string HeadersFrame(const SpdyHeaderBlock& headers) { + return HeadersFrame(EncodeQpackHeaders(headers)); + } + + // Construct HEADERS frame with given payload. std::string HeadersFrame(QuicStringPiece payload) { std::unique_ptr<char[]> headers_buffer; QuicByteCount headers_frame_header_length = @@ -224,6 +258,24 @@ class QuicSpdyStreamTest : public QuicTestWithParam<ParsedQuicVersion> { return QuicStrCat(data_frame_header, payload); } + std::string UnknownFrame(uint64_t frame_type, QuicStringPiece payload) { + std::string frame; + const size_t length = QuicDataWriter::GetVarInt62Len(frame_type) + + QuicDataWriter::GetVarInt62Len(payload.size()) + + payload.size(); + frame.resize(length); + + QuicDataWriter writer(length, const_cast<char*>(frame.data())); + writer.WriteVarInt62(frame_type); + writer.WriteStringPieceVarInt62(payload); + // Even though integers can be encoded with different lengths, + // QuicDataWriter is expected to produce an encoding in Write*() of length + // promised in GetVarInt62Len(). + DCHECK_EQ(length, writer.length()); + + return frame; + } + MockQuicConnectionHelper helper_; MockAlarmFactory alarm_factory_; MockQuicConnection* connection_; @@ -245,7 +297,8 @@ INSTANTIATE_TEST_SUITE_P(Tests, TEST_P(QuicSpdyStreamTest, ProcessHeaderList) { Initialize(kShouldProcessData); - stream_->OnStreamHeadersPriority(kV3HighestPriority); + stream_->OnStreamHeadersPriority( + spdy::SpdyStreamPrecedence(kV3HighestPriority)); ProcessHeaders(false, headers_); EXPECT_EQ("", stream_->data()); EXPECT_FALSE(stream_->header_list().empty()); @@ -256,7 +309,8 @@ TEST_P(QuicSpdyStreamTest, ProcessTooLargeHeaderList) { Initialize(kShouldProcessData); QuicHeaderList headers; - stream_->OnStreamHeadersPriority(kV3HighestPriority); + stream_->OnStreamHeadersPriority( + spdy::SpdyStreamPrecedence(kV3HighestPriority)); const bool version_uses_qpack = VersionUsesQpack(GetParam().transport_version); @@ -288,7 +342,8 @@ TEST_P(QuicSpdyStreamTest, ProcessHeaderListWithFin) { headers.OnHeader(p.first, p.second); total_bytes += p.first.size() + p.second.size(); } - stream_->OnStreamHeadersPriority(kV3HighestPriority); + stream_->OnStreamHeadersPriority( + spdy::SpdyStreamPrecedence(kV3HighestPriority)); stream_->OnStreamHeaderList(true, total_bytes, headers); EXPECT_EQ("", stream_->data()); EXPECT_FALSE(stream_->header_list().empty()); @@ -925,7 +980,8 @@ TEST_P(QuicSpdyStreamTest, ReceivingTrailersViaHeaderList) { total_bytes += p.first.size() + p.second.size(); } - stream_->OnStreamHeadersPriority(kV3HighestPriority); + stream_->OnStreamHeadersPriority( + spdy::SpdyStreamPrecedence(kV3HighestPriority)); stream_->OnStreamHeaderList(/*fin=*/false, total_bytes, headers); stream_->ConsumeHeaderList(); @@ -1191,6 +1247,14 @@ TEST_P(QuicSpdyStreamTest, ClientWritesPriority) { EXPECT_CALL(*session_, WritevData(send_control_stream, send_control_stream->id(), _, _, _)) .Times(3); + auto qpack_encoder_stream = + QuicSpdySessionPeer::GetQpackEncoderSendStream(session_.get()); + EXPECT_CALL(*session_, WritevData(qpack_encoder_stream, + qpack_encoder_stream->id(), 1, 0, _)); + auto qpack_decoder_stream = + QuicSpdySessionPeer::GetQpackDecoderSendStream(session_.get()); + EXPECT_CALL(*session_, WritevData(qpack_decoder_stream, + qpack_decoder_stream->id(), 1, 0, _)); } // Write the initial headers, without a FIN. @@ -1352,7 +1416,7 @@ TEST_P(QuicSpdyStreamTest, WritingTrailersAfterFIN) { TEST_P(QuicSpdyStreamTest, HeaderStreamNotiferCorrespondingSpdyStream) { // There is no headers stream if QPACK is used. - if (!VersionUsesQpack(GetParam().transport_version)) { + if (VersionUsesQpack(GetParam().transport_version)) { return; } @@ -1432,8 +1496,9 @@ TEST_P(QuicSpdyStreamTest, StreamBecomesZombieWithWriteThatCloses) { TEST_P(QuicSpdyStreamTest, OnPriorityFrame) { Initialize(kShouldProcessData); - stream_->OnPriorityFrame(kV3HighestPriority); - EXPECT_EQ(kV3HighestPriority, stream_->priority()); + stream_->OnPriorityFrame(spdy::SpdyStreamPrecedence(kV3HighestPriority)); + EXPECT_EQ(spdy::SpdyStreamPrecedence(kV3HighestPriority), + stream_->precedence()); } TEST_P(QuicSpdyStreamTest, OnPriorityFrameAfterSendingData) { @@ -1451,8 +1516,9 @@ TEST_P(QuicSpdyStreamTest, OnPriorityFrameAfterSendingData) { } EXPECT_CALL(*session_, WritevData(_, _, 4, _, FIN)); stream_->WriteOrBufferBody("data", true); - stream_->OnPriorityFrame(kV3HighestPriority); - EXPECT_EQ(kV3HighestPriority, stream_->priority()); + stream_->OnPriorityFrame(spdy::SpdyStreamPrecedence(kV3HighestPriority)); + EXPECT_EQ(spdy::SpdyStreamPrecedence(kV3HighestPriority), + stream_->precedence()); } TEST_P(QuicSpdyStreamTest, SetPriorityBeforeUpdateStreamPriority) { @@ -1473,11 +1539,11 @@ TEST_P(QuicSpdyStreamTest, SetPriorityBeforeUpdateStreamPriority) { // if called within UpdateStreamPriority(). This expectation is enforced in // TestMockUpdateStreamSession::UpdateStreamPriority(). session->SetExpectedStream(stream); - session->SetExpectedPriority(kV3HighestPriority); - stream->SetPriority(kV3HighestPriority); + session->SetExpectedPriority(spdy::SpdyStreamPrecedence(kV3HighestPriority)); + stream->SetPriority(spdy::SpdyStreamPrecedence(kV3HighestPriority)); - session->SetExpectedPriority(kV3LowestPriority); - stream->SetPriority(kV3LowestPriority); + session->SetExpectedPriority(spdy::SpdyStreamPrecedence(kV3LowestPriority)); + stream->SetPriority(spdy::SpdyStreamPrecedence(kV3LowestPriority)); } TEST_P(QuicSpdyStreamTest, StreamWaitsForAcks) { @@ -1735,14 +1801,10 @@ TEST_P(QuicSpdyStreamTest, HeadersFrameOnRequestStream) { Initialize(kShouldProcessData); - // HEADERS frame with QPACK encoded single header field "foo: bar". - std::string headers = - HeadersFrame(QuicTextUtils::HexDecode("00002a94e703626172")); + std::string headers = HeadersFrame({std::make_pair("foo", "bar")}); std::string data = DataFrame(kDataFramePayload); - // HEADERS frame with QPACK encoded single header - // field "custom-key: custom-value". - std::string trailers = HeadersFrame( - QuicTextUtils::HexDecode("00002f0125a849e95ba97d7f8925a849e95bb8e8b4bf")); + std::string trailers = + HeadersFrame({std::make_pair("custom-key", "custom-value")}); std::string stream_frame_payload = QuicStrCat(headers, data, trailers); QuicStreamFrame frame(stream_->id(), false, 0, stream_frame_payload); @@ -1767,11 +1829,7 @@ TEST_P(QuicSpdyStreamTest, ProcessBodyAfterTrailers) { Initialize(!kShouldProcessData); - // HEADERS frame with QPACK encoded single header field "foo: bar". - std::string headers = - HeadersFrame(QuicTextUtils::HexDecode("00002a94e703626172")); - - // DATA frame. + std::string headers = HeadersFrame({std::make_pair("foo", "bar")}); std::string data = DataFrame(kDataFramePayload); // A header block that will take more than one block of sequencer buffer. @@ -1779,9 +1837,7 @@ TEST_P(QuicSpdyStreamTest, ProcessBodyAfterTrailers) { // be freed. SpdyHeaderBlock trailers_block; trailers_block["key1"] = std::string(10000, 'x'); - std::string trailers_frame_payload = - EncodeQpackHeaders(stream_->id(), &trailers_block); - std::string trailers = HeadersFrame(trailers_frame_payload); + std::string trailers = HeadersFrame(trailers_block); // Feed all three HTTP/3 frames in a single stream frame. std::string stream_frame_payload = QuicStrCat(headers, data, trailers); @@ -1857,8 +1913,24 @@ TEST_P(QuicSpdyStreamTest, ImmediateHeaderDecodingWithDynamicTableEntries) { return; } + if (GetParam().handshake_protocol == PROTOCOL_TLS1_3) { + // TODO(nharper, b/112643533): Figure out why this test fails when TLS is + // enabled and fix it. + return; + } + + testing::InSequence s; Initialize(kShouldProcessData); + session_->qpack_decoder()->OnSetDynamicTableCapacity(1024); + + 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, + decoder_send_stream->id(), 1, 0, _)); + EXPECT_CALL(*session_, WritevData(decoder_send_stream, + decoder_send_stream->id(), _, _, _)); // Deliver dynamic table entry to decoder. session_->qpack_decoder()->OnInsertWithoutNameReference("foo", "bar"); @@ -1879,6 +1951,8 @@ TEST_P(QuicSpdyStreamTest, ImmediateHeaderDecodingWithDynamicTableEntries) { headers.length(), data)); EXPECT_EQ(kDataFramePayload, stream_->data()); + EXPECT_CALL(*session_, WritevData(decoder_send_stream, + decoder_send_stream->id(), _, _, _)); // Deliver second dynamic table entry to decoder. session_->qpack_decoder()->OnInsertWithoutNameReference("trailing", "foobar"); @@ -1902,7 +1976,15 @@ TEST_P(QuicSpdyStreamTest, BlockedHeaderDecoding) { return; } + if (GetParam().handshake_protocol == PROTOCOL_TLS1_3) { + // TODO(nharper, b/112643533): Figure out why this test fails when TLS is + // enabled and fix it. + return; + } + + testing::InSequence s; Initialize(kShouldProcessData); + session_->qpack_decoder()->OnSetDynamicTableCapacity(1024); // HEADERS frame referencing first dynamic table entry. std::string headers = HeadersFrame(QuicTextUtils::HexDecode("020080")); @@ -1911,6 +1993,14 @@ TEST_P(QuicSpdyStreamTest, BlockedHeaderDecoding) { // Decoding is blocked because dynamic table entry has not been received yet. EXPECT_FALSE(stream_->headers_decompressed()); + 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, + decoder_send_stream->id(), 1, 0, _)); + EXPECT_CALL(*session_, WritevData(decoder_send_stream, + decoder_send_stream->id(), _, _, _)); // Deliver dynamic table entry to decoder. session_->qpack_decoder()->OnInsertWithoutNameReference("foo", "bar"); EXPECT_TRUE(stream_->headers_decompressed()); @@ -1934,6 +2024,8 @@ TEST_P(QuicSpdyStreamTest, BlockedHeaderDecoding) { // Decoding is blocked because dynamic table entry has not been received yet. EXPECT_FALSE(stream_->trailers_decompressed()); + EXPECT_CALL(*session_, WritevData(decoder_send_stream, + decoder_send_stream->id(), _, _, _)); // Deliver second dynamic table entry to decoder. session_->qpack_decoder()->OnInsertWithoutNameReference("trailing", "foobar"); EXPECT_TRUE(stream_->trailers_decompressed()); @@ -1950,6 +2042,7 @@ TEST_P(QuicSpdyStreamTest, AsyncErrorDecodingHeaders) { } Initialize(kShouldProcessData); + session_->qpack_decoder()->OnSetDynamicTableCapacity(1024); // HEADERS frame only referencing entry with absolute index 0 but with // Required Insert Count = 2, which is incorrect. @@ -1980,7 +2073,15 @@ TEST_P(QuicSpdyStreamTest, AsyncErrorDecodingTrailers) { return; } + if (GetParam().handshake_protocol == PROTOCOL_TLS1_3) { + // TODO(nharper, b/112643533): Figure out why this test fails when TLS is + // enabled and fix it. + return; + } + + testing::InSequence s; Initialize(kShouldProcessData); + session_->qpack_decoder()->OnSetDynamicTableCapacity(1024); // HEADERS frame referencing first dynamic table entry. std::string headers = HeadersFrame(QuicTextUtils::HexDecode("020080")); @@ -1989,6 +2090,14 @@ TEST_P(QuicSpdyStreamTest, AsyncErrorDecodingTrailers) { // Decoding is blocked because dynamic table entry has not been received yet. EXPECT_FALSE(stream_->headers_decompressed()); + 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, + decoder_send_stream->id(), 1, 0, _)); + EXPECT_CALL(*session_, WritevData(decoder_send_stream, + decoder_send_stream->id(), _, _, _)); // Deliver dynamic table entry to decoder. session_->qpack_decoder()->OnInsertWithoutNameReference("foo", "bar"); EXPECT_TRUE(stream_->headers_decompressed()); @@ -2076,20 +2185,17 @@ INSTANTIATE_TEST_SUITE_P(Tests, // Test that stream bytes are consumed (by calling // sequencer()->MarkConsumed()) incrementally, as soon as possible. -TEST_P(QuicSpdyStreamIncrementalConsumptionTest, IncrementalConsumptionTest) { +TEST_P(QuicSpdyStreamIncrementalConsumptionTest, OnlyKnownFrames) { if (!VersionUsesQpack(GetParam().transport_version)) { return; } Initialize(!kShouldProcessData); - // HEADERS frame with QPACK encoded single header field "foo: bar". - std::string headers = - HeadersFrame(QuicTextUtils::HexDecode("00002a94e703626172")); + std::string headers = HeadersFrame({std::make_pair("foo", "bar")}); // All HEADERS frame bytes are consumed even if the frame is not received - // completely (as long as at least some of the payload is received, which is - // an implementation detail that should not be tested). + // completely. OnStreamFrame(QuicStringPiece(headers).substr(0, headers.size() - 1)); EXPECT_EQ(headers.size() - 1, NewlyConsumedBytes()); @@ -2104,21 +2210,21 @@ TEST_P(QuicSpdyStreamIncrementalConsumptionTest, IncrementalConsumptionTest) { // DATA frame. QuicStringPiece data_payload(kDataFramePayload); std::string data_frame = DataFrame(data_payload); + QuicByteCount data_frame_header_length = + data_frame.size() - data_payload.size(); - // DATA frame is not consumed because payload has to be buffered. - // TODO(bnc): Consume frame header as soon as possible. + // DATA frame header is consumed. + // DATA frame payload is not consumed because payload has to be buffered. OnStreamFrame(data_frame); - EXPECT_EQ(0u, NewlyConsumedBytes()); + EXPECT_EQ(data_frame_header_length, NewlyConsumedBytes()); // Consume all but last byte of data. EXPECT_EQ(data_payload.substr(0, data_payload.size() - 1), ReadFromStream(data_payload.size() - 1)); - EXPECT_EQ(data_frame.size() - 1, NewlyConsumedBytes()); + EXPECT_EQ(data_payload.size() - 1, NewlyConsumedBytes()); - // Trailing HEADERS frame with QPACK encoded - // single header field "custom-key: custom-value". - std::string trailers = HeadersFrame( - QuicTextUtils::HexDecode("00002f0125a849e95ba97d7f8925a849e95bb8e8b4bf")); + std::string trailers = + HeadersFrame({std::make_pair("custom-key", "custom-value")}); // No bytes are consumed, because last byte of DATA payload is still buffered. OnStreamFrame(QuicStringPiece(trailers).substr(0, trailers.size() - 1)); @@ -2138,24 +2244,109 @@ TEST_P(QuicSpdyStreamIncrementalConsumptionTest, IncrementalConsumptionTest) { ElementsAre(Pair("custom-key", "custom-value"))); } -TEST_P(QuicSpdyStreamTest, PushPromiseOnDataStreamShouldClose) { +TEST_P(QuicSpdyStreamIncrementalConsumptionTest, UnknownFramesInterleaved) { + if (!VersionUsesQpack(GetParam().transport_version)) { + return; + } + + Initialize(!kShouldProcessData); + + // Unknown frame of reserved type before HEADERS is consumed immediately. + std::string unknown_frame1 = UnknownFrame(0x21, "foo"); + OnStreamFrame(unknown_frame1); + EXPECT_EQ(unknown_frame1.size(), NewlyConsumedBytes()); + + std::string headers = HeadersFrame({std::make_pair("foo", "bar")}); + + // All HEADERS frame bytes are consumed even if the frame is not received + // completely. + OnStreamFrame(QuicStringPiece(headers).substr(0, headers.size() - 1)); + EXPECT_EQ(headers.size() - 1, NewlyConsumedBytes()); + + // The rest of the HEADERS frame is also consumed immediately. + OnStreamFrame(QuicStringPiece(headers).substr(headers.size() - 1)); + EXPECT_EQ(1u, NewlyConsumedBytes()); + + // Verify headers. + EXPECT_THAT(stream_->header_list(), ElementsAre(Pair("foo", "bar"))); + stream_->ConsumeHeaderList(); + + // Frame of unknown, not reserved type between HEADERS and DATA is consumed + // immediately. + std::string unknown_frame2 = UnknownFrame(0x3a, ""); + OnStreamFrame(unknown_frame2); + EXPECT_EQ(unknown_frame2.size(), NewlyConsumedBytes()); + + // DATA frame. + QuicStringPiece data_payload(kDataFramePayload); + std::string data_frame = DataFrame(data_payload); + QuicByteCount data_frame_header_length = + data_frame.size() - data_payload.size(); + + // DATA frame header is consumed. + // DATA frame payload is not consumed because payload has to be buffered. + OnStreamFrame(data_frame); + EXPECT_EQ(data_frame_header_length, NewlyConsumedBytes()); + + // Frame of unknown, not reserved type is not consumed because DATA payload is + // still buffered. + std::string unknown_frame3 = UnknownFrame(0x39, "bar"); + OnStreamFrame(unknown_frame3); + EXPECT_EQ(0u, NewlyConsumedBytes()); + + // Consume all but last byte of data. + EXPECT_EQ(data_payload.substr(0, data_payload.size() - 1), + ReadFromStream(data_payload.size() - 1)); + EXPECT_EQ(data_payload.size() - 1, NewlyConsumedBytes()); + + std::string trailers = + HeadersFrame({std::make_pair("custom-key", "custom-value")}); + + // No bytes are consumed, because last byte of DATA payload is still buffered. + OnStreamFrame(QuicStringPiece(trailers).substr(0, trailers.size() - 1)); + EXPECT_EQ(0u, NewlyConsumedBytes()); + + // Reading last byte of DATA payload triggers consumption of all data received + // so far, even though last HEADERS frame has not been received completely. + EXPECT_EQ(data_payload.substr(data_payload.size() - 1), ReadFromStream(1)); + EXPECT_EQ(1 + unknown_frame3.size() + trailers.size() - 1, + NewlyConsumedBytes()); + + // Last byte of trailers is immediately consumed. + OnStreamFrame(QuicStringPiece(trailers).substr(trailers.size() - 1)); + EXPECT_EQ(1u, NewlyConsumedBytes()); + + // Verify trailers. + EXPECT_THAT(stream_->received_trailers(), + ElementsAre(Pair("custom-key", "custom-value"))); + + // Unknown frame of reserved type after trailers is consumed immediately. + std::string unknown_frame4 = UnknownFrame(0x40, ""); + OnStreamFrame(unknown_frame4); + EXPECT_EQ(unknown_frame4.size(), NewlyConsumedBytes()); +} + +TEST_P(QuicSpdyStreamTest, PushPromiseOnDataStream) { Initialize(kShouldProcessData); if (!HasFrameHeader()) { return; } + + std::string headers = EncodeQpackHeaders({{"foo", "bar"}}); + PushPromiseFrame push_promise; push_promise.push_id = 0x01; - push_promise.headers = "Headers"; + push_promise.headers = headers; std::unique_ptr<char[]> buffer; HttpEncoder encoder; uint64_t length = encoder.SerializePushPromiseFrameWithOnlyPushId(push_promise, &buffer); - QuicStreamFrame frame(stream_->id(), false, 0, buffer.get(), length); - // TODO(lassey): Check for HTTP_WRONG_STREAM error code. - EXPECT_CALL(*connection_, CloseConnection(QUIC_HTTP_DECODER_ERROR, _, _)); - stream_->OnStreamHeadersPriority(kV3HighestPriority); - ProcessHeaders(false, headers_); - stream_->ConsumeHeaderList(); + std::string data = std::string(buffer.get(), length) + headers; + QuicStreamFrame frame(stream_->id(), false, 0, data); + + EXPECT_CALL(*session_, + OnPromiseHeaderList(stream_->id(), push_promise.push_id, + headers.length(), _)); stream_->OnStreamFrame(frame); } @@ -2189,9 +2380,8 @@ TEST_P(QuicSpdyStreamTest, TrailersAfterTrailers) { Initialize(kShouldProcessData); - // Receive and consume headers, with single header field "foo: bar". - std::string headers = - HeadersFrame(QuicTextUtils::HexDecode("00002a94e703626172")); + // Receive and consume headers. + std::string headers = HeadersFrame({std::make_pair("foo", "bar")}); QuicStreamOffset offset = 0; stream_->OnStreamFrame( QuicStreamFrame(stream_->id(), false, offset, headers)); @@ -2207,10 +2397,9 @@ TEST_P(QuicSpdyStreamTest, TrailersAfterTrailers) { EXPECT_EQ(kDataFramePayload, stream_->data()); - // Receive and consume trailers, with single header field - // "custom-key: custom-value". - std::string trailers1 = HeadersFrame( - QuicTextUtils::HexDecode("00002f0125a849e95ba97d7f8925a849e95bb8e8b4bf")); + // Receive and consume trailers. + std::string trailers1 = + HeadersFrame({std::make_pair("custom-key", "custom-value")}); stream_->OnStreamFrame( QuicStreamFrame(stream_->id(), false, offset, trailers1)); offset += trailers1.size(); @@ -2230,7 +2419,7 @@ TEST_P(QuicSpdyStreamTest, TrailersAfterTrailers) { .WillOnce(InvokeWithoutArgs([this]() { stream_->StopReading(); })); // Receive another HEADERS frame, with no header fields. - std::string trailers2 = HeadersFrame(QuicTextUtils::HexDecode("0000")); + std::string trailers2 = HeadersFrame(SpdyHeaderBlock()); stream_->OnStreamFrame( QuicStreamFrame(stream_->id(), false, offset, trailers2)); } @@ -2244,9 +2433,8 @@ TEST_P(QuicSpdyStreamTest, DataAfterTrailers) { Initialize(kShouldProcessData); - // Receive and consume headers, with single header field "foo: bar". - std::string headers = - HeadersFrame(QuicTextUtils::HexDecode("00002a94e703626172")); + // Receive and consume headers. + std::string headers = HeadersFrame({std::make_pair("foo", "bar")}); QuicStreamOffset offset = 0; stream_->OnStreamFrame( QuicStreamFrame(stream_->id(), false, offset, headers)); @@ -2262,8 +2450,8 @@ TEST_P(QuicSpdyStreamTest, DataAfterTrailers) { EXPECT_EQ(kDataFramePayload, stream_->data()); // Receive trailers, with single header field "custom-key: custom-value". - std::string trailers = HeadersFrame( - QuicTextUtils::HexDecode("00002f0125a849e95ba97d7f8925a849e95bb8e8b4bf")); + std::string trailers = + HeadersFrame({std::make_pair("custom-key", "custom-value")}); stream_->OnStreamFrame( QuicStreamFrame(stream_->id(), false, offset, trailers)); offset += trailers.size(); @@ -2297,10 +2485,10 @@ TEST_P(QuicSpdyStreamTest, StopProcessingIfConnectionClosed) { // SETTINGS frame with empty payload. std::string settings = QuicTextUtils::HexDecode("0400"); - // HEADERS frame with QPACK encoded single header field "foo: bar". + + // HEADERS frame. // Since it arrives after a SETTINGS frame, it should never be read. - std::string headers = - HeadersFrame(QuicTextUtils::HexDecode("00002a94e703626172")); + std::string headers = HeadersFrame({std::make_pair("foo", "bar")}); // Combine the two frames to make sure they are processed in a single // QuicSpdyStream::OnDataAvailable() call. diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/offline/qpack_offline_decoder.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/offline/qpack_offline_decoder.cc index 20af0748723..dc2d864f058 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/offline/qpack_offline_decoder.cc +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/offline/qpack_offline_decoder.cc @@ -20,7 +20,6 @@ namespace quic { QpackOfflineDecoder::QpackOfflineDecoder() : encoder_stream_error_detected_(false), - qpack_decoder_(this, &decoder_stream_sender_delegate_), max_blocked_streams_(0) {} bool QpackOfflineDecoder::DecodeAndVerifyOfflineData( @@ -93,8 +92,10 @@ bool QpackOfflineDecoder::ParseInputFilename(QuicStringPiece input_filename) { << "\" as an integer."; return false; } - - qpack_decoder_.SetMaximumDynamicTableCapacity(maximum_dynamic_table_capacity); + qpack_decoder_ = QuicMakeUnique<QpackDecoder>(maximum_dynamic_table_capacity, + max_blocked_streams_, this); + qpack_decoder_->set_qpack_stream_sender_delegate( + &decoder_stream_sender_delegate_); return true; } @@ -133,7 +134,7 @@ bool QpackOfflineDecoder::DecodeHeaderBlocksFromFile( // Process data. if (stream_id == 0) { - qpack_decoder_.DecodeEncoderStreamData(data); + qpack_decoder_->encoder_stream_receiver()->Decode(data); if (encoder_stream_error_detected_) { QUIC_LOG(ERROR) << "Error detected on encoder stream."; @@ -141,7 +142,7 @@ bool QpackOfflineDecoder::DecodeHeaderBlocksFromFile( } } else { auto headers_handler = QuicMakeUnique<test::TestHeadersHandler>(); - auto progressive_decoder = qpack_decoder_.CreateProgressiveDecoder( + auto progressive_decoder = qpack_decoder_->CreateProgressiveDecoder( stream_id, headers_handler.get()); progressive_decoder->Decode(data); @@ -183,6 +184,7 @@ bool QpackOfflineDecoder::DecodeHeaderBlocksFromFile( } // Enforce limit on blocked streams. + // TODO(b/112770235): Move this logic to QpackDecoder. uint64_t blocked_streams_count = 0; for (const auto& decoder : decoders_) { if (!decoder.headers_handler->decoding_completed()) { diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/offline/qpack_offline_decoder.h b/chromium/net/third_party/quiche/src/quic/core/qpack/offline/qpack_offline_decoder.h index 9984d4b4b16..dcb8894572c 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/offline/qpack_offline_decoder.h +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/offline/qpack_offline_decoder.h @@ -72,7 +72,7 @@ class QpackOfflineDecoder : public QpackDecoder::EncoderStreamErrorDelegate { bool encoder_stream_error_detected_; NoopQpackStreamSenderDelegate decoder_stream_sender_delegate_; - QpackDecoder qpack_decoder_; + std::unique_ptr<QpackDecoder> qpack_decoder_; uint64_t max_blocked_streams_; // Objects necessary for decoding, one list element for each header block. diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_blocking_manager.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_blocking_manager.cc new file mode 100644 index 00000000000..04d824fa969 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_blocking_manager.cc @@ -0,0 +1,144 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/third_party/quiche/src/quic/core/qpack/qpack_blocking_manager.h" + +#include <utility> + +namespace quic { + +QpackBlockingManager::QpackBlockingManager() : known_received_count_(0) {} + +bool QpackBlockingManager::OnHeaderAcknowledgement(QuicStreamId stream_id) { + auto it = header_blocks_.find(stream_id); + if (it == header_blocks_.end()) { + return false; + } + + DCHECK(!it->second.empty()); + + const IndexSet& indices = it->second.front(); + DCHECK(!indices.empty()); + + const uint64_t required_index_count = RequiredInsertCount(indices); + if (known_received_count_ < required_index_count) { + IncreaseKnownReceivedCountTo(required_index_count); + } + + DecreaseReferenceCounts(indices); + + it->second.pop_front(); + if (it->second.empty()) { + header_blocks_.erase(it); + } + + return true; +} + +void QpackBlockingManager::OnStreamCancellation(QuicStreamId stream_id) { + auto it = header_blocks_.find(stream_id); + if (it == header_blocks_.end()) { + return; + } + + for (const IndexSet& indices : it->second) { + DecreaseReferenceCounts(indices); + } + + header_blocks_.erase(it); +} + +void QpackBlockingManager::OnInsertCountIncrement(uint64_t increment) { + IncreaseKnownReceivedCountTo(known_received_count_ + increment); +} + +void QpackBlockingManager::OnHeaderBlockSent(QuicStreamId stream_id, + IndexSet indices) { + DCHECK(!indices.empty()); + + IncreaseReferenceCounts(indices); + header_blocks_[stream_id].push_back(std::move(indices)); +} + +void QpackBlockingManager::OnReferenceSentOnEncoderStream( + uint64_t inserted_index, + uint64_t referred_index) { + auto result = unacked_encoder_stream_references_.insert( + {inserted_index, referred_index}); + // Each dynamic table entry can refer to at most one |referred_index|. + DCHECK(result.second); + IncreaseReferenceCounts({referred_index}); +} + +uint64_t QpackBlockingManager::blocked_stream_count() const { + uint64_t blocked_stream_count = 0; + for (const auto& header_blocks_for_stream : header_blocks_) { + for (const IndexSet& indices : header_blocks_for_stream.second) { + if (RequiredInsertCount(indices) > known_received_count_) { + ++blocked_stream_count; + break; + } + } + } + + return blocked_stream_count; +} + +uint64_t QpackBlockingManager::smallest_blocking_index() const { + return entry_reference_counts_.empty() + ? std::numeric_limits<uint64_t>::max() + : entry_reference_counts_.begin()->first; +} + +// static +uint64_t QpackBlockingManager::RequiredInsertCount(const IndexSet& indices) { + return *indices.rbegin() + 1; +} + +void QpackBlockingManager::IncreaseKnownReceivedCountTo( + uint64_t new_known_received_count) { + DCHECK_GT(new_known_received_count, known_received_count_); + + known_received_count_ = new_known_received_count; + + // Remove referred indices with key less than new Known Received Count from + // |unacked_encoder_stream_references_| and |entry_reference_counts_|. + IndexSet acknowledged_references; + auto it = unacked_encoder_stream_references_.begin(); + while (it != unacked_encoder_stream_references_.end() && + it->first < known_received_count_) { + acknowledged_references.insert(it->second); + ++it; + } + unacked_encoder_stream_references_.erase( + unacked_encoder_stream_references_.begin(), it); + DecreaseReferenceCounts(acknowledged_references); +} + +void QpackBlockingManager::IncreaseReferenceCounts(const IndexSet& indices) { + for (const uint64_t index : indices) { + auto it = entry_reference_counts_.lower_bound(index); + if (it != entry_reference_counts_.end() && it->first == index) { + ++it->second; + } else { + entry_reference_counts_.insert(it, {index, 1}); + } + } +} + +void QpackBlockingManager::DecreaseReferenceCounts(const IndexSet& indices) { + for (const uint64_t index : indices) { + auto it = entry_reference_counts_.find(index); + DCHECK(it != entry_reference_counts_.end()); + DCHECK_NE(0u, it->second); + + if (it->second == 1) { + entry_reference_counts_.erase(it); + } else { + --it->second; + } + } +} + +} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_blocking_manager.h b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_blocking_manager.h new file mode 100644 index 00000000000..7acfd5abac1 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_blocking_manager.h @@ -0,0 +1,100 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef QUICHE_QUIC_CORE_QPACK_QPACK_BLOCKING_MANAGER_H_ +#define QUICHE_QUIC_CORE_QPACK_QPACK_BLOCKING_MANAGER_H_ + +#include <cstdint> +#include <map> +#include <set> + +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" + +namespace quic { + +// Class to keep track of blocked streams and blocking dynamic table entries: +// https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#blocked-decoding +// https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#blocked-insertion +class QUIC_EXPORT_PRIVATE QpackBlockingManager { + public: + using IndexSet = std::multiset<uint64_t>; + + QpackBlockingManager(); + + // Called when a Header Acknowledgement instruction is received on the decoder + // stream. Returns false if there are no outstanding header blocks to be + // acknowledged on |stream_id|. + bool OnHeaderAcknowledgement(QuicStreamId stream_id); + + // Called when a Stream Cancellation instruction is received on the decoder + // stream. + void OnStreamCancellation(QuicStreamId stream_id); + + // Called when an Insert Count Increment instruction is received on the + // decoder stream. + void OnInsertCountIncrement(uint64_t increment); + + // Called when sending a header block containing references to dynamic table + // entries with |indices|. |indices| must not be empty. + void OnHeaderBlockSent(QuicStreamId stream_id, IndexSet indices); + + // Called when sending Insert With Name Reference or Duplicate instruction on + // encoder stream, inserting entry |inserted_index| referring to + // |referred_index|. + void OnReferenceSentOnEncoderStream(uint64_t inserted_index, + uint64_t referred_index); + + // Returns the number of blocked streams. + uint64_t blocked_stream_count() const; + + // Returns the index of the blocking entry with the smallest index, + // or std::numeric_limits<uint64_t>::max() if there are no blocking entries. + uint64_t smallest_blocking_index() const; + + // Returns the Known Received Count as defined at + // https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#known-received-count. + uint64_t known_received_count() const { return known_received_count_; } + + // Required Insert Count for set of indices. + static uint64_t RequiredInsertCount(const IndexSet& indices); + + private: + // A stream typically has only one header block, except for the rare cases of + // 1xx responses, trailers, or push promises. Even if there are multiple + // header blocks sent on a single stream, they might not be blocked at the + // same time. Use std::list instead of QuicDeque because it has lower memory + // footprint when holding few elements. + using HeaderBlocksForStream = std::list<IndexSet>; + using HeaderBlocks = QuicUnorderedMap<QuicStreamId, HeaderBlocksForStream>; + + // Increases |known_received_count_| to |new_known_received_count|, which must + // me larger than |known_received_count_|. Removes acknowledged references + // from |unacked_encoder_stream_references_|. + void IncreaseKnownReceivedCountTo(uint64_t new_known_received_count); + + // Increase or decrease the reference count for each index in |indices|. + void IncreaseReferenceCounts(const IndexSet& indices); + void DecreaseReferenceCounts(const IndexSet& indices); + + // Multiset of indices in each header block for each stream. + // Must not contain a stream id with an empty queue. + HeaderBlocks header_blocks_; + + // Unacknowledged references on the encoder stream. + // The key is the absolute index of the inserted entry, + // the mapped value is the absolute index of the entry referred. + std::map<uint64_t, uint64_t> unacked_encoder_stream_references_; + + // Number of references in |header_blocks_| and + // |unacked_encoder_stream_references_| for each entry index. + std::map<uint64_t, uint64_t> entry_reference_counts_; + + uint64_t known_received_count_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QPACK_QPACK_BLOCKING_MANAGER_H_ diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_blocking_manager_test.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_blocking_manager_test.cc new file mode 100644 index 00000000000..6f78d87bd54 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_blocking_manager_test.cc @@ -0,0 +1,293 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/third_party/quiche/src/quic/core/qpack/qpack_blocking_manager.h" + +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" + +namespace quic { +namespace test { +namespace { + +class QpackBlockingManagerTest : public QuicTest { + protected: + QpackBlockingManagerTest() = default; + ~QpackBlockingManagerTest() override = default; + + QpackBlockingManager manager_; +}; + +TEST_F(QpackBlockingManagerTest, Empty) { + EXPECT_EQ(0u, manager_.blocked_stream_count()); + EXPECT_EQ(0u, manager_.known_received_count()); + EXPECT_EQ(std::numeric_limits<uint64_t>::max(), + manager_.smallest_blocking_index()); + + EXPECT_FALSE(manager_.OnHeaderAcknowledgement(0)); + EXPECT_FALSE(manager_.OnHeaderAcknowledgement(1)); +} + +TEST_F(QpackBlockingManagerTest, NotBlockedByInsertCountIncrement) { + manager_.OnInsertCountIncrement(2); + + // Stream 0 is not blocked, because it only references entries that are + // already acknowledged by an Insert Count Increment instruction. + manager_.OnHeaderBlockSent(0, {1, 0}); + EXPECT_EQ(0u, manager_.blocked_stream_count()); +} + +TEST_F(QpackBlockingManagerTest, UnblockedByInsertCountIncrement) { + manager_.OnHeaderBlockSent(0, {1, 0}); + EXPECT_EQ(1u, manager_.blocked_stream_count()); + + manager_.OnInsertCountIncrement(2); + EXPECT_EQ(0u, manager_.blocked_stream_count()); +} + +TEST_F(QpackBlockingManagerTest, NotBlockedByHeaderAcknowledgement) { + manager_.OnHeaderBlockSent(0, {2, 1, 1}); + EXPECT_EQ(1u, manager_.blocked_stream_count()); + + EXPECT_TRUE(manager_.OnHeaderAcknowledgement(0)); + EXPECT_EQ(0u, manager_.blocked_stream_count()); + + // Stream 1 is not blocked, because it only references entries that are + // already acknowledged by a Header Acknowledgement instruction. + manager_.OnHeaderBlockSent(1, {2, 2}); + EXPECT_EQ(0u, manager_.blocked_stream_count()); +} + +TEST_F(QpackBlockingManagerTest, UnblockedByHeaderAcknowledgement) { + manager_.OnHeaderBlockSent(0, {2, 1, 1}); + manager_.OnHeaderBlockSent(1, {2, 2}); + EXPECT_EQ(2u, manager_.blocked_stream_count()); + + EXPECT_TRUE(manager_.OnHeaderAcknowledgement(0)); + EXPECT_EQ(0u, manager_.blocked_stream_count()); +} + +TEST_F(QpackBlockingManagerTest, KnownReceivedCount) { + EXPECT_EQ(0u, manager_.known_received_count()); + + // Sending a header block does not change Known Received Count. + manager_.OnHeaderBlockSent(0, {0}); + EXPECT_EQ(0u, manager_.known_received_count()); + + manager_.OnHeaderBlockSent(1, {1}); + EXPECT_EQ(0u, manager_.known_received_count()); + + // Header Acknowledgement might increase Known Received Count. + EXPECT_TRUE(manager_.OnHeaderAcknowledgement(0)); + EXPECT_EQ(1u, manager_.known_received_count()); + + manager_.OnHeaderBlockSent(2, {5}); + EXPECT_EQ(1u, manager_.known_received_count()); + + EXPECT_TRUE(manager_.OnHeaderAcknowledgement(1)); + EXPECT_EQ(2u, manager_.known_received_count()); + + // Insert Count Increment increases Known Received Count. + manager_.OnInsertCountIncrement(2); + EXPECT_EQ(4u, manager_.known_received_count()); + + EXPECT_TRUE(manager_.OnHeaderAcknowledgement(2)); + EXPECT_EQ(6u, manager_.known_received_count()); + + // Stream Cancellation does not change Known Received Count. + manager_.OnStreamCancellation(0); + EXPECT_EQ(6u, manager_.known_received_count()); + + // Header Acknowledgement of a block with smaller Required Insert Count does + // not increase Known Received Count. + manager_.OnHeaderBlockSent(0, {3}); + EXPECT_EQ(6u, manager_.known_received_count()); + + EXPECT_TRUE(manager_.OnHeaderAcknowledgement(0)); + EXPECT_EQ(6u, manager_.known_received_count()); + + // Header Acknowledgement of a block with equal Required Insert Count does not + // increase Known Received Count. + manager_.OnHeaderBlockSent(1, {5}); + EXPECT_EQ(6u, manager_.known_received_count()); + + EXPECT_TRUE(manager_.OnHeaderAcknowledgement(1)); + EXPECT_EQ(6u, manager_.known_received_count()); +} + +TEST_F(QpackBlockingManagerTest, SmallestBlockingIndex) { + EXPECT_EQ(std::numeric_limits<uint64_t>::max(), + manager_.smallest_blocking_index()); + + manager_.OnHeaderBlockSent(0, {0}); + EXPECT_EQ(0u, manager_.smallest_blocking_index()); + + manager_.OnHeaderBlockSent(1, {2}); + EXPECT_EQ(0u, manager_.smallest_blocking_index()); + + EXPECT_TRUE(manager_.OnHeaderAcknowledgement(0)); + EXPECT_EQ(2u, manager_.smallest_blocking_index()); + + manager_.OnHeaderBlockSent(1, {1}); + EXPECT_EQ(1u, manager_.smallest_blocking_index()); + + EXPECT_TRUE(manager_.OnHeaderAcknowledgement(1)); + EXPECT_EQ(1u, manager_.smallest_blocking_index()); + + // Insert Count Increment does not change smallest blocking index. + manager_.OnInsertCountIncrement(2); + EXPECT_EQ(1u, manager_.smallest_blocking_index()); + + manager_.OnStreamCancellation(1); + EXPECT_EQ(std::numeric_limits<uint64_t>::max(), + manager_.smallest_blocking_index()); +} + +TEST_F(QpackBlockingManagerTest, HeaderAcknowledgementsOnSingleStream) { + EXPECT_EQ(0u, manager_.known_received_count()); + EXPECT_EQ(0u, manager_.blocked_stream_count()); + EXPECT_EQ(std::numeric_limits<uint64_t>::max(), + manager_.smallest_blocking_index()); + + manager_.OnHeaderBlockSent(0, {2, 1, 1}); + EXPECT_EQ(0u, manager_.known_received_count()); + EXPECT_EQ(1u, manager_.blocked_stream_count()); + EXPECT_EQ(1u, manager_.smallest_blocking_index()); + + manager_.OnHeaderBlockSent(0, {1, 0}); + EXPECT_EQ(0u, manager_.known_received_count()); + EXPECT_EQ(1u, manager_.blocked_stream_count()); + EXPECT_EQ(0u, manager_.smallest_blocking_index()); + + EXPECT_TRUE(manager_.OnHeaderAcknowledgement(0)); + EXPECT_EQ(3u, manager_.known_received_count()); + EXPECT_EQ(0u, manager_.blocked_stream_count()); + EXPECT_EQ(0u, manager_.smallest_blocking_index()); + + manager_.OnHeaderBlockSent(0, {3}); + EXPECT_EQ(3u, manager_.known_received_count()); + EXPECT_EQ(1u, manager_.blocked_stream_count()); + EXPECT_EQ(0u, manager_.smallest_blocking_index()); + + EXPECT_TRUE(manager_.OnHeaderAcknowledgement(0)); + EXPECT_EQ(3u, manager_.known_received_count()); + EXPECT_EQ(1u, manager_.blocked_stream_count()); + EXPECT_EQ(3u, manager_.smallest_blocking_index()); + + EXPECT_TRUE(manager_.OnHeaderAcknowledgement(0)); + EXPECT_EQ(4u, manager_.known_received_count()); + EXPECT_EQ(0u, manager_.blocked_stream_count()); + EXPECT_EQ(std::numeric_limits<uint64_t>::max(), + manager_.smallest_blocking_index()); + + EXPECT_FALSE(manager_.OnHeaderAcknowledgement(0)); +} + +TEST_F(QpackBlockingManagerTest, CancelStream) { + manager_.OnHeaderBlockSent(0, {3}); + EXPECT_EQ(1u, manager_.blocked_stream_count()); + EXPECT_EQ(3u, manager_.smallest_blocking_index()); + + manager_.OnHeaderBlockSent(0, {2}); + EXPECT_EQ(1u, manager_.blocked_stream_count()); + EXPECT_EQ(2u, manager_.smallest_blocking_index()); + + manager_.OnHeaderBlockSent(1, {4}); + EXPECT_EQ(2u, manager_.blocked_stream_count()); + EXPECT_EQ(2u, manager_.smallest_blocking_index()); + + manager_.OnStreamCancellation(0); + EXPECT_EQ(1u, manager_.blocked_stream_count()); + EXPECT_EQ(4u, manager_.smallest_blocking_index()); + + manager_.OnStreamCancellation(1); + EXPECT_EQ(0u, manager_.blocked_stream_count()); + EXPECT_EQ(std::numeric_limits<uint64_t>::max(), + manager_.smallest_blocking_index()); +} + +TEST_F(QpackBlockingManagerTest, + ReferenceOnEncoderStreamUnblockedByInsertCountIncrement) { + EXPECT_EQ(0u, manager_.known_received_count()); + EXPECT_EQ(std::numeric_limits<uint64_t>::max(), + manager_.smallest_blocking_index()); + + // Entry 1 refers to entry 0. + manager_.OnReferenceSentOnEncoderStream(1, 0); + // Entry 2 also refers to entry 0. + manager_.OnReferenceSentOnEncoderStream(2, 0); + + EXPECT_EQ(0u, manager_.known_received_count()); + EXPECT_EQ(0u, manager_.smallest_blocking_index()); + + // Acknowledging entry 1 still leaves one unacknowledged reference to entry 0. + manager_.OnInsertCountIncrement(2); + + EXPECT_EQ(2u, manager_.known_received_count()); + EXPECT_EQ(0u, manager_.smallest_blocking_index()); + + // Entry 3 also refers to entry 2. + manager_.OnReferenceSentOnEncoderStream(3, 2); + + EXPECT_EQ(2u, manager_.known_received_count()); + EXPECT_EQ(0u, manager_.smallest_blocking_index()); + + // Acknowledging entry 2 removes last reference to entry 0. + manager_.OnInsertCountIncrement(1); + + EXPECT_EQ(3u, manager_.known_received_count()); + EXPECT_EQ(2u, manager_.smallest_blocking_index()); + + // Acknowledging entry 4 (and implicitly 3) removes reference to entry 2. + manager_.OnInsertCountIncrement(2); + + EXPECT_EQ(5u, manager_.known_received_count()); + EXPECT_EQ(std::numeric_limits<uint64_t>::max(), + manager_.smallest_blocking_index()); +} + +TEST_F(QpackBlockingManagerTest, + ReferenceOnEncoderStreamUnblockedByHeaderAcknowledgement) { + EXPECT_EQ(0u, manager_.known_received_count()); + EXPECT_EQ(std::numeric_limits<uint64_t>::max(), + manager_.smallest_blocking_index()); + + // Entry 1 refers to entry 0. + manager_.OnReferenceSentOnEncoderStream(1, 0); + // Entry 2 also refers to entry 0. + manager_.OnReferenceSentOnEncoderStream(2, 0); + + EXPECT_EQ(0u, manager_.known_received_count()); + EXPECT_EQ(0u, manager_.smallest_blocking_index()); + + // Acknowledging a header block with entries up to 1 still leave one + // unacknowledged reference to entry 0. + manager_.OnHeaderBlockSent(/* stream_id = */ 0, {0, 1}); + manager_.OnHeaderAcknowledgement(/* stream_id = */ 0); + + EXPECT_EQ(2u, manager_.known_received_count()); + EXPECT_EQ(0u, manager_.smallest_blocking_index()); + + // Entry 3 also refers to entry 2. + manager_.OnReferenceSentOnEncoderStream(3, 2); + + // Acknowledging a header block with entries up to 2 removes last reference to + // entry 0. + manager_.OnHeaderBlockSent(/* stream_id = */ 0, {2, 0, 2}); + manager_.OnHeaderAcknowledgement(/* stream_id = */ 0); + + EXPECT_EQ(3u, manager_.known_received_count()); + EXPECT_EQ(2u, manager_.smallest_blocking_index()); + + // Acknowledging entry 4 (and implicitly 3) removes reference to entry 2. + manager_.OnHeaderBlockSent(/* stream_id = */ 0, {1, 4, 2, 0}); + manager_.OnHeaderAcknowledgement(/* stream_id = */ 0); + + EXPECT_EQ(5u, manager_.known_received_count()); + EXPECT_EQ(std::numeric_limits<uint64_t>::max(), + manager_.smallest_blocking_index()); +} + +} // namespace +} // namespace test +} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoded_headers_accumulator_test.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoded_headers_accumulator_test.cc index 0898327c6be..1d60660e9c4 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoded_headers_accumulator_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoded_headers_accumulator_test.cc @@ -31,6 +31,9 @@ const size_t kMaxHeaderListSize = 100; // Maximum dynamic table capacity. const size_t kMaxDynamicTableCapacity = 100; +// Maximum number of blocked streams. +const uint64_t kMaximumBlockedStreams = 1; + // Header Acknowledgement decoder stream instruction with stream_id = 1. const char* const kHeaderAcknowledgement = "\x81"; @@ -46,12 +49,16 @@ class NoopVisitor : public QpackDecodedHeadersAccumulator::Visitor { class QpackDecodedHeadersAccumulatorTest : public QuicTest { protected: QpackDecodedHeadersAccumulatorTest() - : qpack_decoder_(&encoder_stream_error_delegate_, - &decoder_stream_sender_delegate_), + : qpack_decoder_(kMaxDynamicTableCapacity, + kMaximumBlockedStreams, + &encoder_stream_error_delegate_), accumulator_(kTestStreamId, &qpack_decoder_, &visitor_, - kMaxHeaderListSize) {} + kMaxHeaderListSize) { + qpack_decoder_.set_qpack_stream_sender_delegate( + &decoder_stream_sender_delegate_); + } NoopEncoderStreamErrorDelegate encoder_stream_error_delegate_; StrictMock<MockQpackStreamSenderDelegate> decoder_stream_sender_delegate_; @@ -74,9 +81,6 @@ TEST_F(QpackDecodedHeadersAccumulatorTest, TruncatedHeaderBlockPrefix) { } TEST_F(QpackDecodedHeadersAccumulatorTest, EmptyHeaderList) { - EXPECT_CALL(decoder_stream_sender_delegate_, - WriteStreamData(Eq(kHeaderAcknowledgement))); - EXPECT_TRUE(accumulator_.Decode(QuicTextUtils::HexDecode("0000"))); EXPECT_EQ(Status::kSuccess, accumulator_.EndHeaderBlock()); @@ -98,9 +102,6 @@ TEST_F(QpackDecodedHeadersAccumulatorTest, InvalidPayload) { } TEST_F(QpackDecodedHeadersAccumulatorTest, Success) { - EXPECT_CALL(decoder_stream_sender_delegate_, - WriteStreamData(Eq(kHeaderAcknowledgement))); - std::string encoded_data(QuicTextUtils::HexDecode("000023666f6f03626172")); EXPECT_TRUE(accumulator_.Decode(encoded_data)); EXPECT_EQ(Status::kSuccess, accumulator_.EndHeaderBlock()); @@ -114,9 +115,6 @@ TEST_F(QpackDecodedHeadersAccumulatorTest, Success) { } TEST_F(QpackDecodedHeadersAccumulatorTest, ExceedingLimit) { - EXPECT_CALL(decoder_stream_sender_delegate_, - WriteStreamData(Eq(kHeaderAcknowledgement))); - // Total length of header list exceeds kMaxHeaderListSize. EXPECT_TRUE(accumulator_.Decode(QuicTextUtils::HexDecode( "0000" // header block prefix @@ -132,12 +130,12 @@ TEST_F(QpackDecodedHeadersAccumulatorTest, ExceedingLimit) { } TEST_F(QpackDecodedHeadersAccumulatorTest, BlockedDecoding) { - qpack_decoder_.SetMaximumDynamicTableCapacity(kMaxDynamicTableCapacity); - // Reference to dynamic table entry not yet received. EXPECT_TRUE(accumulator_.Decode(QuicTextUtils::HexDecode("020080"))); EXPECT_EQ(Status::kBlocked, accumulator_.EndHeaderBlock()); + // Set dynamic table capacity. + qpack_decoder_.OnSetDynamicTableCapacity(kMaxDynamicTableCapacity); // Adding dynamic table entry unblocks decoding. EXPECT_CALL(decoder_stream_sender_delegate_, WriteStreamData(Eq(kHeaderAcknowledgement))); @@ -148,11 +146,11 @@ TEST_F(QpackDecodedHeadersAccumulatorTest, BlockedDecoding) { TEST_F(QpackDecodedHeadersAccumulatorTest, BlockedDecodingUnblockedBeforeEndOfHeaderBlock) { - qpack_decoder_.SetMaximumDynamicTableCapacity(kMaxDynamicTableCapacity); - // Reference to dynamic table entry not yet received. EXPECT_TRUE(accumulator_.Decode(QuicTextUtils::HexDecode("020080"))); + // Set dynamic table capacity. + qpack_decoder_.OnSetDynamicTableCapacity(kMaxDynamicTableCapacity); // Adding dynamic table entry unblocks decoding. qpack_decoder_.OnInsertWithoutNameReference("foo", "bar"); diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder.cc index d0e5ad948a5..b20cbdf9d45 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder.cc +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder.cc @@ -4,36 +4,41 @@ #include "net/third_party/quiche/src/quic/core/qpack/qpack_decoder.h" -#include <limits> - +#include "net/third_party/quiche/src/quic/core/qpack/qpack_index_conversions.h" #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" #include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" namespace quic { QpackDecoder::QpackDecoder( - EncoderStreamErrorDelegate* encoder_stream_error_delegate, - QpackStreamSenderDelegate* decoder_stream_sender_delegate) + uint64_t maximum_dynamic_table_capacity, + uint64_t maximum_blocked_streams, + EncoderStreamErrorDelegate* encoder_stream_error_delegate) : encoder_stream_error_delegate_(encoder_stream_error_delegate), encoder_stream_receiver_(this), - decoder_stream_sender_(decoder_stream_sender_delegate) { + maximum_blocked_streams_(maximum_blocked_streams) { DCHECK(encoder_stream_error_delegate_); - DCHECK(decoder_stream_sender_delegate); -} - -QpackDecoder::~QpackDecoder() {} -void QpackDecoder::SetMaximumDynamicTableCapacity( - uint64_t maximum_dynamic_table_capacity) { header_table_.SetMaximumDynamicTableCapacity(maximum_dynamic_table_capacity); } +QpackDecoder::~QpackDecoder() {} + void QpackDecoder::OnStreamReset(QuicStreamId stream_id) { + // TODO(bnc): SendStreamCancellation should not be called if maximum dynamic + // table capacity is zero. decoder_stream_sender_.SendStreamCancellation(stream_id); } -void QpackDecoder::DecodeEncoderStreamData(QuicStringPiece data) { - encoder_stream_receiver_.Decode(data); +bool QpackDecoder::OnStreamBlocked(QuicStreamId stream_id) { + auto result = blocked_streams_.insert(stream_id); + DCHECK(result.second); + return blocked_streams_.size() <= maximum_blocked_streams_; +} + +void QpackDecoder::OnStreamUnblocked(QuicStreamId stream_id) { + size_t result = blocked_streams_.erase(stream_id); + DCHECK_EQ(1u, result); } void QpackDecoder::OnInsertWithNameReference(bool is_static, @@ -56,7 +61,8 @@ void QpackDecoder::OnInsertWithNameReference(bool is_static, } uint64_t absolute_index; - if (!EncoderStreamRelativeIndexToAbsoluteIndex(name_index, &absolute_index)) { + if (!QpackEncoderStreamRelativeIndexToAbsoluteIndex( + name_index, header_table_.inserted_entry_count(), &absolute_index)) { encoder_stream_error_delegate_->OnEncoderStreamError( "Invalid relative index."); return; @@ -87,7 +93,8 @@ void QpackDecoder::OnInsertWithoutNameReference(QuicStringPiece name, void QpackDecoder::OnDuplicate(uint64_t index) { uint64_t absolute_index; - if (!EncoderStreamRelativeIndexToAbsoluteIndex(index, &absolute_index)) { + if (!QpackEncoderStreamRelativeIndexToAbsoluteIndex( + index, header_table_.inserted_entry_count(), &absolute_index)) { encoder_stream_error_delegate_->OnEncoderStreamError( "Invalid relative index."); return; @@ -118,24 +125,11 @@ void QpackDecoder::OnErrorDetected(QuicStringPiece error_message) { encoder_stream_error_delegate_->OnEncoderStreamError(error_message); } -bool QpackDecoder::EncoderStreamRelativeIndexToAbsoluteIndex( - uint64_t relative_index, - uint64_t* absolute_index) const { - if (relative_index == std::numeric_limits<uint64_t>::max() || - relative_index + 1 > std::numeric_limits<uint64_t>::max() - - header_table_.inserted_entry_count()) { - return false; - } - - *absolute_index = header_table_.inserted_entry_count() - relative_index - 1; - return true; -} - std::unique_ptr<QpackProgressiveDecoder> QpackDecoder::CreateProgressiveDecoder( QuicStreamId stream_id, QpackProgressiveDecoder::HeadersHandlerInterface* handler) { return QuicMakeUnique<QpackProgressiveDecoder>( - stream_id, &header_table_, &decoder_stream_sender_, handler); + stream_id, this, &header_table_, &decoder_stream_sender_, handler); } } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder.h b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder.h index 9f8a3e38de1..102fbc4c41e 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder.h +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder.h @@ -7,6 +7,7 @@ #include <cstdint> #include <memory> +#include <set> #include "net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_sender.h" #include "net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_receiver.h" @@ -22,7 +23,8 @@ namespace quic { // This class vends a new QpackProgressiveDecoder instance for each new header // list to be encoded. class QUIC_EXPORT_PRIVATE QpackDecoder - : public QpackEncoderStreamReceiver::Delegate { + : public QpackEncoderStreamReceiver::Delegate, + public QpackProgressiveDecoder::BlockedStreamLimitEnforcer { public: // Interface for receiving notification that an error has occurred on the // encoder stream. This MUST be treated as a connection error of type @@ -34,14 +36,11 @@ class QUIC_EXPORT_PRIVATE QpackDecoder virtual void OnEncoderStreamError(QuicStringPiece error_message) = 0; }; - QpackDecoder(EncoderStreamErrorDelegate* encoder_stream_error_delegate, - QpackStreamSenderDelegate* decoder_stream_sender_delegate); + QpackDecoder(uint64_t maximum_dynamic_table_capacity, + uint64_t maximum_blocked_streams, + EncoderStreamErrorDelegate* encoder_stream_error_delegate); ~QpackDecoder() override; - // Set maximum capacity of dynamic table. - // This method must only be called at most once. - void SetMaximumDynamicTableCapacity(uint64_t maximum_dynamic_table_capacity); - // Signal to the peer's encoder that a stream is reset. This lets the peer's // encoder know that no more header blocks will be processed on this stream, // therefore references to dynamic table entries shall not prevent their @@ -60,6 +59,10 @@ class QUIC_EXPORT_PRIVATE QpackDecoder // using the FIN bit. void OnStreamReset(QuicStreamId stream_id); + // QpackProgressiveDecoder::BlockedStreamLimitEnforcer implementation. + bool OnStreamBlocked(QuicStreamId stream_id) override; + void OnStreamUnblocked(QuicStreamId stream_id) override; + // Factory method to create a QpackProgressiveDecoder for decoding a header // block. |handler| must remain valid until the returned // QpackProgressiveDecoder instance is destroyed or the decoder calls @@ -68,9 +71,6 @@ class QUIC_EXPORT_PRIVATE QpackDecoder QuicStreamId stream_id, QpackProgressiveDecoder::HeadersHandlerInterface* handler); - // Decode data received on the encoder stream. - void DecodeEncoderStreamData(QuicStringPiece data); - // QpackEncoderStreamReceiver::Delegate implementation void OnInsertWithNameReference(bool is_static, uint64_t name_index, @@ -81,19 +81,22 @@ class QUIC_EXPORT_PRIVATE QpackDecoder void OnSetDynamicTableCapacity(uint64_t capacity) override; void OnErrorDetected(QuicStringPiece error_message) override; - private: - // The encoder stream uses relative index (but different from the kind of - // relative index used on a request stream). This method converts relative - // index to absolute index (zero based). It returns true on success, or false - // if conversion fails due to overflow/underflow. - bool EncoderStreamRelativeIndexToAbsoluteIndex( - uint64_t relative_index, - uint64_t* absolute_index) const; + // delegate must be set if dynamic table capacity is not zero. + void set_qpack_stream_sender_delegate(QpackStreamSenderDelegate* delegate) { + decoder_stream_sender_.set_qpack_stream_sender_delegate(delegate); + } + + QpackStreamReceiver* encoder_stream_receiver() { + return &encoder_stream_receiver_; + } + private: EncoderStreamErrorDelegate* const encoder_stream_error_delegate_; QpackEncoderStreamReceiver encoder_stream_receiver_; QpackDecoderStreamSender decoder_stream_sender_; QpackHeaderTable header_table_; + std::set<QuicStreamId> blocked_streams_; + const uint64_t maximum_blocked_streams_; }; } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_sender.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_sender.cc index 63f17219928..7f3f346ca5d 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_sender.cc +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_sender.cc @@ -13,11 +13,7 @@ namespace quic { -QpackDecoderStreamSender::QpackDecoderStreamSender( - QpackStreamSenderDelegate* delegate) - : delegate_(delegate) { - DCHECK(delegate_); -} +QpackDecoderStreamSender::QpackDecoderStreamSender() : delegate_(nullptr) {} void QpackDecoderStreamSender::SendInsertCountIncrement(uint64_t increment) { values_.varint = increment; diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_sender.h b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_sender.h index 5f17744f207..6bf466f9276 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_sender.h +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_sender.h @@ -19,8 +19,7 @@ namespace quic { // stream. class QUIC_EXPORT_PRIVATE QpackDecoderStreamSender { public: - explicit QpackDecoderStreamSender(QpackStreamSenderDelegate* delegate); - QpackDecoderStreamSender() = delete; + QpackDecoderStreamSender(); QpackDecoderStreamSender(const QpackDecoderStreamSender&) = delete; QpackDecoderStreamSender& operator=(const QpackDecoderStreamSender&) = delete; @@ -34,8 +33,13 @@ class QUIC_EXPORT_PRIVATE QpackDecoderStreamSender { // 5.3.3 Stream Cancellation void SendStreamCancellation(QuicStreamId stream_id); + // delegate must be set if dynamic table capacity is not zero. + void set_qpack_stream_sender_delegate(QpackStreamSenderDelegate* delegate) { + delegate_ = delegate; + } + private: - QpackStreamSenderDelegate* const delegate_; + QpackStreamSenderDelegate* delegate_; QpackInstructionEncoder instruction_encoder_; QpackInstructionEncoder::Values values_; }; diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_sender_test.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_sender_test.cc index 49483f7f37d..ccb42a3c25d 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_sender_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_sender_test.cc @@ -17,7 +17,9 @@ namespace { class QpackDecoderStreamSenderTest : public QuicTest { protected: - QpackDecoderStreamSenderTest() : stream_(&delegate_) {} + QpackDecoderStreamSenderTest() { + stream_.set_qpack_stream_sender_delegate(&delegate_); + } ~QpackDecoderStreamSenderTest() override = default; StrictMock<MockQpackStreamSenderDelegate> delegate_; diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test.cc index af3a6a250c1..ef711b969fb 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test.cc @@ -26,25 +26,34 @@ namespace { // Header Acknowledgement decoder stream instruction with stream_id = 1. const char* const kHeaderAcknowledgement = "\x81"; +const uint64_t kMaximumDynamicTableCapacity = 1024; +const uint64_t kMaximumBlockedStreams = 1; + class QpackDecoderTest : public QuicTestWithParam<FragmentMode> { protected: QpackDecoderTest() - : qpack_decoder_(&encoder_stream_error_delegate_, - &decoder_stream_sender_delegate_), + : qpack_decoder_(kMaximumDynamicTableCapacity, + kMaximumBlockedStreams, + &encoder_stream_error_delegate_), fragment_mode_(GetParam()) { - qpack_decoder_.SetMaximumDynamicTableCapacity(1024); + qpack_decoder_.set_qpack_stream_sender_delegate( + &decoder_stream_sender_delegate_); } ~QpackDecoderTest() override = default; void DecodeEncoderStreamData(QuicStringPiece data) { - qpack_decoder_.DecodeEncoderStreamData(data); + qpack_decoder_.encoder_stream_receiver()->Decode(data); + } + + std::unique_ptr<QpackProgressiveDecoder> CreateProgressiveDecoder( + QuicStreamId stream_id) { + return qpack_decoder_.CreateProgressiveDecoder(stream_id, &handler_); } // Set up |progressive_decoder_|. void StartDecoding() { - progressive_decoder_ = - qpack_decoder_.CreateProgressiveDecoder(/* stream_id = */ 1, &handler_); + progressive_decoder_ = CreateProgressiveDecoder(/* stream_id = */ 1); } // Pass header block data to QpackProgressiveDecoder::Decode() @@ -98,8 +107,6 @@ TEST_P(QpackDecoderTest, NoPrefix) { TEST_P(QpackDecoderTest, EmptyHeaderBlock) { EXPECT_CALL(handler_, OnDecodingCompleted()); - EXPECT_CALL(decoder_stream_sender_delegate_, - WriteStreamData(Eq(kHeaderAcknowledgement))); DecodeHeaderBlock(QuicTextUtils::HexDecode("0000")); } @@ -107,8 +114,6 @@ TEST_P(QpackDecoderTest, EmptyHeaderBlock) { TEST_P(QpackDecoderTest, LiteralEntryEmptyName) { EXPECT_CALL(handler_, OnHeaderDecoded(Eq(""), Eq("foo"))); EXPECT_CALL(handler_, OnDecodingCompleted()); - EXPECT_CALL(decoder_stream_sender_delegate_, - WriteStreamData(Eq(kHeaderAcknowledgement))); DecodeHeaderBlock(QuicTextUtils::HexDecode("00002003666f6f")); } @@ -116,8 +121,6 @@ TEST_P(QpackDecoderTest, LiteralEntryEmptyName) { TEST_P(QpackDecoderTest, LiteralEntryEmptyValue) { EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq(""))); EXPECT_CALL(handler_, OnDecodingCompleted()); - EXPECT_CALL(decoder_stream_sender_delegate_, - WriteStreamData(Eq(kHeaderAcknowledgement))); DecodeHeaderBlock(QuicTextUtils::HexDecode("000023666f6f00")); } @@ -125,8 +128,6 @@ TEST_P(QpackDecoderTest, LiteralEntryEmptyValue) { TEST_P(QpackDecoderTest, LiteralEntryEmptyNameAndValue) { EXPECT_CALL(handler_, OnHeaderDecoded(Eq(""), Eq(""))); EXPECT_CALL(handler_, OnDecodingCompleted()); - EXPECT_CALL(decoder_stream_sender_delegate_, - WriteStreamData(Eq(kHeaderAcknowledgement))); DecodeHeaderBlock(QuicTextUtils::HexDecode("00002000")); } @@ -134,8 +135,6 @@ TEST_P(QpackDecoderTest, LiteralEntryEmptyNameAndValue) { TEST_P(QpackDecoderTest, SimpleLiteralEntry) { EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("bar"))); EXPECT_CALL(handler_, OnDecodingCompleted()); - EXPECT_CALL(decoder_stream_sender_delegate_, - WriteStreamData(Eq(kHeaderAcknowledgement))); DecodeHeaderBlock(QuicTextUtils::HexDecode("000023666f6f03626172")); } @@ -145,8 +144,6 @@ TEST_P(QpackDecoderTest, MultipleLiteralEntries) { std::string str(127, 'a'); EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foobaar"), QuicStringPiece(str))); EXPECT_CALL(handler_, OnDecodingCompleted()); - EXPECT_CALL(decoder_stream_sender_delegate_, - WriteStreamData(Eq(kHeaderAcknowledgement))); DecodeHeaderBlock(QuicTextUtils::HexDecode( "0000" // prefix @@ -204,8 +201,6 @@ TEST_P(QpackDecoderTest, IncompleteHeaderBlock) { TEST_P(QpackDecoderTest, HuffmanSimple) { EXPECT_CALL(handler_, OnHeaderDecoded(Eq("custom-key"), Eq("custom-value"))); EXPECT_CALL(handler_, OnDecodingCompleted()); - EXPECT_CALL(decoder_stream_sender_delegate_, - WriteStreamData(Eq(kHeaderAcknowledgement))); DecodeHeaderBlock( QuicTextUtils::HexDecode("00002f0125a849e95ba97d7f8925a849e95bb8e8b4bf")); @@ -215,8 +210,6 @@ TEST_P(QpackDecoderTest, AlternatingHuffmanNonHuffman) { EXPECT_CALL(handler_, OnHeaderDecoded(Eq("custom-key"), Eq("custom-value"))) .Times(4); EXPECT_CALL(handler_, OnDecodingCompleted()); - EXPECT_CALL(decoder_stream_sender_delegate_, - WriteStreamData(Eq(kHeaderAcknowledgement))); DecodeHeaderBlock(QuicTextUtils::HexDecode( "0000" // Prefix. @@ -289,8 +282,6 @@ TEST_P(QpackDecoderTest, StaticTable) { EXPECT_CALL(handler_, OnHeaderDecoded(Eq("location"), Eq("foo"))); EXPECT_CALL(handler_, OnDecodingCompleted()); - EXPECT_CALL(decoder_stream_sender_delegate_, - WriteStreamData(Eq(kHeaderAcknowledgement))); DecodeHeaderBlock(QuicTextUtils::HexDecode( "0000d1dfccd45f108621e9aec2a11f5c8294e75f000554524143455f1000")); @@ -310,6 +301,7 @@ TEST_P(QpackDecoderTest, TooHighStaticTableIndex) { TEST_P(QpackDecoderTest, DynamicTable) { DecodeEncoderStreamData(QuicTextUtils::HexDecode( + "3fe107" // Set dynamic table capacity to 1024. "6294e703626172" // Add literal entry with name "foo" and value "bar". "80035a5a5a" // Add entry with name of dynamic table entry index 0 // (relative index) and value "ZZZ". @@ -391,6 +383,8 @@ TEST_P(QpackDecoderTest, DynamicTable) { } TEST_P(QpackDecoderTest, DecreasingDynamicTableCapacityEvictsEntries) { + // Set dynamic table capacity to 1024. + DecodeEncoderStreamData(QuicTextUtils::HexDecode("3fe107")); // Add literal entry with name "foo" and value "bar". DecodeEncoderStreamData(QuicTextUtils::HexDecode("6294e703626172")); @@ -437,9 +431,10 @@ TEST_P(QpackDecoderTest, EncoderStreamErrorInvalidStaticTableEntry) { TEST_P(QpackDecoderTest, EncoderStreamErrorInvalidDynamicTableEntry) { EXPECT_CALL(encoder_stream_error_delegate_, - OnEncoderStreamError(Eq("Dynamic table entry not found."))); + OnEncoderStreamError(Eq("Invalid relative index."))); DecodeEncoderStreamData(QuicTextUtils::HexDecode( + "3fe107" // Set dynamic table capacity to 1024. "6294e703626172" // Add literal entry with name "foo" and value "bar". "8100")); // Address dynamic table entry with relative index 1. Such // entry does not exist. The most recently added and only @@ -448,9 +443,10 @@ TEST_P(QpackDecoderTest, EncoderStreamErrorInvalidDynamicTableEntry) { TEST_P(QpackDecoderTest, EncoderStreamErrorDuplicateInvalidEntry) { EXPECT_CALL(encoder_stream_error_delegate_, - OnEncoderStreamError(Eq("Dynamic table entry not found."))); + OnEncoderStreamError(Eq("Invalid relative index."))); DecodeEncoderStreamData(QuicTextUtils::HexDecode( + "3fe107" // Set dynamic table capacity to 1024. "6294e703626172" // Add literal entry with name "foo" and value "bar". "01")); // Duplicate dynamic table entry with relative index 1. Such // entry does not exist. The most recently added and only @@ -467,6 +463,8 @@ TEST_P(QpackDecoderTest, EncoderStreamErrorTooLargeInteger) { TEST_P(QpackDecoderTest, InvalidDynamicEntryWhenBaseIsZero) { EXPECT_CALL(handler_, OnDecodingErrorDetected(Eq("Invalid relative index."))); + // Set dynamic table capacity to 1024. + DecodeEncoderStreamData(QuicTextUtils::HexDecode("3fe107")); // Add literal entry with name "foo" and value "bar". DecodeEncoderStreamData(QuicTextUtils::HexDecode("6294e703626172")); @@ -486,6 +484,8 @@ TEST_P(QpackDecoderTest, InvalidNegativeBase) { } TEST_P(QpackDecoderTest, InvalidDynamicEntryByRelativeIndex) { + // Set dynamic table capacity to 1024. + DecodeEncoderStreamData(QuicTextUtils::HexDecode("3fe107")); // Add literal entry with name "foo" and value "bar". DecodeEncoderStreamData(QuicTextUtils::HexDecode("6294e703626172")); @@ -566,7 +566,7 @@ TEST_P(QpackDecoderTest, TableCapacityMustNotExceedMaximum) { DecodeEncoderStreamData(QuicTextUtils::HexDecode("3fe10f")); } -TEST_P(QpackDecoderTest, SetMaximumDynamicTableCapacity) { +TEST_P(QpackDecoderTest, SetDynamicTableCapacity) { // Update dynamic table capacity to 128, which does not exceed the maximum. DecodeEncoderStreamData(QuicTextUtils::HexDecode("3f61")); } @@ -594,6 +594,8 @@ TEST_P(QpackDecoderTest, WrappedRequiredInsertCount) { // Maximum dynamic table capacity is 1024. // MaxEntries is 1024 / 32 = 32. + // Set dynamic table capacity to 1024. + DecodeEncoderStreamData(QuicTextUtils::HexDecode("3fe107")); // Add literal entry with name "foo" and a 600 byte long value. This will fit // in the dynamic table once but not twice. DecodeEncoderStreamData( @@ -620,6 +622,8 @@ TEST_P(QpackDecoderTest, WrappedRequiredInsertCount) { } TEST_P(QpackDecoderTest, NonZeroRequiredInsertCountButNoDynamicEntries) { + // Set dynamic table capacity to 1024. + DecodeEncoderStreamData(QuicTextUtils::HexDecode("3fe107")); // Add literal entry with name "foo" and value "bar". DecodeEncoderStreamData(QuicTextUtils::HexDecode("6294e703626172")); @@ -633,6 +637,8 @@ TEST_P(QpackDecoderTest, NonZeroRequiredInsertCountButNoDynamicEntries) { } TEST_P(QpackDecoderTest, AddressEntryNotAllowedByRequiredInsertCount) { + // Set dynamic table capacity to 1024. + DecodeEncoderStreamData(QuicTextUtils::HexDecode("3fe107")); // Add literal entry with name "foo" and value "bar". DecodeEncoderStreamData(QuicTextUtils::HexDecode("6294e703626172")); @@ -689,6 +695,8 @@ TEST_P(QpackDecoderTest, AddressEntryNotAllowedByRequiredInsertCount) { } TEST_P(QpackDecoderTest, PromisedRequiredInsertCountLargerThanActual) { + // Set dynamic table capacity to 1024. + DecodeEncoderStreamData(QuicTextUtils::HexDecode("3fe107")); // Add literal entry with name "foo" and value "bar". DecodeEncoderStreamData(QuicTextUtils::HexDecode("6294e703626172")); // Duplicate entry twice so that decoding of header blocks with Required @@ -757,6 +765,8 @@ TEST_P(QpackDecoderTest, BlockedDecoding) { EXPECT_CALL(decoder_stream_sender_delegate_, WriteStreamData(Eq(kHeaderAcknowledgement))); + // Set dynamic table capacity to 1024. + DecodeEncoderStreamData(QuicTextUtils::HexDecode("3fe107")); // Add literal entry with name "foo" and value "bar". DecodeEncoderStreamData(QuicTextUtils::HexDecode("6294e703626172")); } @@ -776,6 +786,10 @@ TEST_P(QpackDecoderTest, BlockedDecodingUnblockedBeforeEndOfHeaderBlock) { // the already consumed part of the header block. EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("bar"))); EXPECT_CALL(handler_, OnHeaderDecoded(Eq(":method"), Eq("GET"))); + + // Set dynamic table capacity to 1024. + DecodeEncoderStreamData(QuicTextUtils::HexDecode("3fe107")); + // Add literal entry with name "foo" and value "bar". DecodeEncoderStreamData(QuicTextUtils::HexDecode("6294e703626172")); Mock::VerifyAndClearExpectations(&handler_); @@ -825,6 +839,21 @@ TEST_P(QpackDecoderTest, BlockedDecodingAndEvictedEntries) { DecodeEncoderStreamData(QuicTextUtils::HexDecode("6294e70362617a")); } +TEST_P(QpackDecoderTest, TooManyBlockedStreams) { + // Required Insert Count 1 and Delta Base 0. + // Without any dynamic table entries received, decoding is blocked. + std::string data = QuicTextUtils::HexDecode("0200"); + + auto progressive_decoder1 = CreateProgressiveDecoder(/* stream_id = */ 1); + progressive_decoder1->Decode(data); + + EXPECT_CALL(handler_, OnDecodingErrorDetected(Eq( + "Limit on number of blocked streams exceeded."))); + + auto progressive_decoder2 = CreateProgressiveDecoder(/* stream_id = */ 2); + progressive_decoder2->Decode(data); +} + } // namespace } // namespace test } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test_utils.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test_utils.cc index 173270d75b6..eaf66648cb0 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test_utils.cc +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test_utils.cc @@ -64,13 +64,16 @@ const std::string& TestHeadersHandler::error_message() const { } void QpackDecode( + uint64_t maximum_dynamic_table_capacity, + uint64_t maximum_blocked_streams, QpackDecoder::EncoderStreamErrorDelegate* encoder_stream_error_delegate, QpackStreamSenderDelegate* decoder_stream_sender_delegate, QpackProgressiveDecoder::HeadersHandlerInterface* handler, const FragmentSizeGenerator& fragment_size_generator, QuicStringPiece data) { - QpackDecoder decoder(encoder_stream_error_delegate, - decoder_stream_sender_delegate); + QpackDecoder decoder(maximum_dynamic_table_capacity, maximum_blocked_streams, + encoder_stream_error_delegate); + decoder.set_qpack_stream_sender_delegate(decoder_stream_sender_delegate); auto progressive_decoder = decoder.CreateProgressiveDecoder(/* stream_id = */ 1, handler); while (!data.empty()) { diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test_utils.h b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test_utils.h index d5f494cfd37..6505b60a1fe 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test_utils.h +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test_utils.h @@ -89,6 +89,8 @@ class NoOpHeadersHandler }; void QpackDecode( + uint64_t maximum_dynamic_table_capacity, + uint64_t maximum_blocked_streams, QpackDecoder::EncoderStreamErrorDelegate* encoder_stream_error_delegate, QpackStreamSenderDelegate* decoder_stream_sender_delegate, QpackProgressiveDecoder::HeadersHandlerInterface* handler, diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder.cc index 3ffe9d07a5f..d1736f81bb6 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder.cc +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder.cc @@ -4,37 +4,112 @@ #include "net/third_party/quiche/src/quic/core/qpack/qpack_encoder.h" -#include <list> +#include <algorithm> +#include <utility> #include "net/third_party/quiche/src/quic/core/qpack/qpack_constants.h" +#include "net/third_party/quiche/src/quic/core/qpack/qpack_index_conversions.h" #include "net/third_party/quiche/src/quic/core/qpack/qpack_instruction_encoder.h" +#include "net/third_party/quiche/src/quic/core/qpack/qpack_required_insert_count.h" #include "net/third_party/quiche/src/quic/core/qpack/value_splitting_header_list.h" #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" #include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h" namespace quic { +namespace { + +// Fraction to calculate draining index. The oldest |kDrainingFraction| entries +// will not be referenced in header blocks. A new entry (duplicate or literal +// with name reference) will be added to the dynamic table instead. This allows +// the number of references to the draining entry to go to zero faster, so that +// it can be evicted. See +// https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#avoiding-blocked-insertions. +// TODO(bnc): Fine tune. +const float kDrainingFraction = 0.25; + +} // anonymous namespace + QpackEncoder::QpackEncoder( - DecoderStreamErrorDelegate* decoder_stream_error_delegate, - QpackStreamSenderDelegate* encoder_stream_sender_delegate) + DecoderStreamErrorDelegate* decoder_stream_error_delegate) : decoder_stream_error_delegate_(decoder_stream_error_delegate), decoder_stream_receiver_(this), - encoder_stream_sender_(encoder_stream_sender_delegate) { + maximum_blocked_streams_(0) { DCHECK(decoder_stream_error_delegate_); - DCHECK(encoder_stream_sender_delegate); } QpackEncoder::~QpackEncoder() {} -std::string QpackEncoder::EncodeHeaderList( - QuicStreamId /* stream_id */, - const spdy::SpdyHeaderBlock* header_list) { - // First pass. +// static +QpackEncoder::InstructionWithValues QpackEncoder::EncodeIndexedHeaderField( + bool is_static, + uint64_t index, + QpackBlockingManager::IndexSet* referred_indices) { + InstructionWithValues instruction{QpackIndexedHeaderFieldInstruction(), {}}; + instruction.values.s_bit = is_static; + instruction.values.varint = index; + // Add |index| to |*referred_indices| only if entry is in the dynamic table. + if (!is_static) { + referred_indices->insert(index); + } + return instruction; +} - // Encode into |instructions| which will be serialized during the second pass. - std::list<InstructionWithValues> instructions; +// static +QpackEncoder::InstructionWithValues +QpackEncoder::EncodeLiteralHeaderFieldWithNameReference( + bool is_static, + uint64_t index, + QuicStringPiece value, + QpackBlockingManager::IndexSet* referred_indices) { + InstructionWithValues instruction{ + QpackLiteralHeaderFieldNameReferenceInstruction(), {}}; + instruction.values.s_bit = is_static; + instruction.values.varint = index; + instruction.values.value = value; + // Add |index| to |*referred_indices| only if entry is in the dynamic table. + if (!is_static) { + referred_indices->insert(index); + } + return instruction; +} + +// static +QpackEncoder::InstructionWithValues QpackEncoder::EncodeLiteralHeaderField( + QuicStringPiece name, + QuicStringPiece value) { + InstructionWithValues instruction{QpackLiteralHeaderFieldInstruction(), {}}; + instruction.values.name = name; + instruction.values.value = value; + return instruction; +} - for (const auto& header : ValueSplittingHeaderList(header_list)) { +QpackEncoder::Instructions QpackEncoder::FirstPassEncode( + const spdy::SpdyHeaderBlock& header_list, + QpackBlockingManager::IndexSet* referred_indices) { + Instructions instructions; + instructions.reserve(header_list.size()); + + // The index of the oldest entry that must not be evicted. + uint64_t smallest_blocking_index = + blocking_manager_.smallest_blocking_index(); + // Entries with index larger than or equal to |known_received_count| are + // blocking. + const uint64_t known_received_count = + blocking_manager_.known_received_count(); + // Only entries with index greater than or equal to |draining_index| are + // allowed to be referenced. + const uint64_t draining_index = + header_table_.draining_index(kDrainingFraction); + // Blocking references are allowed if the number of blocked streams is less + // than the limit. + // TODO(b/112770235): Also allow blocking if given stream is already blocked. + const bool blocking_allowed = + maximum_blocked_streams_ > blocking_manager_.blocked_stream_count(); + + for (const auto& header : ValueSplittingHeaderList(&header_list)) { + // These strings are owned by |header_list|. QuicStringPiece name = header.first; QuicStringPiece value = header.second; @@ -46,48 +121,172 @@ std::string QpackEncoder::EncodeHeaderList( switch (match_type) { case QpackHeaderTable::MatchType::kNameAndValue: - DCHECK(is_static) << "Dynamic table entries not supported yet."; + if (is_static) { + // Refer to entry directly. + instructions.push_back( + EncodeIndexedHeaderField(is_static, index, referred_indices)); + + break; + } - instructions.push_back({QpackIndexedHeaderFieldInstruction(), {}}); - instructions.back().values.s_bit = is_static; - instructions.back().values.varint = index; + if (blocking_allowed || index < known_received_count) { + if (index >= draining_index) { + // If allowed, refer to entry directly. + instructions.push_back( + EncodeIndexedHeaderField(is_static, index, referred_indices)); + smallest_blocking_index = std::min(smallest_blocking_index, index); + + break; + } + + if (blocking_allowed && + QpackEntry::Size(name, value) <= + header_table_.MaxInsertSizeWithoutEvictingGivenEntry( + std::min(smallest_blocking_index, index))) { + // If allowed, duplicate entry and refer to it. + encoder_stream_sender_.SendDuplicate( + QpackAbsoluteIndexToEncoderStreamRelativeIndex( + index, header_table_.inserted_entry_count())); + auto entry = header_table_.InsertEntry(name, value); + blocking_manager_.OnReferenceSentOnEncoderStream( + entry->InsertionIndex(), index); + instructions.push_back(EncodeIndexedHeaderField( + is_static, entry->InsertionIndex(), referred_indices)); + smallest_blocking_index = std::min(smallest_blocking_index, index); + + break; + } + } + + // Encode entry as string literals. + // TODO(b/112770235): Use already acknowledged entry with lower index if + // exists. + // TODO(b/112770235): Use static entry name with literal value if + // dynamic entry exists but cannot be used. + instructions.push_back(EncodeLiteralHeaderField(name, value)); break; + case QpackHeaderTable::MatchType::kName: - DCHECK(is_static) << "Dynamic table entries not supported yet."; + if (is_static) { + if (blocking_allowed && + QpackEntry::Size(name, value) <= + header_table_.MaxInsertSizeWithoutEvictingGivenEntry( + smallest_blocking_index)) { + // If allowed, insert entry into dynamic table and refer to it. + encoder_stream_sender_.SendInsertWithNameReference(is_static, index, + value); + auto entry = header_table_.InsertEntry(name, value); + instructions.push_back(EncodeIndexedHeaderField( + /* is_static = */ false, entry->InsertionIndex(), + referred_indices)); + smallest_blocking_index = std::min<uint64_t>( + smallest_blocking_index, entry->InsertionIndex()); + + break; + } + + // Emit literal field with name reference. + instructions.push_back(EncodeLiteralHeaderFieldWithNameReference( + is_static, index, value, referred_indices)); + + break; + } + + if (blocking_allowed && + QpackEntry::Size(name, value) <= + header_table_.MaxInsertSizeWithoutEvictingGivenEntry( + std::min(smallest_blocking_index, index))) { + // If allowed, insert entry with name reference and refer to it. + encoder_stream_sender_.SendInsertWithNameReference( + is_static, + QpackAbsoluteIndexToEncoderStreamRelativeIndex( + index, header_table_.inserted_entry_count()), + value); + auto entry = header_table_.InsertEntry(name, value); + blocking_manager_.OnReferenceSentOnEncoderStream( + entry->InsertionIndex(), index); + instructions.push_back(EncodeIndexedHeaderField( + is_static, entry->InsertionIndex(), referred_indices)); + smallest_blocking_index = std::min(smallest_blocking_index, index); + + break; + } + + if ((blocking_allowed || index < known_received_count) && + index >= draining_index) { + // If allowed, refer to entry name directly, with literal value. + instructions.push_back(EncodeLiteralHeaderFieldWithNameReference( + is_static, index, value, referred_indices)); + smallest_blocking_index = std::min(smallest_blocking_index, index); + + break; + } - instructions.push_back( - {QpackLiteralHeaderFieldNameReferenceInstruction(), {}}); - instructions.back().values.s_bit = is_static; - instructions.back().values.varint = index; - instructions.back().values.value = value; + // Encode entry as string literals. + // TODO(b/112770235): Use already acknowledged entry with lower index if + // exists. + // TODO(b/112770235): Use static entry name with literal value if + // dynamic entry exists but cannot be used. + instructions.push_back(EncodeLiteralHeaderField(name, value)); break; + case QpackHeaderTable::MatchType::kNoMatch: - instructions.push_back({QpackLiteralHeaderFieldInstruction(), {}}); - instructions.back().values.name = name; - instructions.back().values.value = value; + if (blocking_allowed && + QpackEntry::Size(name, value) <= + header_table_.MaxInsertSizeWithoutEvictingGivenEntry( + smallest_blocking_index)) { + // If allowed, insert entry and refer to it. + encoder_stream_sender_.SendInsertWithoutNameReference(name, value); + auto entry = header_table_.InsertEntry(name, value); + instructions.push_back(EncodeIndexedHeaderField( + /* is_static = */ false, entry->InsertionIndex(), + referred_indices)); + smallest_blocking_index = std::min<uint64_t>(smallest_blocking_index, + entry->InsertionIndex()); + + break; + } + + // Encode entry as string literals. + instructions.push_back(EncodeLiteralHeaderField(name, value)); break; } } - // Second pass. + return instructions; +} + +std::string QpackEncoder::SecondPassEncode( + QpackEncoder::Instructions instructions, + uint64_t required_insert_count) const { QpackInstructionEncoder instruction_encoder; std::string encoded_headers; // Header block prefix. - // TODO(bnc): Implement dynamic entries and set Required Insert Count and - // Delta Base accordingly. QpackInstructionEncoder::Values values; - values.varint = 0; // Encoded required insert count. + values.varint = QpackEncodeRequiredInsertCount(required_insert_count, + header_table_.max_entries()); values.varint2 = 0; // Delta Base. values.s_bit = false; // Delta Base sign. + const uint64_t base = required_insert_count; instruction_encoder.Encode(QpackPrefixInstruction(), values, &encoded_headers); - for (const auto& instruction : instructions) { + for (auto& instruction : instructions) { + // Dynamic table references must be transformed from absolute to relative + // indices. + if ((instruction.instruction == QpackIndexedHeaderFieldInstruction() || + instruction.instruction == + QpackLiteralHeaderFieldNameReferenceInstruction()) && + !instruction.values.s_bit) { + instruction.values.varint = + QpackAbsoluteIndexToRequestStreamRelativeIndex( + instruction.values.varint, base); + } instruction_encoder.Encode(instruction.instruction, instruction.values, &encoded_headers); } @@ -95,20 +294,72 @@ std::string QpackEncoder::EncodeHeaderList( return encoded_headers; } -void QpackEncoder::DecodeDecoderStreamData(QuicStringPiece data) { - decoder_stream_receiver_.Decode(data); +std::string QpackEncoder::EncodeHeaderList( + QuicStreamId stream_id, + const spdy::SpdyHeaderBlock& header_list) { + // Keep track of all dynamic table indices that this header block refers to so + // that it can be passed to QpackBlockingManager. + QpackBlockingManager::IndexSet referred_indices; + + // First pass: encode into |instructions|. + Instructions instructions = FirstPassEncode(header_list, &referred_indices); + + const uint64_t required_insert_count = + referred_indices.empty() + ? 0 + : QpackBlockingManager::RequiredInsertCount(referred_indices); + if (!referred_indices.empty()) { + blocking_manager_.OnHeaderBlockSent(stream_id, std::move(referred_indices)); + } + + // Second pass. + return SecondPassEncode(std::move(instructions), required_insert_count); +} + +void QpackEncoder::SetMaximumDynamicTableCapacity( + uint64_t maximum_dynamic_table_capacity) { + header_table_.SetMaximumDynamicTableCapacity(maximum_dynamic_table_capacity); +} + +void QpackEncoder::SetDynamicTableCapacity(uint64_t dynamic_table_capacity) { + encoder_stream_sender_.SendSetDynamicTableCapacity(dynamic_table_capacity); + bool success = header_table_.SetDynamicTableCapacity(dynamic_table_capacity); + DCHECK(success); +} + +void QpackEncoder::SetMaximumBlockedStreams(uint64_t maximum_blocked_streams) { + maximum_blocked_streams_ = maximum_blocked_streams; } -void QpackEncoder::OnInsertCountIncrement(uint64_t /*increment*/) { - // TODO(bnc): Implement dynamic table management for encoding. +void QpackEncoder::OnInsertCountIncrement(uint64_t increment) { + if (increment == 0) { + decoder_stream_error_delegate_->OnDecoderStreamError( + "Invalid increment value 0."); + return; + } + + blocking_manager_.OnInsertCountIncrement(increment); + + if (blocking_manager_.known_received_count() > + header_table_.inserted_entry_count()) { + decoder_stream_error_delegate_->OnDecoderStreamError(QuicStrCat( + "Increment value ", increment, " raises known received count to ", + blocking_manager_.known_received_count(), + " exceeding inserted entry count ", + header_table_.inserted_entry_count())); + } } -void QpackEncoder::OnHeaderAcknowledgement(QuicStreamId /*stream_id*/) { - // TODO(bnc): Implement dynamic table management for encoding. +void QpackEncoder::OnHeaderAcknowledgement(QuicStreamId stream_id) { + if (!blocking_manager_.OnHeaderAcknowledgement(stream_id)) { + decoder_stream_error_delegate_->OnDecoderStreamError( + QuicStrCat("Header Acknowledgement received for stream ", stream_id, + " with no outstanding header blocks.")); + } } -void QpackEncoder::OnStreamCancellation(QuicStreamId /*stream_id*/) { - // TODO(bnc): Implement dynamic table management for encoding. +void QpackEncoder::OnStreamCancellation(QuicStreamId stream_id) { + blocking_manager_.OnStreamCancellation(stream_id); } void QpackEncoder::OnErrorDetected(QuicStringPiece error_message) { diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder.h b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder.h index 39600e89fcd..4aa69c138f1 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder.h +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder.h @@ -8,7 +8,9 @@ #include <cstdint> #include <memory> #include <string> +#include <vector> +#include "net/third_party/quiche/src/quic/core/qpack/qpack_blocking_manager.h" #include "net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_receiver.h" #include "net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_sender.h" #include "net/third_party/quiche/src/quic/core/qpack/qpack_header_table.h" @@ -20,10 +22,16 @@ namespace spdy { class SpdyHeaderBlock; -} +} // namespace spdy namespace quic { +namespace test { + +class QpackEncoderPeer; + +} // namespace test + // QPACK encoder class. Exactly one instance should exist per QUIC connection. class QUIC_EXPORT_PRIVATE QpackEncoder : public QpackDecoderStreamReceiver::Delegate { @@ -38,16 +46,27 @@ class QUIC_EXPORT_PRIVATE QpackEncoder virtual void OnDecoderStreamError(QuicStringPiece error_message) = 0; }; - QpackEncoder(DecoderStreamErrorDelegate* decoder_stream_error_delegate, - QpackStreamSenderDelegate* encoder_stream_sender_delegate); + QpackEncoder(DecoderStreamErrorDelegate* decoder_stream_error_delegate); ~QpackEncoder() override; // Encode a header list. std::string EncodeHeaderList(QuicStreamId stream_id, - const spdy::SpdyHeaderBlock* header_list); + const spdy::SpdyHeaderBlock& header_list); + + // Set maximum dynamic table capacity to |maximum_dynamic_table_capacity|, + // measured in bytes. Called when SETTINGS_QPACK_MAX_TABLE_CAPACITY is + // received. Encoder needs to know this value so that it can calculate + // MaxEntries, used as a modulus to encode Required Insert Count. + void SetMaximumDynamicTableCapacity(uint64_t maximum_dynamic_table_capacity); - // Decode data received on the decoder stream. - void DecodeDecoderStreamData(QuicStringPiece data); + // Set dynamic table capacity to |dynamic_table_capacity|. + // |dynamic_table_capacity| must not exceed maximum dynamic table capacity. + // Also sends Set Dynamic Table Capacity instruction on encoder stream. + void SetDynamicTableCapacity(uint64_t dynamic_table_capacity); + + // Set maximum number of blocked streams. + // Called when SETTINGS_QPACK_BLOCKED_STREAMS is received. + void SetMaximumBlockedStreams(uint64_t maximum_blocked_streams); // QpackDecoderStreamReceiver::Delegate implementation void OnInsertCountIncrement(uint64_t increment) override; @@ -55,7 +74,18 @@ class QUIC_EXPORT_PRIVATE QpackEncoder void OnStreamCancellation(QuicStreamId stream_id) override; void OnErrorDetected(QuicStringPiece error_message) override; + // delegate must be set if dynamic table capacity is not zero. + void set_qpack_stream_sender_delegate(QpackStreamSenderDelegate* delegate) { + encoder_stream_sender_.set_qpack_stream_sender_delegate(delegate); + } + + QpackStreamReceiver* decoder_stream_receiver() { + return &decoder_stream_receiver_; + } + private: + friend class test::QpackEncoderPeer; + // TODO(bnc): Consider moving this class to QpackInstructionEncoder or // qpack_constants, adding factory methods, one for each instruction, and // changing QpackInstructionEncoder::Encoder() to take an @@ -66,11 +96,51 @@ class QUIC_EXPORT_PRIVATE QpackEncoder const QpackInstruction* instruction; QpackInstructionEncoder::Values values; }; + using Instructions = std::vector<InstructionWithValues>; + + // Generate indexed header field instruction + // and optionally update |*referred_indices|. + static InstructionWithValues EncodeIndexedHeaderField( + bool is_static, + uint64_t index, + QpackBlockingManager::IndexSet* referred_indices); + + // Generate literal header field with name reference instruction + // and optionally update |*referred_indices|. + static InstructionWithValues EncodeLiteralHeaderFieldWithNameReference( + bool is_static, + uint64_t index, + QuicStringPiece value, + QpackBlockingManager::IndexSet* referred_indices); + + // Generate literal header field instruction. + static InstructionWithValues EncodeLiteralHeaderField(QuicStringPiece name, + QuicStringPiece value); + + // Performs first pass of two-pass encoding: represent each header field in + // |*header_list| as a reference to an existing entry, the name of an existing + // entry with a literal value, or a literal name and value pair. Sends + // necessary instructions on the encoder stream. Records absolute indices of + // referred dynamic table entries in |*referred_indices|. Returns list of + // header field representations, with all dynamic table entries referred to + // with absolute indices. Returned Instructions object may have + // QuicStringPieces pointing to strings owned by |*header_list|. + Instructions FirstPassEncode( + const spdy::SpdyHeaderBlock& header_list, + QpackBlockingManager::IndexSet* referred_indices); + + // Performs second pass of two-pass encoding: serializes representations + // generated in first pass, transforming absolute indices of dynamic table + // entries to relative indices. + std::string SecondPassEncode(Instructions instructions, + uint64_t required_insert_count) const; DecoderStreamErrorDelegate* const decoder_stream_error_delegate_; QpackDecoderStreamReceiver decoder_stream_receiver_; QpackEncoderStreamSender encoder_stream_sender_; QpackHeaderTable header_table_; + uint64_t maximum_blocked_streams_; + QpackBlockingManager blocking_manager_; }; } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_sender.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_sender.cc index dce183ae17b..e6209fd2c5c 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_sender.cc +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_sender.cc @@ -13,11 +13,7 @@ namespace quic { -QpackEncoderStreamSender::QpackEncoderStreamSender( - QpackStreamSenderDelegate* delegate) - : delegate_(delegate) { - DCHECK(delegate_); -} +QpackEncoderStreamSender::QpackEncoderStreamSender() : delegate_(nullptr) {} void QpackEncoderStreamSender::SendInsertWithNameReference( bool is_static, diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_sender.h b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_sender.h index bf3a79fc0a1..3c410d570b0 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_sender.h +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_sender.h @@ -17,8 +17,7 @@ namespace quic { // This class serializes instructions for transmission on the encoder stream. class QUIC_EXPORT_PRIVATE QpackEncoderStreamSender { public: - explicit QpackEncoderStreamSender(QpackStreamSenderDelegate* delegate); - QpackEncoderStreamSender() = delete; + QpackEncoderStreamSender(); QpackEncoderStreamSender(const QpackEncoderStreamSender&) = delete; QpackEncoderStreamSender& operator=(const QpackEncoderStreamSender&) = delete; @@ -37,8 +36,13 @@ class QUIC_EXPORT_PRIVATE QpackEncoderStreamSender { // 5.2.4. Set Dynamic Table Capacity void SendSetDynamicTableCapacity(uint64_t capacity); + // delegate must be set if dynamic table capacity is not zero. + void set_qpack_stream_sender_delegate(QpackStreamSenderDelegate* delegate) { + delegate_ = delegate; + } + private: - QpackStreamSenderDelegate* const delegate_; + QpackStreamSenderDelegate* delegate_; QpackInstructionEncoder instruction_encoder_; QpackInstructionEncoder::Values values_; }; diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_sender_test.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_sender_test.cc index 01f1cbf4992..4a15d1f2ca1 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_sender_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_sender_test.cc @@ -17,7 +17,9 @@ namespace { class QpackEncoderStreamSenderTest : public QuicTest { protected: - QpackEncoderStreamSenderTest() : stream_(&delegate_) {} + QpackEncoderStreamSenderTest() { + stream_.set_qpack_stream_sender_delegate(&delegate_); + } ~QpackEncoderStreamSenderTest() override = default; StrictMock<MockQpackStreamSenderDelegate> delegate_; diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_test.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_test.cc index 544b6701f64..0da6f3eb3a3 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_test.cc @@ -7,10 +7,14 @@ #include <string> #include "net/third_party/quiche/src/quic/core/qpack/qpack_encoder_test_utils.h" +#include "net/third_party/quiche/src/quic/core/qpack/qpack_test_utils.h" #include "net/third_party/quiche/src/quic/core/qpack/qpack_utils.h" #include "net/third_party/quiche/src/quic/platform/api/quic_test.h" #include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/qpack_encoder_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/qpack_header_table_peer.h" +using ::testing::_; using ::testing::Eq; using ::testing::StrictMock; using ::testing::Values; @@ -21,22 +25,25 @@ namespace { class QpackEncoderTest : public QuicTest { protected: - QpackEncoderTest() = default; + QpackEncoderTest() : encoder_(&decoder_stream_error_delegate_) { + encoder_.set_qpack_stream_sender_delegate(&encoder_stream_sender_delegate_); + encoder_.SetMaximumBlockedStreams(1); + } + ~QpackEncoderTest() override = default; - std::string Encode(const spdy::SpdyHeaderBlock* header_list) { - QpackEncoder encoder(&decoder_stream_error_delegate_, - &encoder_stream_sender_delegate_); - return encoder.EncodeHeaderList(/* stream_id = */ 1, header_list); + std::string Encode(const spdy::SpdyHeaderBlock& header_list) { + return encoder_.EncodeHeaderList(/* stream_id = */ 1, header_list); } StrictMock<MockDecoderStreamErrorDelegate> decoder_stream_error_delegate_; - NoopQpackStreamSenderDelegate encoder_stream_sender_delegate_; + StrictMock<MockQpackStreamSenderDelegate> encoder_stream_sender_delegate_; + QpackEncoder encoder_; }; TEST_F(QpackEncoderTest, Empty) { spdy::SpdyHeaderBlock header_list; - std::string output = Encode(&header_list); + std::string output = Encode(header_list); EXPECT_EQ(QuicTextUtils::HexDecode("0000"), output); } @@ -44,7 +51,7 @@ TEST_F(QpackEncoderTest, Empty) { TEST_F(QpackEncoderTest, EmptyName) { spdy::SpdyHeaderBlock header_list; header_list[""] = "foo"; - std::string output = Encode(&header_list); + std::string output = Encode(header_list); EXPECT_EQ(QuicTextUtils::HexDecode("0000208294e7"), output); } @@ -52,7 +59,7 @@ TEST_F(QpackEncoderTest, EmptyName) { TEST_F(QpackEncoderTest, EmptyValue) { spdy::SpdyHeaderBlock header_list; header_list["foo"] = ""; - std::string output = Encode(&header_list); + std::string output = Encode(header_list); EXPECT_EQ(QuicTextUtils::HexDecode("00002a94e700"), output); } @@ -60,7 +67,7 @@ TEST_F(QpackEncoderTest, EmptyValue) { TEST_F(QpackEncoderTest, EmptyNameAndValue) { spdy::SpdyHeaderBlock header_list; header_list[""] = ""; - std::string output = Encode(&header_list); + std::string output = Encode(header_list); EXPECT_EQ(QuicTextUtils::HexDecode("00002000"), output); } @@ -68,7 +75,7 @@ TEST_F(QpackEncoderTest, EmptyNameAndValue) { TEST_F(QpackEncoderTest, Simple) { spdy::SpdyHeaderBlock header_list; header_list["foo"] = "bar"; - std::string output = Encode(&header_list); + std::string output = Encode(header_list); EXPECT_EQ(QuicTextUtils::HexDecode("00002a94e703626172"), output); } @@ -78,7 +85,7 @@ TEST_F(QpackEncoderTest, Multiple) { header_list["foo"] = "bar"; // 'Z' would be Huffman encoded to 8 bits, so no Huffman encoding is used. header_list["ZZZZZZZ"] = std::string(127, 'Z'); - std::string output = Encode(&header_list); + std::string output = Encode(header_list); EXPECT_EQ( QuicTextUtils::HexDecode( @@ -102,7 +109,7 @@ TEST_F(QpackEncoderTest, StaticTable) { header_list["accept-encoding"] = "gzip, deflate, br"; header_list["location"] = ""; - std::string output = Encode(&header_list); + std::string output = Encode(header_list); EXPECT_EQ(QuicTextUtils::HexDecode("0000d1dfcc"), output); } { @@ -111,7 +118,7 @@ TEST_F(QpackEncoderTest, StaticTable) { header_list["accept-encoding"] = "compress"; header_list["location"] = "foo"; - std::string output = Encode(&header_list); + std::string output = Encode(header_list); EXPECT_EQ(QuicTextUtils::HexDecode("0000d45f108621e9aec2a11f5c8294e7"), output); } @@ -120,7 +127,7 @@ TEST_F(QpackEncoderTest, StaticTable) { header_list[":method"] = "TRACE"; header_list["accept-encoding"] = ""; - std::string output = Encode(&header_list); + std::string output = Encode(header_list); EXPECT_EQ(QuicTextUtils::HexDecode("00005f000554524143455f1000"), output); } } @@ -129,16 +136,16 @@ TEST_F(QpackEncoderTest, DecoderStreamError) { EXPECT_CALL(decoder_stream_error_delegate_, OnDecoderStreamError(Eq("Encoded integer too large."))); - QpackEncoder encoder(&decoder_stream_error_delegate_, - &encoder_stream_sender_delegate_); - encoder.DecodeDecoderStreamData( + QpackEncoder encoder(&decoder_stream_error_delegate_); + encoder.set_qpack_stream_sender_delegate(&encoder_stream_sender_delegate_); + encoder.decoder_stream_receiver()->Decode( QuicTextUtils::HexDecode("ffffffffffffffffffffff")); } TEST_F(QpackEncoderTest, SplitAlongNullCharacter) { spdy::SpdyHeaderBlock header_list; header_list["foo"] = QuicStringPiece("bar\0bar\0baz", 11); - std::string output = Encode(&header_list); + std::string output = Encode(header_list); EXPECT_EQ(QuicTextUtils::HexDecode("0000" // prefix "2a94e703626172" // foo: bar @@ -148,6 +155,286 @@ TEST_F(QpackEncoderTest, SplitAlongNullCharacter) { output); } +TEST_F(QpackEncoderTest, ZeroInsertCountIncrement) { + // Encoder receives insert count increment with forbidden value 0. + EXPECT_CALL(decoder_stream_error_delegate_, + OnDecoderStreamError(Eq("Invalid increment value 0."))); + encoder_.OnInsertCountIncrement(0); +} + +TEST_F(QpackEncoderTest, TooLargeInsertCountIncrement) { + // Encoder receives insert count increment with value that increases Known + // Received Count to a value (one) which is larger than the number of dynamic + // table insertions sent (zero). + EXPECT_CALL( + decoder_stream_error_delegate_, + OnDecoderStreamError(Eq("Increment value 1 raises known received count " + "to 1 exceeding inserted entry count 0"))); + encoder_.OnInsertCountIncrement(1); +} + +TEST_F(QpackEncoderTest, InvalidHeaderAcknowledgement) { + // Encoder receives header acknowledgement for a stream on which no header + // block with dynamic table entries was ever sent. + EXPECT_CALL( + decoder_stream_error_delegate_, + OnDecoderStreamError(Eq("Header Acknowledgement received for stream 0 " + "with no outstanding header blocks."))); + encoder_.OnHeaderAcknowledgement(/* stream_id = */ 0); +} + +TEST_F(QpackEncoderTest, DynamicTable) { + encoder_.SetMaximumBlockedStreams(1); + encoder_.SetMaximumDynamicTableCapacity(4096); + + // Set Dynamic Table Capacity instruction. + EXPECT_CALL(encoder_stream_sender_delegate_, + WriteStreamData(Eq(QuicTextUtils::HexDecode("3fe11f")))); + encoder_.SetDynamicTableCapacity(4096); + + spdy::SpdyHeaderBlock header_list; + header_list["foo"] = "bar"; + header_list.AppendValueOrAddHeader("foo", + "baz"); // name matches dynamic entry + header_list["cookie"] = "baz"; // name matches static entry + + // Insert three entries into the dynamic table. + EXPECT_CALL(encoder_stream_sender_delegate_, + WriteStreamData(Eq(QuicTextUtils::HexDecode( + "62" // insert without name reference + "94e7" // Huffman-encoded name "foo" + "03626172")))); // value "bar" + EXPECT_CALL(encoder_stream_sender_delegate_, + WriteStreamData(Eq(QuicTextUtils::HexDecode( + "80" // insert with name reference, dynamic index 0 + "0362617a")))); // value "baz" + EXPECT_CALL(encoder_stream_sender_delegate_, + WriteStreamData(Eq(QuicTextUtils::HexDecode( + "c5" // insert with name reference, static index 5 + "0362617a")))); // value "baz" + + EXPECT_EQ(QuicTextUtils::HexDecode( + "0400" // prefix + "828180"), // dynamic entries with relative index 0, 1, and 2 + Encode(header_list)); +} + +// There is no room in the dynamic table after inserting the first entry. +TEST_F(QpackEncoderTest, SmallDynamicTable) { + encoder_.SetMaximumBlockedStreams(1); + encoder_.SetMaximumDynamicTableCapacity(QpackEntry::Size("foo", "bar")); + + // Set Dynamic Table Capacity instruction. + EXPECT_CALL(encoder_stream_sender_delegate_, + WriteStreamData(Eq(QuicTextUtils::HexDecode("3f07")))); + encoder_.SetDynamicTableCapacity(QpackEntry::Size("foo", "bar")); + + spdy::SpdyHeaderBlock header_list; + header_list["foo"] = "bar"; + header_list.AppendValueOrAddHeader("foo", + "baz"); // name matches dynamic entry + header_list["cookie"] = "baz"; // name matches static entry + header_list["bar"] = "baz"; // no match + + // Insert one entry into the dynamic table. + EXPECT_CALL(encoder_stream_sender_delegate_, + WriteStreamData(Eq(QuicTextUtils::HexDecode( + "62" // insert without name reference + "94e7" // Huffman-encoded name "foo" + "03626172")))); // value "bar" + + EXPECT_EQ(QuicTextUtils::HexDecode("0200" // prefix + "80" // dynamic entry 0 + "40" // reference to dynamic entry 0 name + "0362617a" // with literal value "baz" + "55" // reference to static entry 5 name + "0362617a" // with literal value "baz" + "23626172" // literal name "bar" + "0362617a"), // with literal value "baz" + Encode(header_list)); +} + +TEST_F(QpackEncoderTest, BlockedStream) { + encoder_.SetMaximumBlockedStreams(1); + encoder_.SetMaximumDynamicTableCapacity(4096); + + // Set Dynamic Table Capacity instruction. + EXPECT_CALL(encoder_stream_sender_delegate_, + WriteStreamData(Eq(QuicTextUtils::HexDecode("3fe11f")))); + encoder_.SetDynamicTableCapacity(4096); + + spdy::SpdyHeaderBlock header_list1; + header_list1["foo"] = "bar"; + + // Insert one entry into the dynamic table. + EXPECT_CALL(encoder_stream_sender_delegate_, + WriteStreamData(Eq(QuicTextUtils::HexDecode( + "62" // insert without name reference + "94e7" // Huffman-encoded name "foo" + "03626172")))); // value "bar" + + EXPECT_EQ(QuicTextUtils::HexDecode("0200" // prefix + "80"), // dynamic entry 0 + encoder_.EncodeHeaderList(/* stream_id = */ 1, header_list1)); + + // Stream 1 is blocked. Stream 2 is not allowed to block. + spdy::SpdyHeaderBlock header_list2; + header_list2["foo"] = "bar"; // name and value match dynamic entry + header_list2.AppendValueOrAddHeader("foo", + "baz"); // name matches dynamic entry + header_list2["cookie"] = "baz"; // name matches static entry + header_list2["bar"] = "baz"; // no match + + EXPECT_EQ(QuicTextUtils::HexDecode("0000" // prefix + "2a94e7" // literal name "foo" + "03626172" // with literal value "bar" + "2a94e7" // literal name "foo" + "0362617a" // with literal value "baz" + "55" // name of static entry 5 + "0362617a" // with literal value "baz" + "23626172" // literal name "bar" + "0362617a"), // with literal value "baz" + encoder_.EncodeHeaderList(/* stream_id = */ 2, header_list2)); + + // Peer acknowledges receipt of one dynamic table entry. + // Stream 1 is no longer blocked. + encoder_.OnInsertCountIncrement(1); + + // Insert three entries into the dynamic table. + EXPECT_CALL(encoder_stream_sender_delegate_, + WriteStreamData(Eq(QuicTextUtils::HexDecode( + "80" // insert with name reference, dynamic index 0 + "0362617a")))); // value "baz" + EXPECT_CALL(encoder_stream_sender_delegate_, + WriteStreamData(Eq(QuicTextUtils::HexDecode( + "c5" // insert with name reference, static index 5 + "0362617a")))); // value "baz" + EXPECT_CALL(encoder_stream_sender_delegate_, + WriteStreamData(Eq(QuicTextUtils::HexDecode( + "43" // insert without name reference + "626172" // name "bar" + "0362617a")))); // value "baz" + EXPECT_EQ(QuicTextUtils::HexDecode("0500" // prefix + "83828180"), // dynamic entries + encoder_.EncodeHeaderList(/* stream_id = */ 3, header_list2)); + + // Stream 3 is blocked. Stream 4 is not allowed to block, but it can + // reference already acknowledged dynamic entry 0. + EXPECT_EQ(QuicTextUtils::HexDecode("0200" // prefix + "80" // dynamic entry 0 + "2a94e7" // literal name "foo" + "0362617a" // with literal value "baz" + "2c21cfd4c5" // literal name "cookie" + "0362617a" // with literal value "baz" + "23626172" // literal name "bar" + "0362617a"), // with literal value "baz" + encoder_.EncodeHeaderList(/* stream_id = */ 4, header_list2)); + + // Peer acknowledges receipt of two more dynamic table entries. + // Stream 3 is still blocked. + encoder_.OnInsertCountIncrement(2); + + // Stream 5 is not allowed to block, but it can reference already acknowledged + // dynamic entries 0, 1, and 2. + EXPECT_EQ(QuicTextUtils::HexDecode("0400" // prefix + "828180" // dynamic entries + "23626172" // literal name "bar" + "0362617a"), // with literal value "baz" + encoder_.EncodeHeaderList(/* stream_id = */ 5, header_list2)); + + // Peer acknowledges decoding header block on stream 3. + // Stream 3 is not blocked any longer. + encoder_.OnHeaderAcknowledgement(3); + + EXPECT_EQ(QuicTextUtils::HexDecode("0500" // prefix + "83828180"), // dynamic entries + encoder_.EncodeHeaderList(/* stream_id = */ 6, header_list2)); +} + +TEST_F(QpackEncoderTest, Draining) { + // TODO(b/112770235): Remove when already blocking stream can emit blocking + // references. + encoder_.SetMaximumBlockedStreams(2); + + spdy::SpdyHeaderBlock header_list1; + header_list1["one"] = "foo"; + header_list1["two"] = "foo"; + header_list1["three"] = "foo"; + header_list1["four"] = "foo"; + header_list1["five"] = "foo"; + header_list1["six"] = "foo"; + header_list1["seven"] = "foo"; + header_list1["eight"] = "foo"; + header_list1["nine"] = "foo"; + header_list1["ten"] = "foo"; + + // Make just enough room in the dynamic table for the header list plus the + // first entry duplicated. This will ensure that the oldest entries are + // draining. + uint64_t maximum_dynamic_table_capacity = 0; + for (const auto& header_field : header_list1) { + maximum_dynamic_table_capacity += + QpackEntry::Size(header_field.first, header_field.second); + } + maximum_dynamic_table_capacity += QpackEntry::Size("one", "foo"); + encoder_.SetMaximumDynamicTableCapacity(maximum_dynamic_table_capacity); + + // Set Dynamic Table Capacity instruction. + EXPECT_CALL(encoder_stream_sender_delegate_, WriteStreamData(_)); + encoder_.SetDynamicTableCapacity(maximum_dynamic_table_capacity); + + // Insert ten entries into the dynamic table. + EXPECT_CALL(encoder_stream_sender_delegate_, WriteStreamData(_)).Times(10); + + EXPECT_EQ( + QuicTextUtils::HexDecode("0b00" // prefix + "89888786858483828180"), // dynamic entries + Encode(header_list1)); + + // Entry is identical to oldest one, which is draining. It will be + // duplicated and referenced. + spdy::SpdyHeaderBlock header_list2; + header_list2["one"] = "foo"; + + // Duplicate oldest entry. + EXPECT_CALL(encoder_stream_sender_delegate_, + WriteStreamData(Eq(QuicTextUtils::HexDecode("09")))); + + EXPECT_EQ(QuicTextUtils::HexDecode("0c00" // prefix + "80"), // most recent dynamic table entry + Encode(header_list2)); + + spdy::SpdyHeaderBlock header_list3; + // Entry is identical to second oldest one, which is draining. There is no + // room to duplicate, it will be encoded with string literals. + header_list3.AppendValueOrAddHeader("two", "foo"); + // Entry has name identical to second oldest one, which is draining. There is + // no room to insert new entry, it will be encoded with string literals. + header_list3.AppendValueOrAddHeader("two", "bar"); + + EXPECT_EQ(QuicTextUtils::HexDecode("0000" // prefix + "2374776f" // literal name "two" + "8294e7" // literal value "foo" + "2374776f" // literal name "two" + "03626172"), // literal value "bar" + Encode(header_list3)); +} + +TEST_F(QpackEncoderTest, DynamicTableCapacityLessThanMaximum) { + encoder_.SetMaximumDynamicTableCapacity(1024); + + // Set Dynamic Table Capacity instruction. + EXPECT_CALL(encoder_stream_sender_delegate_, + WriteStreamData(Eq(QuicTextUtils::HexDecode("3e")))); + encoder_.SetDynamicTableCapacity(30); + + QpackHeaderTable* header_table = QpackEncoderPeer::header_table(&encoder_); + + EXPECT_EQ(1024u, + QpackHeaderTablePeer::maximum_dynamic_table_capacity(header_table)); + EXPECT_EQ(30u, QpackHeaderTablePeer::dynamic_table_capacity(header_table)); +} + } // namespace } // namespace test } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_header_table.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_header_table.cc index 6fa64125a1a..bb28f722f3e 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_header_table.cc +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_header_table.cc @@ -141,6 +141,28 @@ const QpackEntry* QpackHeaderTable::InsertEntry(QuicStringPiece name, return new_entry; } +uint64_t QpackHeaderTable::MaxInsertSizeWithoutEvictingGivenEntry( + uint64_t index) const { + DCHECK_LE(dropped_entry_count_, index); + + if (index > inserted_entry_count()) { + // All entries are allowed to be evicted. + return dynamic_table_capacity_; + } + + // Initialize to current available capacity. + uint64_t max_insert_size = dynamic_table_capacity_ - dynamic_table_size_; + + for (const auto& entry : dynamic_entries_) { + if (entry.InsertionIndex() >= index) { + break; + } + max_insert_size += entry.Size(); + } + + return max_insert_size; +} + bool QpackHeaderTable::SetDynamicTableCapacity(uint64_t capacity) { if (capacity > maximum_dynamic_table_capacity_) { return false; @@ -162,7 +184,6 @@ void QpackHeaderTable::SetMaximumDynamicTableCapacity( DCHECK_EQ(0u, maximum_dynamic_table_capacity_); DCHECK_EQ(0u, max_entries_); - dynamic_table_capacity_ = maximum_dynamic_table_capacity; maximum_dynamic_table_capacity_ = maximum_dynamic_table_capacity; max_entries_ = maximum_dynamic_table_capacity / 32; } @@ -173,6 +194,31 @@ void QpackHeaderTable::RegisterObserver(Observer* observer, observers_.push({observer, required_insert_count}); } +uint64_t QpackHeaderTable::draining_index(float draining_fraction) const { + DCHECK_LE(0.0, draining_fraction); + DCHECK_LE(draining_fraction, 1.0); + + const uint64_t required_space = draining_fraction * dynamic_table_capacity_; + uint64_t space_above_draining_index = + dynamic_table_capacity_ - dynamic_table_size_; + + if (dynamic_entries_.empty() || + space_above_draining_index >= required_space) { + return dropped_entry_count_; + } + + auto it = dynamic_entries_.begin(); + while (space_above_draining_index < required_space) { + space_above_draining_index += it->Size(); + ++it; + if (it == dynamic_entries_.end()) { + return inserted_entry_count(); + } + } + + return it->InsertionIndex(); +} + bool QpackHeaderTable::ObserverWithThreshold::operator>( const ObserverWithThreshold& other) const { return required_insert_count > other.required_insert_count; diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_header_table.h b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_header_table.h index 39d7326758b..f251894f781 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_header_table.h +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_header_table.h @@ -17,6 +17,12 @@ namespace quic { +namespace test { + +class QpackHeaderTablePeer; + +} // namespace test + using QpackEntry = spdy::HpackEntry; // This class manages the QPACK static and dynamic tables. For dynamic entries, @@ -70,6 +76,12 @@ class QUIC_EXPORT_PRIVATE QpackHeaderTable { // is larger than the capacity of the dynamic table. const QpackEntry* InsertEntry(QuicStringPiece name, QuicStringPiece value); + // Returns the size of the largest entry that could be inserted into the + // dynamic table without evicting entry |index|. |index| might be larger than + // inserted_entry_count(), in which case the capacity of the table is + // returned. |index| must not be smaller than dropped_entry_count(). + uint64_t MaxInsertSizeWithoutEvictingGivenEntry(uint64_t index) const; + // Change dynamic table capacity to |capacity|. Returns true on success. // Returns false is |capacity| exceeds maximum dynamic table capacity. bool SetDynamicTableCapacity(uint64_t capacity); @@ -99,7 +111,17 @@ class QUIC_EXPORT_PRIVATE QpackHeaderTable { // The number of entries dropped from the dynamic table. uint64_t dropped_entry_count() const { return dropped_entry_count_; } + // Returns the draining index described at + // https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#avoiding-blocked-insertions. + // Entries with an index larger than or equal to the draining index take up + // approximately |1.0 - draining_fraction| of dynamic table capacity. The + // remaining capacity is taken up by draining entries and unused space. + // The returned index might not be the index of a valid entry. + uint64_t draining_index(float draining_fraction) const; + private: + friend class test::QpackHeaderTablePeer; + // Evict entries from the dynamic table until table size is less than or equal // to current value of |dynamic_table_capacity_|. void EvictDownToCurrentCapacity(); diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_header_table_test.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_header_table_test.cc index ff7ef48d4fc..00f36e5286d 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_header_table_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_header_table_test.cc @@ -30,6 +30,7 @@ class QpackHeaderTableTest : public QuicTest { QpackHeaderTableTest() { table_.SetMaximumDynamicTableCapacity( kMaximumDynamicTableCapacityForTesting); + table_.SetDynamicTableCapacity(kMaximumDynamicTableCapacityForTesting); } ~QpackHeaderTableTest() override = default; @@ -365,6 +366,56 @@ TEST_F(QpackHeaderTableTest, EvictOldestOfSameName) { /* expected_is_static = */ false, 2u); } +// Returns the size of the largest entry that could be inserted into the +// dynamic table without evicting entry |index|. +TEST_F(QpackHeaderTableTest, MaxInsertSizeWithoutEvictingGivenEntry) { + const uint64_t dynamic_table_capacity = 100; + QpackHeaderTable table; + table.SetMaximumDynamicTableCapacity(dynamic_table_capacity); + EXPECT_TRUE(table.SetDynamicTableCapacity(dynamic_table_capacity)); + + // Empty table can take an entry up to its capacity. + EXPECT_EQ(dynamic_table_capacity, + table.MaxInsertSizeWithoutEvictingGivenEntry(0)); + + const uint64_t entry_size1 = QpackEntry::Size("foo", "bar"); + EXPECT_TRUE(table.InsertEntry("foo", "bar")); + EXPECT_EQ(dynamic_table_capacity - entry_size1, + table.MaxInsertSizeWithoutEvictingGivenEntry(0)); + // Table can take an entry up to its capacity if all entries are allowed to be + // evicted. + EXPECT_EQ(dynamic_table_capacity, + table.MaxInsertSizeWithoutEvictingGivenEntry(1)); + + const uint64_t entry_size2 = QpackEntry::Size("baz", "foobar"); + EXPECT_TRUE(table.InsertEntry("baz", "foobar")); + // Table can take an entry up to its capacity if all entries are allowed to be + // evicted. + EXPECT_EQ(dynamic_table_capacity, + table.MaxInsertSizeWithoutEvictingGivenEntry(2)); + // Second entry must stay. + EXPECT_EQ(dynamic_table_capacity - entry_size2, + table.MaxInsertSizeWithoutEvictingGivenEntry(1)); + // First and second entry must stay. + EXPECT_EQ(dynamic_table_capacity - entry_size2 - entry_size1, + table.MaxInsertSizeWithoutEvictingGivenEntry(0)); + + // Third entry evicts first one. + const uint64_t entry_size3 = QpackEntry::Size("last", "entry"); + EXPECT_TRUE(table.InsertEntry("last", "entry")); + EXPECT_EQ(1u, table.dropped_entry_count()); + // Table can take an entry up to its capacity if all entries are allowed to be + // evicted. + EXPECT_EQ(dynamic_table_capacity, + table.MaxInsertSizeWithoutEvictingGivenEntry(3)); + // Third entry must stay. + EXPECT_EQ(dynamic_table_capacity - entry_size3, + table.MaxInsertSizeWithoutEvictingGivenEntry(2)); + // Second and third entry must stay. + EXPECT_EQ(dynamic_table_capacity - entry_size3 - entry_size2, + table.MaxInsertSizeWithoutEvictingGivenEntry(1)); +} + TEST_F(QpackHeaderTableTest, Observer) { StrictMock<MockObserver> observer1; RegisterObserver(&observer1, 1); @@ -404,6 +455,44 @@ TEST_F(QpackHeaderTableTest, Observer) { Mock::VerifyAndClearExpectations(&observer5); } +TEST_F(QpackHeaderTableTest, DrainingIndex) { + QpackHeaderTable table; + table.SetMaximumDynamicTableCapacity(kMaximumDynamicTableCapacityForTesting); + EXPECT_TRUE( + table.SetDynamicTableCapacity(4 * QpackEntry::Size("foo", "bar"))); + + // Empty table: no draining entry. + EXPECT_EQ(0u, table.draining_index(0.0)); + EXPECT_EQ(0u, table.draining_index(1.0)); + + // Table with one entry. + EXPECT_TRUE(table.InsertEntry("foo", "bar")); + // Any entry can be referenced if none of the table is draining. + EXPECT_EQ(0u, table.draining_index(0.0)); + // No entry can be referenced if all of the table is draining. + EXPECT_EQ(1u, table.draining_index(1.0)); + + // Table with two entries is at half capacity. + EXPECT_TRUE(table.InsertEntry("foo", "bar")); + // Any entry can be referenced if at most half of the table is draining, + // because current entries only take up half of total capacity. + EXPECT_EQ(0u, table.draining_index(0.0)); + EXPECT_EQ(0u, table.draining_index(0.5)); + // No entry can be referenced if all of the table is draining. + EXPECT_EQ(2u, table.draining_index(1.0)); + + // Table with four entries is full. + EXPECT_TRUE(table.InsertEntry("foo", "bar")); + EXPECT_TRUE(table.InsertEntry("foo", "bar")); + // Any entry can be referenced if none of the table is draining. + EXPECT_EQ(0u, table.draining_index(0.0)); + // In a full table with identically sized entries, |draining_fraction| of all + // entries are draining. + EXPECT_EQ(2u, table.draining_index(0.5)); + // No entry can be referenced if all of the table is draining. + EXPECT_EQ(4u, table.draining_index(1.0)); +} + } // namespace } // namespace test } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_index_conversions.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_index_conversions.cc new file mode 100644 index 00000000000..8f1d52f6f34 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_index_conversions.cc @@ -0,0 +1,62 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/third_party/quiche/src/quic/core/qpack/qpack_index_conversions.h" + +#include <limits> + +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" + +namespace quic { + +uint64_t QpackAbsoluteIndexToEncoderStreamRelativeIndex( + uint64_t absolute_index, + uint64_t inserted_entry_count) { + DCHECK_LT(absolute_index, inserted_entry_count); + + return inserted_entry_count - absolute_index - 1; +} + +uint64_t QpackAbsoluteIndexToRequestStreamRelativeIndex(uint64_t absolute_index, + uint64_t base) { + DCHECK_LT(absolute_index, base); + + return base - absolute_index - 1; +} + +bool QpackEncoderStreamRelativeIndexToAbsoluteIndex( + uint64_t relative_index, + uint64_t inserted_entry_count, + uint64_t* absolute_index) { + if (relative_index >= inserted_entry_count) { + return false; + } + + *absolute_index = inserted_entry_count - relative_index - 1; + return true; +} + +bool QpackRequestStreamRelativeIndexToAbsoluteIndex(uint64_t relative_index, + uint64_t base, + uint64_t* absolute_index) { + if (relative_index >= base) { + return false; + } + + *absolute_index = base - relative_index - 1; + return true; +} + +bool QpackPostBaseIndexToAbsoluteIndex(uint64_t post_base_index, + uint64_t base, + uint64_t* absolute_index) { + if (post_base_index >= std::numeric_limits<uint64_t>::max() - base) { + return false; + } + + *absolute_index = base + post_base_index; + return true; +} + +} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_index_conversions.h b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_index_conversions.h new file mode 100644 index 00000000000..2348ac70964 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_index_conversions.h @@ -0,0 +1,59 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Utility methods to convert between absolute indexing (used in the dynamic +// table), relative indexing used on the encoder stream, and relative indexing +// and post-base indexing used on request streams (in header blocks). See: +// https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#indexing +// https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#relative-indexing +// https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#post-base + +#ifndef QUICHE_QUIC_CORE_QPACK_QPACK_INDEX_CONVERSIONS_H_ +#define QUICHE_QUIC_CORE_QPACK_QPACK_INDEX_CONVERSIONS_H_ + +#include <cstdint> + +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" + +namespace quic { + +// Conversion functions used in the encoder do not check for overflow/underflow. +// Since the maximum index is limited by maximum dynamic table capacity +// (represented on uint64_t) divided by minimum header field size (defined to be +// 32 bytes), overflow is not possible. The caller is responsible for providing +// input that does not underflow. + +QUIC_EXPORT_PRIVATE uint64_t +QpackAbsoluteIndexToEncoderStreamRelativeIndex(uint64_t absolute_index, + uint64_t inserted_entry_count); + +QUIC_EXPORT_PRIVATE uint64_t +QpackAbsoluteIndexToRequestStreamRelativeIndex(uint64_t absolute_index, + uint64_t base); + +// Conversion functions used in the decoder operate on input received from the +// network. These functions return false on overflow or underflow. + +QUIC_EXPORT_PRIVATE bool QpackEncoderStreamRelativeIndexToAbsoluteIndex( + uint64_t relative_index, + uint64_t inserted_entry_count, + uint64_t* absolute_index); + +// On success, |*absolute_index| is guaranteed to be strictly less than +// std::numeric_limits<uint64_t>::max(). +QUIC_EXPORT_PRIVATE bool QpackRequestStreamRelativeIndexToAbsoluteIndex( + uint64_t relative_index, + uint64_t base, + uint64_t* absolute_index); + +// On success, |*absolute_index| is guaranteed to be strictly less than +// std::numeric_limits<uint64_t>::max(). +QUIC_EXPORT_PRIVATE bool QpackPostBaseIndexToAbsoluteIndex( + uint64_t post_base_index, + uint64_t base, + uint64_t* absolute_index); + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QPACK_QPACK_INDEX_CONVERSIONS_H_ diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_index_conversions_test.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_index_conversions_test.cc new file mode 100644 index 00000000000..214dff5f27d --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_index_conversions_test.cc @@ -0,0 +1,99 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/third_party/quiche/src/quic/core/qpack/qpack_index_conversions.h" + +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" + +namespace quic { +namespace test { +namespace { + +struct { + uint64_t relative_index; + uint64_t inserted_entry_count; + uint64_t expected_absolute_index; +} kEncoderStreamRelativeIndexTestData[] = {{0, 1, 0}, {0, 2, 1}, {1, 2, 0}, + {0, 10, 9}, {5, 10, 4}, {9, 10, 0}}; + +TEST(QpackIndexConversions, EncoderStreamRelativeIndex) { + for (const auto& test_data : kEncoderStreamRelativeIndexTestData) { + uint64_t absolute_index = 42; + EXPECT_TRUE(QpackEncoderStreamRelativeIndexToAbsoluteIndex( + test_data.relative_index, test_data.inserted_entry_count, + &absolute_index)); + EXPECT_EQ(test_data.expected_absolute_index, absolute_index); + + EXPECT_EQ(test_data.relative_index, + QpackAbsoluteIndexToEncoderStreamRelativeIndex( + absolute_index, test_data.inserted_entry_count)); + } +} + +struct { + uint64_t relative_index; + uint64_t base; + uint64_t expected_absolute_index; +} kRequestStreamRelativeIndexTestData[] = {{0, 1, 0}, {0, 2, 1}, {1, 2, 0}, + {0, 10, 9}, {5, 10, 4}, {9, 10, 0}}; + +TEST(QpackIndexConversions, RequestStreamRelativeIndex) { + for (const auto& test_data : kRequestStreamRelativeIndexTestData) { + uint64_t absolute_index = 42; + EXPECT_TRUE(QpackRequestStreamRelativeIndexToAbsoluteIndex( + test_data.relative_index, test_data.base, &absolute_index)); + EXPECT_EQ(test_data.expected_absolute_index, absolute_index); + + EXPECT_EQ(test_data.relative_index, + QpackAbsoluteIndexToRequestStreamRelativeIndex(absolute_index, + test_data.base)); + } +} + +struct { + uint64_t post_base_index; + uint64_t base; + uint64_t expected_absolute_index; +} kPostBaseIndexTestData[] = {{0, 1, 1}, {1, 0, 1}, {2, 0, 2}, + {1, 1, 2}, {0, 2, 2}, {1, 2, 3}}; + +TEST(QpackIndexConversions, PostBaseIndex) { + for (const auto& test_data : kPostBaseIndexTestData) { + uint64_t absolute_index = 42; + EXPECT_TRUE(QpackPostBaseIndexToAbsoluteIndex( + test_data.post_base_index, test_data.base, &absolute_index)); + EXPECT_EQ(test_data.expected_absolute_index, absolute_index); + } +} + +TEST(QpackIndexConversions, EncoderStreamRelativeIndexUnderflow) { + uint64_t absolute_index; + EXPECT_FALSE(QpackEncoderStreamRelativeIndexToAbsoluteIndex( + /* relative_index = */ 10, + /* inserted_entry_count = */ 10, &absolute_index)); + EXPECT_FALSE(QpackEncoderStreamRelativeIndexToAbsoluteIndex( + /* relative_index = */ 12, + /* inserted_entry_count = */ 10, &absolute_index)); +} + +TEST(QpackIndexConversions, RequestStreamRelativeIndexUnderflow) { + uint64_t absolute_index; + EXPECT_FALSE(QpackRequestStreamRelativeIndexToAbsoluteIndex( + /* relative_index = */ 10, + /* base = */ 10, &absolute_index)); + EXPECT_FALSE(QpackRequestStreamRelativeIndexToAbsoluteIndex( + /* relative_index = */ 12, + /* base = */ 10, &absolute_index)); +} + +TEST(QpackIndexConversions, QpackPostBaseIndexToAbsoluteIndexOverflow) { + uint64_t absolute_index; + EXPECT_FALSE(QpackPostBaseIndexToAbsoluteIndex( + /* post_base_index = */ 20, + /* base = */ std::numeric_limits<uint64_t>::max() - 10, &absolute_index)); +} + +} // namespace +} // namespace test +} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_progressive_decoder.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_progressive_decoder.cc index e364edc1484..51d98ffa93b 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_progressive_decoder.cc +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_progressive_decoder.cc @@ -8,6 +8,7 @@ #include <limits> #include "net/third_party/quiche/src/quic/core/qpack/qpack_constants.h" +#include "net/third_party/quiche/src/quic/core/qpack/qpack_index_conversions.h" #include "net/third_party/quiche/src/quic/core/qpack/qpack_required_insert_count.h" #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" #include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" @@ -16,6 +17,7 @@ namespace quic { QpackProgressiveDecoder::QpackProgressiveDecoder( QuicStreamId stream_id, + BlockedStreamLimitEnforcer* enforcer, QpackHeaderTable* header_table, QpackDecoderStreamSender* decoder_stream_sender, HeadersHandlerInterface* handler) @@ -23,6 +25,7 @@ QpackProgressiveDecoder::QpackProgressiveDecoder( prefix_decoder_( QuicMakeUnique<QpackInstructionDecoder>(QpackPrefixLanguage(), this)), instruction_decoder_(QpackRequestStreamLanguage(), this), + enforcer_(enforcer), header_table_(header_table), decoder_stream_sender_(decoder_stream_sender), handler_(handler), @@ -116,6 +119,7 @@ void QpackProgressiveDecoder::OnInsertCountReachedThreshold() { } blocked_ = false; + enforcer_->OnStreamUnblocked(stream_id_); if (!decoding_) { FinishDecoding(); @@ -125,8 +129,8 @@ void QpackProgressiveDecoder::OnInsertCountReachedThreshold() { bool QpackProgressiveDecoder::DoIndexedHeaderFieldInstruction() { if (!instruction_decoder_.s_bit()) { uint64_t absolute_index; - if (!RequestStreamRelativeIndexToAbsoluteIndex( - instruction_decoder_.varint(), &absolute_index)) { + if (!QpackRequestStreamRelativeIndexToAbsoluteIndex( + instruction_decoder_.varint(), base_, &absolute_index)) { OnError("Invalid relative index."); return false; } @@ -164,8 +168,8 @@ bool QpackProgressiveDecoder::DoIndexedHeaderFieldInstruction() { bool QpackProgressiveDecoder::DoIndexedHeaderFieldPostBaseInstruction() { uint64_t absolute_index; - if (!PostBaseIndexToAbsoluteIndex(instruction_decoder_.varint(), - &absolute_index)) { + if (!QpackPostBaseIndexToAbsoluteIndex(instruction_decoder_.varint(), base_, + &absolute_index)) { OnError("Invalid post-base index."); return false; } @@ -193,8 +197,8 @@ bool QpackProgressiveDecoder::DoIndexedHeaderFieldPostBaseInstruction() { bool QpackProgressiveDecoder::DoLiteralHeaderFieldNameReferenceInstruction() { if (!instruction_decoder_.s_bit()) { uint64_t absolute_index; - if (!RequestStreamRelativeIndexToAbsoluteIndex( - instruction_decoder_.varint(), &absolute_index)) { + if (!QpackRequestStreamRelativeIndexToAbsoluteIndex( + instruction_decoder_.varint(), base_, &absolute_index)) { OnError("Invalid relative index."); return false; } @@ -232,8 +236,8 @@ bool QpackProgressiveDecoder::DoLiteralHeaderFieldNameReferenceInstruction() { bool QpackProgressiveDecoder::DoLiteralHeaderFieldPostBaseInstruction() { uint64_t absolute_index; - if (!PostBaseIndexToAbsoluteIndex(instruction_decoder_.varint(), - &absolute_index)) { + if (!QpackPostBaseIndexToAbsoluteIndex(instruction_decoder_.varint(), base_, + &absolute_index)) { OnError("Invalid post-base index."); return false; } @@ -286,6 +290,10 @@ bool QpackProgressiveDecoder::DoPrefixInstruction() { if (required_insert_count_ > header_table_->inserted_entry_count()) { blocked_ = true; + if (!enforcer_->OnStreamBlocked(stream_id_)) { + OnError("Limit on number of blocked streams exceeded."); + return false; + } header_table_->RegisterObserver(this, required_insert_count_); } @@ -316,7 +324,10 @@ void QpackProgressiveDecoder::FinishDecoding() { return; } - decoder_stream_sender_->SendHeaderAcknowledgement(stream_id_); + if (required_insert_count_ > 0) { + decoder_stream_sender_->SendHeaderAcknowledgement(stream_id_); + } + handler_->OnDecodingCompleted(); } @@ -340,27 +351,4 @@ bool QpackProgressiveDecoder::DeltaBaseToBase(bool sign, return true; } -bool QpackProgressiveDecoder::RequestStreamRelativeIndexToAbsoluteIndex( - uint64_t relative_index, - uint64_t* absolute_index) const { - if (relative_index == std::numeric_limits<uint64_t>::max() || - relative_index + 1 > base_) { - return false; - } - - *absolute_index = base_ - 1 - relative_index; - return true; -} - -bool QpackProgressiveDecoder::PostBaseIndexToAbsoluteIndex( - uint64_t post_base_index, - uint64_t* absolute_index) const { - if (post_base_index >= std::numeric_limits<uint64_t>::max() - base_) { - return false; - } - - *absolute_index = base_ + post_base_index; - return true; -} - } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_progressive_decoder.h b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_progressive_decoder.h index 3abf4279fa2..5c116b1d525 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_progressive_decoder.h +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_progressive_decoder.h @@ -49,8 +49,26 @@ class QUIC_EXPORT_PRIVATE QpackProgressiveDecoder virtual void OnDecodingErrorDetected(QuicStringPiece error_message) = 0; }; + // Interface for keeping track of blocked streams for the purpose of enforcing + // the limit communicated to peer via QPACK_BLOCKED_STREAMS settings. + class QUIC_EXPORT_PRIVATE BlockedStreamLimitEnforcer { + public: + virtual ~BlockedStreamLimitEnforcer() {} + + // Called when the stream becomes blocked. Returns true if allowed. Returns + // false if limit is violated, in which case QpackProgressiveDecoder signals + // an error. + // Stream must not be already blocked. + virtual bool OnStreamBlocked(QuicStreamId stream_id) = 0; + + // Called when the stream becomes unblocked. + // Stream must be blocked. + virtual void OnStreamUnblocked(QuicStreamId stream_id) = 0; + }; + QpackProgressiveDecoder() = delete; QpackProgressiveDecoder(QuicStreamId stream_id, + BlockedStreamLimitEnforcer* enforcer, QpackHeaderTable* header_table, QpackDecoderStreamSender* decoder_stream_sender, HeadersHandlerInterface* handler); @@ -89,18 +107,6 @@ class QUIC_EXPORT_PRIVATE QpackProgressiveDecoder // failure due to overflow/underflow. bool DeltaBaseToBase(bool sign, uint64_t delta_base, uint64_t* base); - // The request stream can use relative index (but different from the kind of - // relative index used on the encoder stream), and post-base index. - // These methods convert relative index and post-base index to absolute index - // (one based). They return true on success, or false if conversion fails due - // to overflow/underflow. On success, |*absolute_index| is guaranteed to be - // strictly less than std::numeric_limits<uint64_t>::max(). - bool RequestStreamRelativeIndexToAbsoluteIndex( - uint64_t relative_index, - uint64_t* absolute_index) const; - bool PostBaseIndexToAbsoluteIndex(uint64_t post_base_index, - uint64_t* absolute_index) const; - const QuicStreamId stream_id_; // |prefix_decoder_| only decodes a handful of bytes then it can be @@ -109,6 +115,7 @@ class QUIC_EXPORT_PRIVATE QpackProgressiveDecoder std::unique_ptr<QpackInstructionDecoder> prefix_decoder_; QpackInstructionDecoder instruction_decoder_; + BlockedStreamLimitEnforcer* const enforcer_; QpackHeaderTable* const header_table_; QpackDecoderStreamSender* const decoder_stream_sender_; HeadersHandlerInterface* const handler_; diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_receive_stream.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_receive_stream.cc index d5f35fb901c..59203986b7d 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_receive_stream.cc +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_receive_stream.cc @@ -7,8 +7,10 @@ #include "net/third_party/quiche/src/quic/core/quic_session.h" namespace quic { -QpackReceiveStream::QpackReceiveStream(PendingStream* pending) - : QuicStream(pending, READ_UNIDIRECTIONAL, /*is_static=*/true) {} +QpackReceiveStream::QpackReceiveStream(PendingStream* pending, + QpackStreamReceiver* receiver) + : QuicStream(pending, READ_UNIDIRECTIONAL, /*is_static=*/true), + receiver_(receiver) {} void QpackReceiveStream::OnStreamReset(const QuicRstStreamFrame& /*frame*/) { // TODO(renjietang) Change the error code to H/3 specific @@ -18,4 +20,15 @@ void QpackReceiveStream::OnStreamReset(const QuicRstStreamFrame& /*frame*/) { ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); } +void QpackReceiveStream::OnDataAvailable() { + iovec iov; + while (!reading_stopped() && sequencer()->GetReadableRegion(&iov)) { + DCHECK(!sequencer()->IsClosed()); + + receiver_->Decode(QuicStringPiece( + reinterpret_cast<const char*>(iov.iov_base), iov.iov_len)); + sequencer()->MarkConsumed(iov.iov_len); + } +} + } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_receive_stream.h b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_receive_stream.h index db18f7c6462..0613871625e 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_receive_stream.h +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_receive_stream.h @@ -5,6 +5,7 @@ #ifndef QUICHE_QUIC_CORE_QPACK_QPACK_RECEIVE_STREAM_H_ #define QUICHE_QUIC_CORE_QPACK_QPACK_RECEIVE_STREAM_H_ +#include "net/third_party/quiche/src/quic/core/qpack/qpack_stream_receiver.h" #include "net/third_party/quiche/src/quic/core/quic_stream.h" #include "net/third_party/quiche/src/quic/platform/api/quic_export.h" @@ -18,7 +19,7 @@ class QUIC_EXPORT_PRIVATE QpackReceiveStream : public QuicStream { public: // Construct receive stream from pending stream, the |pending| object needs // to be deleted after the construction. - explicit QpackReceiveStream(PendingStream* pending); + QpackReceiveStream(PendingStream* pending, QpackStreamReceiver* receiver); QpackReceiveStream(const QpackReceiveStream&) = delete; QpackReceiveStream& operator=(const QpackReceiveStream&) = delete; ~QpackReceiveStream() override = default; @@ -27,9 +28,13 @@ class QUIC_EXPORT_PRIVATE QpackReceiveStream : public QuicStream { // closed before connection. void OnStreamReset(const QuicRstStreamFrame& frame) override; - // Implementation of QuicStream. Unimplemented yet. - // TODO(bnc): Feed data to QPACK. - void OnDataAvailable() override {} + // Implementation of QuicStream. + void OnDataAvailable() override; + + void SetUnblocked() { sequencer()->SetUnblocked(); } + + private: + QpackStreamReceiver* receiver_; }; } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_receive_stream_test.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_receive_stream_test.cc index 121043fc576..df081d1765b 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_receive_stream_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_receive_stream_test.cc @@ -55,13 +55,16 @@ class QpackReceiveStreamTest : public QuicTestWithParam<TestParams> { SupportedVersions(GetParam().version))), session_(connection_) { session_.Initialize(); - PendingStream* pending = - new PendingStream(QuicUtils::GetFirstUnidirectionalStreamId( - GetParam().version.transport_version, - QuicUtils::InvertPerspective(perspective())), - &session_); - qpack_receive_stream_ = QuicMakeUnique<QpackReceiveStream>(pending); - delete pending; + QuicStreamId id = perspective() == Perspective::IS_SERVER + ? GetNthClientInitiatedUnidirectionalStreamId( + session_.transport_version(), 3) + : GetNthServerInitiatedUnidirectionalStreamId( + session_.transport_version(), 3); + char type[] = {0x03}; + QuicStreamFrame data1(id, false, 0, QuicStringPiece(type, 1)); + session_.OnStreamFrame(data1); + qpack_receive_stream_ = + QuicSpdySessionPeer::GetQpackDecoderReceiveStream(&session_); } Perspective perspective() const { return GetParam().perspective; } @@ -70,7 +73,7 @@ class QpackReceiveStreamTest : public QuicTestWithParam<TestParams> { MockAlarmFactory alarm_factory_; StrictMock<MockQuicConnection>* connection_; StrictMock<MockQuicSpdySession> session_; - std::unique_ptr<QpackReceiveStream> qpack_receive_stream_; + QpackReceiveStream* qpack_receive_stream_; }; INSTANTIATE_TEST_SUITE_P(Tests, diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_round_trip_test.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_round_trip_test.cc index 1e9de2d7651..9441b825c5f 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_round_trip_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_round_trip_test.cc @@ -29,17 +29,20 @@ class QpackRoundTripTest : public QuicTestWithParam<FragmentMode> { const spdy::SpdyHeaderBlock& header_list) { NoopDecoderStreamErrorDelegate decoder_stream_error_delegate; NoopQpackStreamSenderDelegate encoder_stream_sender_delegate; - QpackEncoder encoder(&decoder_stream_error_delegate, - &encoder_stream_sender_delegate); + QpackEncoder encoder(&decoder_stream_error_delegate); + encoder.set_qpack_stream_sender_delegate(&encoder_stream_sender_delegate); std::string encoded_header_block = - encoder.EncodeHeaderList(/* stream_id = */ 1, &header_list); + encoder.EncodeHeaderList(/* stream_id = */ 1, header_list); TestHeadersHandler handler; NoopEncoderStreamErrorDelegate encoder_stream_error_delegate; NoopQpackStreamSenderDelegate decoder_stream_sender_delegate; - QpackDecode(&encoder_stream_error_delegate, &decoder_stream_sender_delegate, - &handler, FragmentModeToFragmentSizeGenerator(GetParam()), - encoded_header_block); + // TODO(b/112770235): Test dynamic table and blocked streams. + QpackDecode( + /* maximum_dynamic_table_capacity = */ 0, + /* maximum_blocked_streams = */ 0, &encoder_stream_error_delegate, + &decoder_stream_sender_delegate, &handler, + FragmentModeToFragmentSizeGenerator(GetParam()), encoded_header_block); EXPECT_TRUE(handler.decoding_completed()); EXPECT_FALSE(handler.decoding_error_detected()); diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_send_stream.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_send_stream.cc index 34bba650a83..50fe687cdd3 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_send_stream.cc +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_send_stream.cc @@ -25,6 +25,11 @@ void QpackSendStream::OnStreamReset(const QuicRstStreamFrame& /*frame*/) { void QpackSendStream::WriteStreamData(QuicStringPiece data) { QuicConnection::ScopedPacketFlusher flusher(session()->connection()); + MaybeSendStreamType(); + WriteOrBufferData(data, false, nullptr); +} + +void QpackSendStream::MaybeSendStreamType() { if (!stream_type_sent_) { char type[sizeof(http3_stream_type_)]; QuicDataWriter writer(QUIC_ARRAYSIZE(type), type); @@ -33,7 +38,6 @@ void QpackSendStream::WriteStreamData(QuicStringPiece data) { nullptr); stream_type_sent_ = true; } - WriteOrBufferData(data, false, nullptr); } } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_send_stream.h b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_send_stream.h index 136a7cc56b2..09c6020cf49 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_send_stream.h +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_send_stream.h @@ -42,6 +42,10 @@ class QUIC_EXPORT_PRIVATE QpackSendStream : public QuicStream, // before the first instruction so that the peer can open an qpack stream. void WriteStreamData(QuicStringPiece data) override; + // TODO(b/112770235): Remove this method once QuicStreamIdManager supports + // creating HTTP/3 unidirectional streams dynamically. + void MaybeSendStreamType(); + private: const uint64_t http3_stream_type_; bool stream_type_sent_; diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_send_stream_test.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_send_stream_test.cc index 46037c95442..8431afc3a04 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_send_stream_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_send_stream_test.cc @@ -56,9 +56,10 @@ class QpackSendStreamTest : public QuicTestWithParam<TestParams> { SupportedVersions(GetParam().version))), session_(connection_) { session_.Initialize(); - qpack_send_stream_ = QuicMakeUnique<QpackSendStream>( - QuicSpdySessionPeer::GetNextOutgoingUnidirectionalStreamId(&session_), - &session_, kQpackEncoderStream); + + qpack_send_stream_ = + QuicSpdySessionPeer::GetQpackDecoderSendStream(&session_); + ON_CALL(session_, WritevData(_, _, _, _, _)) .WillByDefault(Invoke(MockQuicSession::ConsumeData)); } @@ -69,7 +70,7 @@ class QpackSendStreamTest : public QuicTestWithParam<TestParams> { MockAlarmFactory alarm_factory_; StrictMock<MockQuicConnection>* connection_; StrictMock<MockQuicSpdySession> session_; - std::unique_ptr<QpackSendStream> qpack_send_stream_; + QpackSendStream* qpack_send_stream_; }; INSTANTIATE_TEST_SUITE_P(Tests, @@ -89,6 +90,8 @@ TEST_P(QpackSendStreamTest, WriteStreamTypeOnlyFirstTime) { EXPECT_CALL(session_, WritevData(_, _, data.length(), _, _)); qpack_send_stream_->WriteStreamData(QuicStringPiece(data)); + EXPECT_CALL(session_, WritevData(_, _, _, _, _)).Times(0); + qpack_send_stream_->MaybeSendStreamType(); } TEST_P(QpackSendStreamTest, ResetQpackStream) { diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_buffered_packet_store_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_buffered_packet_store_test.cc index a00f2139d37..dbf6c16e627 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_buffered_packet_store_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_buffered_packet_store_test.cc @@ -55,7 +55,7 @@ class QuicBufferedPacketStoreTest : public QuicTest { packet_time_(QuicTime::Zero() + QuicTime::Delta::FromMicroseconds(42)), packet_(packet_content_.data(), packet_content_.size(), packet_time_), invalid_version_(UnsupportedQuicVersion()), - valid_version_(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_44) {} + valid_version_(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_46) {} protected: QuicBufferedPacketStoreVisitor visitor_; diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_config.cc b/chromium/net/third_party/quiche/src/quic/core/quic_config.cc index e24e84b8e7d..22aef80fb03 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_config.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_config.cc @@ -413,7 +413,9 @@ QuicConfig::QuicConfig() alternate_server_address_(kASAD, PRESENCE_OPTIONAL), support_max_header_list_size_(kSMHL, PRESENCE_OPTIONAL), stateless_reset_token_(kSRST, PRESENCE_OPTIONAL), - max_incoming_unidirectional_streams_(kMIUS, PRESENCE_OPTIONAL) { + max_incoming_unidirectional_streams_(kMIUS, PRESENCE_OPTIONAL), + max_ack_delay_ms_(kMAD, PRESENCE_OPTIONAL), + ack_delay_exponent_(kADE, PRESENCE_OPTIONAL) { SetDefaults(); } @@ -540,6 +542,38 @@ uint32_t QuicConfig::ReceivedMaxIncomingUnidirectionalStreams() { return max_incoming_unidirectional_streams_.GetReceivedValue(); } +void QuicConfig::SetMaxAckDelayToSendMs(uint32_t max_ack_delay_ms) { + return max_ack_delay_ms_.SetSendValue(max_ack_delay_ms); +} + +uint32_t QuicConfig::GetMaxAckDelayToToSendMs() const { + return max_ack_delay_ms_.GetSendValue(); +} + +bool QuicConfig::HasReceivedMaxAckDelayMs() const { + return max_ack_delay_ms_.HasReceivedValue(); +} + +uint32_t QuicConfig::ReceivedMaxAckDelayMs() const { + return max_ack_delay_ms_.GetReceivedValue(); +} + +void QuicConfig::SetAckDelayExponentToSend(uint32_t exponent) { + ack_delay_exponent_.SetSendValue(exponent); +} + +uint32_t QuicConfig::GetAckDelayExponentToSend() { + return ack_delay_exponent_.GetSendValue(); +} + +bool QuicConfig::HasReceivedAckDelayExponent() const { + return ack_delay_exponent_.HasReceivedValue(); +} + +uint32_t QuicConfig::ReceivedAckDelayExponent() const { + return ack_delay_exponent_.GetReceivedValue(); +} + bool QuicConfig::HasSetBytesForConnectionIdToSend() const { return bytes_for_connection_id_.HasSendValue(); } @@ -692,7 +726,9 @@ void QuicConfig::SetDefaults() { SetInitialStreamFlowControlWindowToSend(kMinimumFlowControlSendWindow); SetInitialSessionFlowControlWindowToSend(kMinimumFlowControlSendWindow); + SetMaxAckDelayToSendMs(kDefaultDelayedAckTimeMs); SetSupportMaxHeaderListSize(); + SetAckDelayExponentToSend(kDefaultAckDelayExponent); } void QuicConfig::ToHandshakeMessage( @@ -706,6 +742,11 @@ void QuicConfig::ToHandshakeMessage( max_incoming_bidirectional_streams_.ToHandshakeMessage(out); if (VersionHasIetfQuicFrames(transport_version)) { max_incoming_unidirectional_streams_.ToHandshakeMessage(out); + ack_delay_exponent_.ToHandshakeMessage(out); + } + if (GetQuicReloadableFlag(quic_negotiate_ack_delay_time)) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_negotiate_ack_delay_time, 1, 4); + max_ack_delay_ms_.ToHandshakeMessage(out); } bytes_for_connection_id_.ToHandshakeMessage(out); initial_round_trip_time_us_.ToHandshakeMessage(out); @@ -777,6 +818,17 @@ QuicErrorCode QuicConfig::ProcessPeerHello( error = stateless_reset_token_.ProcessPeerHello(peer_hello, hello_type, error_details); } + + if (GetQuicReloadableFlag(quic_negotiate_ack_delay_time) && + error == QUIC_NO_ERROR) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_negotiate_ack_delay_time, 2, 4); + error = max_ack_delay_ms_.ProcessPeerHello(peer_hello, hello_type, + error_details); + } + if (error == QUIC_NO_ERROR) { + error = ack_delay_exponent_.ProcessPeerHello(peer_hello, hello_type, + error_details); + } return error; } @@ -805,7 +857,11 @@ bool QuicConfig::FillTransportParameters(TransportParameters* params) const { max_incoming_bidirectional_streams_.GetSendValue()); params->initial_max_streams_uni.set_value( max_incoming_unidirectional_streams_.GetSendValue()); - params->max_ack_delay.set_value(kDefaultDelayedAckTimeMs); + if (GetQuicReloadableFlag(quic_negotiate_ack_delay_time)) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_negotiate_ack_delay_time, 3, 4); + params->max_ack_delay.set_value(kDefaultDelayedAckTimeMs); + } + params->ack_delay_exponent.set_value(ack_delay_exponent_.GetSendValue()); params->disable_migration = connection_migration_disabled_.HasSendValue() && connection_migration_disabled_.GetSendValue() != 0; @@ -887,7 +943,14 @@ QuicErrorCode QuicConfig::ProcessTransportParameters( initial_stream_flow_control_window_bytes_.SetReceivedValue( std::min<uint64_t>(params.initial_max_stream_data_bidi_local.value(), std::numeric_limits<uint32_t>::max())); - + if (GetQuicReloadableFlag(quic_negotiate_ack_delay_time)) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_negotiate_ack_delay_time, 4, 4); + max_ack_delay_ms_.SetReceivedValue(std::min<uint32_t>( + params.max_ack_delay.value(), std::numeric_limits<uint32_t>::max())); + } + if (params.ack_delay_exponent.IsValid()) { + ack_delay_exponent_.SetReceivedValue(params.ack_delay_exponent.value()); + } connection_migration_disabled_.SetReceivedValue( params.disable_migration ? 1u : 0u); diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_config.h b/chromium/net/third_party/quiche/src/quic/core/quic_config.h index ae15b6aae38..b4a4ca9ceaf 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_config.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_config.h @@ -414,6 +414,21 @@ class QUIC_EXPORT_PRIVATE QuicConfig { QuicUint128 ReceivedStatelessResetToken() const; + // Manage the IETF QUIC Max ACK Delay transport parameter. + // The sent value is the delay that this node uses + // (QuicSentPacketManager::local_max_ack_delay_). + // The received delay is the value received from + // the peer (QuicSentPacketManager::peer_max_ack_delay_). + void SetMaxAckDelayToSendMs(uint32_t max_ack_delay_ms); + uint32_t GetMaxAckDelayToToSendMs() const; + bool HasReceivedMaxAckDelayMs() const; + uint32_t ReceivedMaxAckDelayMs() const; + + void SetAckDelayExponentToSend(uint32_t exponent); + uint32_t GetAckDelayExponentToSend(); + bool HasReceivedAckDelayExponent() const; + uint32_t ReceivedAckDelayExponent() const; + bool negotiated() const; void SetCreateSessionTagIndicators(QuicTagVector tags); @@ -501,6 +516,18 @@ class QUIC_EXPORT_PRIVATE QuicConfig { // Maximum number of incoming unidirectional streams that the connection can // support. QuicFixedUint32 max_incoming_unidirectional_streams_; + + // Maximum ack delay. The sent value is the value used on this node. + // The received value is the value received from the peer and used by + // the peer. + QuicFixedUint32 max_ack_delay_ms_; + + // ack_delay_exponent parameter negotiated in IETF QUIC transport + // parameter negotiation. The sent exponent is the exponent that this + // node uses when serializing an ACK frame (and the peer should use when + // deserializing the frame); the received exponent is the value the peer uses + // to serialize frames and this node uses to deserialize them. + QuicFixedUint32 ack_delay_exponent_; }; } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_config_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_config_test.cc index 84e89c17d2d..630e67ad1a4 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_config_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_config_test.cc @@ -56,6 +56,8 @@ TEST_P(QuicConfigTest, ToHandshakeMessage) { } TEST_P(QuicConfigTest, ProcessClientHello) { + const uint32_t kTestMaxAckDelayMs = + static_cast<uint32_t>(kDefaultDelayedAckTimeMs + 1); QuicConfig client_config; QuicTagVector cgst; cgst.push_back(kQBIC); @@ -70,6 +72,7 @@ TEST_P(QuicConfigTest, ProcessClientHello) { QuicTagVector copt; copt.push_back(kTBBR); client_config.SetConnectionOptionsToSend(copt); + client_config.SetMaxAckDelayToSendMs(kTestMaxAckDelayMs); CryptoHandshakeMessage msg; client_config.ToHandshakeMessage(&msg, GetParam()); @@ -99,6 +102,12 @@ TEST_P(QuicConfigTest, ProcessClientHello) { 2 * kInitialStreamFlowControlWindowForTest); EXPECT_EQ(config_.ReceivedInitialSessionFlowControlWindowBytes(), 2 * kInitialSessionFlowControlWindowForTest); + if (GetQuicReloadableFlag(quic_negotiate_ack_delay_time)) { + EXPECT_TRUE(config_.HasReceivedMaxAckDelayMs()); + EXPECT_EQ(kTestMaxAckDelayMs, config_.ReceivedMaxAckDelayMs()); + } else { + EXPECT_FALSE(config_.HasReceivedMaxAckDelayMs()); + } } TEST_P(QuicConfigTest, ProcessServerHello) { @@ -106,6 +115,8 @@ TEST_P(QuicConfigTest, ProcessServerHello) { host.FromString("127.0.3.1"); const QuicSocketAddress kTestServerAddress = QuicSocketAddress(host, 1234); const QuicUint128 kTestResetToken = MakeQuicUint128(0, 10111100001); + const uint32_t kTestMaxAckDelayMs = + static_cast<uint32_t>(kDefaultDelayedAckTimeMs + 1); QuicConfig server_config; QuicTagVector cgst; cgst.push_back(kQBIC); @@ -119,6 +130,7 @@ TEST_P(QuicConfigTest, ProcessServerHello) { 2 * kInitialSessionFlowControlWindowForTest); server_config.SetAlternateServerAddressToSend(kTestServerAddress); server_config.SetStatelessResetTokenToSend(kTestResetToken); + server_config.SetMaxAckDelayToSendMs(kTestMaxAckDelayMs); CryptoHandshakeMessage msg; server_config.ToHandshakeMessage(&msg, GetParam()); std::string error_details; @@ -137,6 +149,12 @@ TEST_P(QuicConfigTest, ProcessServerHello) { EXPECT_EQ(kTestServerAddress, config_.ReceivedAlternateServerAddress()); EXPECT_TRUE(config_.HasReceivedStatelessResetToken()); EXPECT_EQ(kTestResetToken, config_.ReceivedStatelessResetToken()); + if (GetQuicReloadableFlag(quic_negotiate_ack_delay_time)) { + EXPECT_TRUE(config_.HasReceivedMaxAckDelayMs()); + EXPECT_EQ(kTestMaxAckDelayMs, config_.ReceivedMaxAckDelayMs()); + } else { + EXPECT_FALSE(config_.HasReceivedMaxAckDelayMs()); + } } TEST_P(QuicConfigTest, MissingOptionalValuesInCHLO) { diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_connection.cc b/chromium/net/third_party/quiche/src/quic/core/quic_connection.cc index 22ee18a7e8d..257ef29a2d7 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_connection.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_connection.cc @@ -247,6 +247,7 @@ QuicConnection::QuicConnection( max_tracked_packets_(GetQuicFlag(FLAGS_quic_max_tracked_packet_count)), pending_version_negotiation_packet_(false), send_ietf_version_negotiation_packet_(false), + send_version_negotiation_packet_with_prefixed_lengths_(false), idle_timeout_connection_close_behavior_( ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET), close_connection_after_five_rtos_(false), @@ -320,7 +321,11 @@ QuicConnection::QuicConnection( processing_ack_frame_(false), supports_release_time_(false), release_time_into_future_(QuicTime::Delta::Zero()), - retry_has_been_parsed_(false) { + retry_has_been_parsed_(false), + max_consecutive_ptos_(0), + bytes_received_before_address_validation_(0), + bytes_sent_before_address_validation_(0), + address_validated_(false) { QUIC_DLOG(INFO) << ENDPOINT << "Created connection with server connection ID " << server_connection_id << " and version: " << ParsedQuicVersionToString(version()); @@ -418,6 +423,16 @@ void QuicConnection::SetFromConfig(const QuicConfig& config) { if (config.HasClientSentConnectionOption(k5RTO, perspective_)) { close_connection_after_five_rtos_ = true; } + if (sent_packet_manager_.pto_enabled()) { + if (config.HasClientSentConnectionOption(k7PTO, perspective_)) { + max_consecutive_ptos_ = 6; + QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_pto, 3, 4); + } + if (config.HasClientSentConnectionOption(k8PTO, perspective_)) { + max_consecutive_ptos_ = 7; + QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_pto, 4, 4); + } + } if (config.HasClientSentConnectionOption(kNSTP, perspective_)) { no_stop_waiting_frames_ = true; } @@ -425,6 +440,9 @@ void QuicConnection::SetFromConfig(const QuicConfig& config) { stateless_reset_token_received_ = true; received_stateless_reset_token_ = config.ReceivedStatelessResetToken(); } + if (config.HasReceivedAckDelayExponent()) { + framer_.set_peer_ack_delay_exponent(config.ReceivedAckDelayExponent()); + } if (GetQuicReloadableFlag(quic_send_timestamps) && config.HasClientSentConnectionOption(kSTMP, perspective_)) { QUIC_RELOADABLE_FLAG_COUNT(quic_send_timestamps); @@ -570,9 +588,11 @@ void QuicConnection::OnVersionNegotiationPacket( } if (QuicContainsValue(packet.versions, version())) { - const std::string error_details = - "Server already supports client's version and should have accepted the " - "connection."; + const std::string error_details = QuicStrCat( + "Server already supports client's version ", + ParsedQuicVersionToString(version()), + " and should have accepted the connection instead of sending {", + ParsedQuicVersionVectorToString(packet.versions), "}."); QUIC_DLOG(WARNING) << error_details; CloseConnection(QUIC_INVALID_VERSION_NEGOTIATION_PACKET, error_details, ConnectionCloseBehavior::SILENT_CLOSE); @@ -748,14 +768,17 @@ bool QuicConnection::OnUnauthenticatedHeader(const QuicPacketHeader& header) { void QuicConnection::OnDecryptedPacket(EncryptionLevel level) { last_decrypted_packet_level_ = level; last_packet_decrypted_ = true; + if (EnforceAntiAmplificationLimit() && + last_decrypted_packet_level_ >= ENCRYPTION_HANDSHAKE) { + // Address is validated by successfully processing a HANDSHAKE packet. + address_validated_ = true; + } // Once the server receives a forward secure packet, the handshake is // confirmed. if (level == ENCRYPTION_FORWARD_SECURE && perspective_ == Perspective::IS_SERVER) { - sent_packet_manager_.SetHandshakeConfirmed(); - // This may have changed the retransmission timer, so re-arm it. - SetRetransmissionAlarm(); + OnHandshakeComplete(); } } @@ -1161,18 +1184,40 @@ bool QuicConnection::OnConnectionCloseFrame( if (debug_visitor_ != nullptr) { debug_visitor_->OnConnectionCloseFrame(frame); } - QUIC_DLOG(INFO) << ENDPOINT << "Received ConnectionClose for connection: " - << connection_id() << ", with error: " - << QuicErrorCodeToString(frame.quic_error_code) << " (" - << frame.error_details << ")"; - if (frame.close_type == GOOGLE_QUIC_CONNECTION_CLOSE && - frame.quic_error_code == QUIC_BAD_MULTIPATH_FLAG) { + switch (frame.close_type) { + case GOOGLE_QUIC_CONNECTION_CLOSE: + QUIC_DLOG(INFO) << ENDPOINT << "Received ConnectionClose for connection: " + << connection_id() << ", with error: " + << QuicErrorCodeToString(frame.extracted_error_code) + << " (" << frame.error_details << ")"; + break; + case IETF_QUIC_TRANSPORT_CONNECTION_CLOSE: + QUIC_DLOG(INFO) << ENDPOINT + << "Received Transport ConnectionClose for connection: " + << connection_id() << ", with error: " + << QuicErrorCodeToString(frame.extracted_error_code) + << " (" << frame.error_details << ")" + << ", transport error code: " + << frame.transport_error_code << ", error frame type: " + << frame.transport_close_frame_type; + break; + case IETF_QUIC_APPLICATION_CONNECTION_CLOSE: + QUIC_DLOG(INFO) << ENDPOINT + << "Received Application ConnectionClose for connection: " + << connection_id() << ", with error: " + << QuicErrorCodeToString(frame.extracted_error_code) + << " (" << frame.error_details << ")" + << ", application error code: " + << frame.application_error_code; + break; + } + + if (frame.extracted_error_code == QUIC_BAD_MULTIPATH_FLAG) { QUIC_LOG_FIRST_N(ERROR, 10) << "Unexpected QUIC_BAD_MULTIPATH_FLAG error." << " last_received_header: " << last_header_ << " encryption_level: " << encryption_level_; } - TearDownLocalConnectionState(frame.quic_error_code, frame.error_details, - ConnectionCloseSource::FROM_PEER); + TearDownLocalConnectionState(frame, ConnectionCloseSource::FROM_PEER); return connected_; } @@ -1215,9 +1260,9 @@ bool QuicConnection::OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) { if (debug_visitor_ != nullptr) { debug_visitor_->OnWindowUpdateFrame(frame, time_of_last_received_packet_); } - QUIC_DLOG(INFO) << ENDPOINT << "WINDOW_UPDATE_FRAME received for stream: " - << frame.stream_id - << " with byte offset: " << frame.byte_offset; + QUIC_DVLOG(1) << ENDPOINT << "WINDOW_UPDATE_FRAME received for stream: " + << frame.stream_id + << " with byte offset: " << frame.byte_offset; visitor_->OnWindowUpdateFrame(frame); should_last_packet_instigate_acks_ = true; return connected_; @@ -1290,28 +1335,29 @@ void QuicConnection::OnPacketComplete() { << ENDPOINT << "Received a padded PING packet. is_probing: " << IsCurrentPacketConnectivityProbing(); - if (perspective_ == Perspective::IS_CLIENT) { - QUIC_DVLOG(1) << ENDPOINT - << "Received a speculative connectivity probing packet for " + if (IsCurrentPacketConnectivityProbing()) { + QUIC_DVLOG(1) << ENDPOINT << "Received a connectivity probing packet for " << GetServerConnectionIdAsRecipient(last_header_, perspective_) << " from ip:port: " << last_packet_source_address_.ToString() << " to ip:port: " << last_packet_destination_address_.ToString(); - // TODO(zhongyi): change the method name. - visitor_->OnConnectivityProbeReceived(last_packet_destination_address_, - last_packet_source_address_); - } else if (IsCurrentPacketConnectivityProbing()) { - // This node is not a client (is a server) AND the received packet was - // connectivity-probing, send an appropriate response. - QUIC_DVLOG(1) << ENDPOINT << "Received a connectivity probing packet for " + visitor_->OnPacketReceived(last_packet_destination_address_, + last_packet_source_address_, + /*is_connectivity_probe=*/true); + } else if (perspective_ == Perspective::IS_CLIENT) { + // This node is a client, notify that a speculative connectivity probing + // packet has been received anyway. + QUIC_DVLOG(1) << ENDPOINT + << "Received a speculative connectivity probing packet for " << GetServerConnectionIdAsRecipient(last_header_, perspective_) << " from ip:port: " << last_packet_source_address_.ToString() << " to ip:port: " << last_packet_destination_address_.ToString(); - visitor_->OnConnectivityProbeReceived(last_packet_destination_address_, - last_packet_source_address_); + visitor_->OnPacketReceived(last_packet_destination_address_, + last_packet_source_address_, + /*is_connectivity_probe=*/false); } else { // This node is not a client (is a server) AND the received packet was // NOT connectivity-probing. If the packet had PATH CHALLENGES, send @@ -1357,8 +1403,7 @@ void QuicConnection::OnPacketComplete() { uber_received_packet_manager_.MaybeUpdateAckTimeout( should_last_packet_instigate_acks_, last_decrypted_packet_level_, last_header_.packet_number, time_of_last_received_packet_, - clock_->ApproximateNow(), sent_packet_manager_.GetRttStats(), - sent_packet_manager_.local_max_ack_delay()); + clock_->ApproximateNow(), sent_packet_manager_.GetRttStats()); } else { QUIC_DLOG(INFO) << ENDPOINT << "Not updating ACK timeout for " << QuicUtils::EncryptionLevelToString( @@ -1452,9 +1497,11 @@ void QuicConnection::MaybeSendInResponseToPacket() { } } -void QuicConnection::SendVersionNegotiationPacket(bool ietf_quic) { +void QuicConnection::SendVersionNegotiationPacket(bool ietf_quic, + bool has_length_prefix) { pending_version_negotiation_packet_ = true; send_ietf_version_negotiation_packet_ = ietf_quic; + send_version_negotiation_packet_with_prefixed_lengths_ = has_length_prefix; if (HandleWriteBlocked()) { return; @@ -1466,7 +1513,7 @@ void QuicConnection::SendVersionNegotiationPacket(bool ietf_quic) { << "}, " << (ietf_quic ? "" : "!") << "ietf_quic"; std::unique_ptr<QuicEncryptedPacket> version_packet( packet_generator_.SerializeVersionNegotiationPacket( - ietf_quic, framer_.supported_versions())); + ietf_quic, has_length_prefix, framer_.supported_versions())); QUIC_DVLOG(2) << ENDPOINT << "Sending version negotiation packet: {" << ParsedQuicVersionVectorToString(framer_.supported_versions()) << "}, " << (ietf_quic ? "" : "!") << "ietf_quic:" << std::endl @@ -1498,7 +1545,9 @@ size_t QuicConnection::SendCryptoData(EncryptionLevel level, QUIC_BUG << "Attempt to send empty crypto frame"; return 0; } - + if (!ShouldGeneratePacket(HAS_RETRANSMITTABLE_DATA, IS_HANDSHAKE)) { + return 0; + } ScopedPacketFlusher flusher(this); return packet_generator_.ConsumeCryptoData(level, write_length, offset); } @@ -1522,6 +1571,18 @@ QuicConsumedData QuicConnection::SendStreamData(QuicStreamId id, } bool QuicConnection::SendControlFrame(const QuicFrame& frame) { + if (SupportsMultiplePacketNumberSpaces() && + (encryption_level_ == ENCRYPTION_INITIAL || + encryption_level_ == ENCRYPTION_HANDSHAKE) && + frame.type != PING_FRAME) { + // Allow PING frame to be sent without APPLICATION key. For example, when + // anti-amplification limit is used, client needs to send something to avoid + // handshake deadlock. + QUIC_DVLOG(1) << ENDPOINT << "Failed to send control frame: " << frame + << " at encryption level: " + << QuicUtils::EncryptionLevelToString(encryption_level_); + return false; + } ScopedPacketFlusher flusher(this); const bool consumed = packet_generator_.ConsumeRetransmittableControlFrame(frame); @@ -1605,6 +1666,45 @@ void QuicConnection::OnCoalescedPacket(const QuicEncryptedPacket& packet) { QueueCoalescedPacket(packet); } +void QuicConnection::OnUndecryptablePacket(const QuicEncryptedPacket& packet, + EncryptionLevel decryption_level, + bool has_decryption_key) { + QUIC_DVLOG(1) << ENDPOINT << "Received undecryptable packet of length " + << packet.length() << " with" + << (has_decryption_key ? "" : "out") << " key at level " + << QuicUtils::EncryptionLevelToString(decryption_level) + << " while connection is at encryption level " + << QuicUtils::EncryptionLevelToString(encryption_level_); + DCHECK(GetQuicRestartFlag(quic_framer_uses_undecryptable_upcall)); + QUIC_RESTART_FLAG_COUNT_N(quic_framer_uses_undecryptable_upcall, 1, 7); + DCHECK(EncryptionLevelIsValid(decryption_level)); + ++stats_.undecryptable_packets_received; + + bool should_enqueue = true; + if (encryption_level_ == ENCRYPTION_FORWARD_SECURE) { + // We do not expect to install any further keys. + should_enqueue = false; + } else if (undecryptable_packets_.size() >= max_undecryptable_packets_) { + // We do not queue more than max_undecryptable_packets_ packets. + should_enqueue = false; + } else if (has_decryption_key) { + // We already have the key for this decryption level, therefore no + // future keys will allow it be decrypted. + should_enqueue = false; + } else if (version().KnowsWhichDecrypterToUse() && + decryption_level <= encryption_level_) { + // On versions that know which decrypter to use, we install keys in order + // so we will not get newer keys for lower encryption levels. + should_enqueue = false; + } + + if (should_enqueue) { + QueueUndecryptablePacket(packet); + } else if (debug_visitor_ != nullptr) { + debug_visitor_->OnUndecryptablePacket(); + } +} + void QuicConnection::ProcessUdpPacket(const QuicSocketAddress& self_address, const QuicSocketAddress& peer_address, const QuicReceivedPacket& packet) { @@ -1647,6 +1747,9 @@ void QuicConnection::ProcessUdpPacket(const QuicSocketAddress& self_address, stats_.bytes_received += packet.length(); ++stats_.packets_received; + if (EnforceAntiAmplificationLimit()) { + bytes_received_before_address_validation_ += last_size_; + } // Ensure the time coming from the packet reader is within 2 minutes of now. if (std::abs((packet.receipt_time() - clock_->ApproximateNow()).ToSeconds()) > @@ -1664,7 +1767,8 @@ void QuicConnection::ProcessUdpPacket(const QuicSocketAddress& self_address, if (!framer_.ProcessPacket(packet)) { // If we are unable to decrypt this packet, it might be // because the CHLO or SHLO packet was lost. - if (framer_.error() == QUIC_DECRYPTION_FAILURE) { + if (framer_.error() == QUIC_DECRYPTION_FAILURE && + !GetQuicRestartFlag(quic_framer_uses_undecryptable_upcall)) { ++stats_.undecryptable_packets_received; if (encryption_level_ != ENCRYPTION_FORWARD_SECURE && undecryptable_packets_.size() < max_undecryptable_packets_) { @@ -1672,6 +1776,8 @@ void QuicConnection::ProcessUdpPacket(const QuicSocketAddress& self_address, } else if (debug_visitor_ != nullptr) { debug_visitor_->OnUndecryptablePacket(); } + } else if (GetQuicRestartFlag(quic_framer_uses_undecryptable_upcall)) { + QUIC_RESTART_FLAG_COUNT_N(quic_framer_uses_undecryptable_upcall, 2, 7); } QUIC_DVLOG(1) << ENDPOINT << "Unable to process packet. Last packet processed: " @@ -1865,7 +1971,9 @@ void QuicConnection::WriteQueuedPackets() { DCHECK(!writer_->IsWriteBlocked()); if (pending_version_negotiation_packet_) { - SendVersionNegotiationPacket(send_ietf_version_negotiation_packet_); + SendVersionNegotiationPacket( + send_ietf_version_negotiation_packet_, + send_version_negotiation_packet_with_prefixed_lengths_); } QUIC_CLIENT_HISTOGRAM_COUNTS("QuicSession.NumQueuedPacketsBeforeWrite", @@ -1961,6 +2069,11 @@ bool QuicConnection::ShouldGeneratePacket( // We should serialize handshake packets immediately to ensure that they // end up sent at the right encryption level. if (handshake == IS_HANDSHAKE) { + if (LimitedByAmplificationFactor()) { + // Server is constrained by the amplification restriction. + QUIC_DVLOG(1) << ENDPOINT << "Constrained by amplification restriction"; + return false; + } return true; } @@ -2181,11 +2294,16 @@ bool QuicConnection::WritePacket(SerializedPacket* packet) { QUIC_DVLOG(1) << ENDPOINT << "time we began writing last sent packet: " << packet_send_time.ToDebuggingValue(); - bool reset_retransmission_alarm = sent_packet_manager_.OnPacketSent( + if (EnforceAntiAmplificationLimit()) { + // Include bytes sent even if they are not in flight. + bytes_sent_before_address_validation_ += packet->encrypted_length; + } + + const bool in_flight = sent_packet_manager_.OnPacketSent( packet, packet->original_packet_number, packet_send_time, packet->transmission_type, IsRetransmittable(*packet)); - if (reset_retransmission_alarm || !retransmission_alarm_->IsSet()) { + if (in_flight || !retransmission_alarm_->IsSet()) { SetRetransmissionAlarm(); } SetPingAlarm(); @@ -2426,14 +2544,11 @@ void QuicConnection::OnPathDegradingTimeout() { } void QuicConnection::OnRetransmissionTimeout() { - DCHECK(!sent_packet_manager_.unacked_packets().empty()); + DCHECK(!sent_packet_manager_.unacked_packets().empty() || + (sent_packet_manager_.handshake_mode_disabled() && + !sent_packet_manager_.handshake_confirmed())); const QuicPacketNumber previous_created_packet_number = packet_generator_.packet_number(); - const size_t previous_crypto_retransmit_count = - stats_.crypto_retransmit_count; - const size_t previous_loss_timeout_count = stats_.loss_timeout_count; - const size_t previous_tlp_count = stats_.tlp_count; - const size_t pervious_rto_count = stats_.rto_count; if (close_connection_after_five_rtos_ && sent_packet_manager_.GetConsecutiveRtoCount() >= 4) { // Close on the 5th consecutive RTO, so after 4 previous RTOs have occurred. @@ -2441,8 +2556,17 @@ void QuicConnection::OnRetransmissionTimeout() { ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); return; } + if (sent_packet_manager_.pto_enabled() && max_consecutive_ptos_ > 0 && + sent_packet_manager_.GetConsecutivePtoCount() >= max_consecutive_ptos_) { + CloseConnection(QUIC_TOO_MANY_RTOS, + QuicStrCat(max_consecutive_ptos_ + 1, + "consecutive retransmission timeouts"), + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return; + } - sent_packet_manager_.OnRetransmissionTimeout(); + const auto retransmission_mode = + sent_packet_manager_.OnRetransmissionTimeout(); WriteIfNotBlocked(); // A write failure can result in the connection being closed, don't attempt to @@ -2451,34 +2575,52 @@ void QuicConnection::OnRetransmissionTimeout() { return; } - // In the TLP case, the SentPacketManager gives the connection the opportunity - // to send new data before retransmitting. - if (sent_packet_manager_.MaybeRetransmitTailLossProbe()) { + // In the PTO and TLP cases, the SentPacketManager gives the connection the + // opportunity to send new data before retransmitting. + if (sent_packet_manager_.pto_enabled()) { + sent_packet_manager_.MaybeSendProbePackets(); + } else if (sent_packet_manager_.MaybeRetransmitTailLossProbe()) { // Send the pending retransmission now that it's been queued. WriteIfNotBlocked(); } if (sent_packet_manager_.fix_rto_retransmission()) { - // Making sure at least one packet is created when retransmission timer - // fires in TLP, RTO or HANDSHAKE mode. It is possible that loss algorithm - // invokes timer based loss but the packet does not need to be - // retransmitted. - QUIC_BUG_IF(stats_.loss_timeout_count == previous_loss_timeout_count && - packet_generator_.packet_number() == - previous_created_packet_number) - << "previous_crypto_retransmit_count: " - << previous_crypto_retransmit_count - << ", crypto_retransmit_count: " << stats_.crypto_retransmit_count - << ", previous_loss_timeout_count: " << previous_loss_timeout_count - << ", loss_timeout_count: " << stats_.loss_timeout_count - << ", previous_tlp_count: " << previous_tlp_count - << ", tlp_count: " << stats_.tlp_count - << ", pervious_rto_count: " << pervious_rto_count - << ", rto_count: " << stats_.rto_count - << ", previous_created_packet_number: " - << previous_created_packet_number - << ", packet_number: " << packet_generator_.packet_number() - << ", session has data to write: " << visitor_->WillingAndAbleToWrite(); + if (packet_generator_.packet_number() == previous_created_packet_number && + (retransmission_mode == QuicSentPacketManager::TLP_MODE || + retransmission_mode == QuicSentPacketManager::RTO_MODE || + retransmission_mode == QuicSentPacketManager::PTO_MODE) && + !visitor_->WillingAndAbleToWrite()) { + // Send PING if timer fires in RTO or PTO mode but there is no data to + // send. + // When TLP fires, either new data or tail loss probe should be sent. + // There is corner case where TLP fires after RTO because packets get + // acked. Two packets are marked RTO_RETRANSMITTED, but the first packet + // is retransmitted as two packets because of packet number length + // increases (please see QuicConnectionTest.RtoPacketAsTwo). + QUIC_BUG_IF(retransmission_mode == QuicSentPacketManager::TLP_MODE && + stats_.rto_count == 0); + DCHECK_LT(0u, sent_packet_manager_.pending_timer_transmission_count()); + visitor_->SendPing(); + } + if (retransmission_mode == QuicSentPacketManager::PTO_MODE) { + sent_packet_manager_.AdjustPendingTimerTransmissions(); + } + if (retransmission_mode != QuicSentPacketManager::LOSS_MODE) { + // When timer fires in TLP or RTO mode, ensure 1) at least one packet is + // created, or there is data to send and available credit (such that + // packets will be sent eventually). + QUIC_BUG_IF( + packet_generator_.packet_number() == previous_created_packet_number && + (!visitor_->WillingAndAbleToWrite() || + sent_packet_manager_.pending_timer_transmission_count() == 0u)) + << "retransmission_mode: " << retransmission_mode + << ", packet_number: " << packet_generator_.packet_number() + << ", session has data to write: " + << visitor_->WillingAndAbleToWrite() + << ", writer is blocked: " << writer_->IsWriteBlocked() + << ", pending_timer_transmission_count: " + << sent_packet_manager_.pending_timer_transmission_count(); + } } // Ensure the retransmission alarm is always set if there are unacked packets @@ -2502,6 +2644,9 @@ void QuicConnection::SetDiversificationNonce( } void QuicConnection::SetDefaultEncryptionLevel(EncryptionLevel level) { + QUIC_DVLOG(1) << ENDPOINT << "Setting default encryption level from " + << QuicUtils::EncryptionLevelToString(encryption_level_) + << " to " << QuicUtils::EncryptionLevelToString(level); if (level != encryption_level_ && packet_generator_.HasPendingFrames()) { // Flush all queued frames when encryption level changes. ScopedPacketFlusher flusher(this); @@ -2557,6 +2702,16 @@ const QuicDecrypter* QuicConnection::alternative_decrypter() const { void QuicConnection::QueueUndecryptablePacket( const QuicEncryptedPacket& packet) { + if (GetQuicRestartFlag(quic_framer_uses_undecryptable_upcall)) { + QUIC_RESTART_FLAG_COUNT_N(quic_framer_uses_undecryptable_upcall, 3, 7); + for (const auto& saved_packet : undecryptable_packets_) { + if (packet.data() == saved_packet->data() && + packet.length() == saved_packet->length()) { + QUIC_DVLOG(1) << ENDPOINT << "Not queueing known undecryptable packet"; + return; + } + } + } QUIC_DVLOG(1) << ENDPOINT << "Queueing undecryptable packet."; undecryptable_packets_.push_back(packet.Clone()); } @@ -2611,21 +2766,26 @@ void QuicConnection::QueueCoalescedPacket(const QuicEncryptedPacket& packet) { void QuicConnection::MaybeProcessCoalescedPackets() { bool processed = false; - for (const auto& packet : coalesced_packets_) { + while (connected_ && !coalesced_packets_.empty()) { + // Making sure there are no pending frames when processing the next + // coalesced packet because the queued ack frame may change. + packet_generator_.FlushAllQueuedFrames(); if (!connected_) { return; } - // } - // while (connected_ && !coalesced_packets_.empty()) { + std::unique_ptr<QuicEncryptedPacket> packet = + std::move(coalesced_packets_.front()); + coalesced_packets_.pop_front(); + QUIC_DVLOG(1) << ENDPOINT << "Processing coalesced packet"; - // QuicEncryptedPacket* packet = coalesced_packets_.front().get(); if (framer_.ProcessPacket(*packet)) { processed = true; } else { // If we are unable to decrypt this packet, it might be // because the CHLO or SHLO packet was lost. - if (framer_.error() == QUIC_DECRYPTION_FAILURE) { + if (framer_.error() == QUIC_DECRYPTION_FAILURE && + !GetQuicRestartFlag(quic_framer_uses_undecryptable_upcall)) { ++stats_.undecryptable_packets_received; if (encryption_level_ != ENCRYPTION_FORWARD_SECURE && undecryptable_packets_.size() < max_undecryptable_packets_) { @@ -2633,11 +2793,11 @@ void QuicConnection::MaybeProcessCoalescedPackets() { } else if (debug_visitor_ != nullptr) { debug_visitor_->OnUndecryptablePacket(); } + } else if (GetQuicRestartFlag(quic_framer_uses_undecryptable_upcall)) { + QUIC_RESTART_FLAG_COUNT_N(quic_framer_uses_undecryptable_upcall, 4, 7); } } - // coalesced_packets_.pop_front(); } - coalesced_packets_.clear(); if (processed) { MaybeProcessUndecryptablePackets(); } @@ -2679,12 +2839,21 @@ void QuicConnection::SendConnectionClosePacket(QuicErrorCode error, !GetUpdatedAckFrame().ack_frame->packets.Empty()) { SendAck(); } - QuicConnectionCloseFrame* frame = - new QuicConnectionCloseFrame(error, details); - // If version99/IETF QUIC set the close type. Default close type is Google - // QUIC. + QuicConnectionCloseFrame* frame; if (VersionHasIetfQuicFrames(transport_version())) { - frame->close_type = IETF_QUIC_TRANSPORT_CONNECTION_CLOSE; + QuicErrorCodeToIetfMapping mapping = + QuicErrorCodeToTransportErrorCode(error); + if (mapping.is_transport_close_) { + frame = new QuicConnectionCloseFrame( + error, details, mapping.transport_error_code_, + framer_.current_received_frame_type()); + } else { + // Maps to an application close. + frame = new QuicConnectionCloseFrame(error, details, + mapping.application_error_code_); + } + } else { + frame = new QuicConnectionCloseFrame(error, details); } packet_generator_.ConsumeRetransmittableControlFrame(QuicFrame(frame)); packet_generator_.FlushAllQueuedFrames(); @@ -2698,6 +2867,13 @@ void QuicConnection::TearDownLocalConnectionState( QuicErrorCode error, const std::string& error_details, ConnectionCloseSource source) { + QuicConnectionCloseFrame frame(error, error_details); + return TearDownLocalConnectionState(frame, source); +} + +void QuicConnection::TearDownLocalConnectionState( + const QuicConnectionCloseFrame& frame, + ConnectionCloseSource source) { if (!connected_) { QUIC_DLOG(INFO) << "Connection is already closed."; return; @@ -2707,9 +2883,6 @@ void QuicConnection::TearDownLocalConnectionState( FlushPackets(); connected_ = false; DCHECK(visitor_ != nullptr); - // TODO(fkastenholz): When the IETF Transport Connection Close information - // gets plumbed in, expand this constructor to include that information. - QuicConnectionCloseFrame frame(error, error_details); visitor_->OnConnectionClosed(frame, source); if (debug_visitor_ != nullptr) { debug_visitor_->OnConnectionClosed(frame, source); @@ -2879,8 +3052,14 @@ void QuicConnection::SetRetransmissionAlarm() { pending_retransmission_alarm_ = true; return; } - QuicTime retransmission_time = sent_packet_manager_.GetRetransmissionTime(); - retransmission_alarm_->Update(retransmission_time, + if (LimitedByAmplificationFactor()) { + // Do not set retransmission timer if connection is anti-amplification limit + // throttled. Otherwise, nothing can be sent when timer fires. + retransmission_alarm_->Cancel(); + return; + } + + retransmission_alarm_->Update(sent_packet_manager_.GetRetransmissionTime(), QuicTime::Delta::FromMilliseconds(1)); } @@ -3403,6 +3582,9 @@ void QuicConnection::MaybeEnableSessionDecidesWhatToWrite() { transport_version() > QUIC_VERSION_39; sent_packet_manager_.SetSessionDecideWhatToWrite( enable_session_decides_what_to_write); + if (version().SupportsAntiAmplificationLimit()) { + sent_packet_manager_.DisableHandshakeMode(); + } packet_generator_.SetCanSetTransmissionType( enable_session_decides_what_to_write); } @@ -3538,6 +3720,7 @@ EncryptionLevel QuicConnection::GetConnectionCloseEncryptionLevel() const { void QuicConnection::SendAllPendingAcks() { DCHECK(SupportsMultiplePacketNumberSpaces()); QUIC_DVLOG(1) << ENDPOINT << "Trying to send all pending ACKs"; + ack_alarm_->Cancel(); // Latches current encryption level. const EncryptionLevel current_encryption_level = encryption_level_; for (int8_t i = INITIAL_DATA; i <= APPLICATION_DATA; ++i) { @@ -3651,6 +3834,18 @@ QuicPacketNumber QuicConnection::GetLargestReceivedPacket() const { last_decrypted_packet_level_); } +bool QuicConnection::EnforceAntiAmplificationLimit() const { + return version().SupportsAntiAmplificationLimit() && + perspective_ == Perspective::IS_SERVER && !address_validated_; +} + +bool QuicConnection::LimitedByAmplificationFactor() const { + return EnforceAntiAmplificationLimit() && + bytes_sent_before_address_validation_ >= + GetQuicFlag(FLAGS_quic_anti_amplification_factor) * + bytes_received_before_address_validation_; +} + size_t QuicConnection::min_received_before_ack_decimation() const { return uber_received_packet_manager_.min_received_before_ack_decimation(); } diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_connection.h b/chromium/net/third_party/quiche/src/quic/core/quic_connection.h index 59a3656364d..6a5062aadbb 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_connection.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_connection.h @@ -134,10 +134,14 @@ class QUIC_EXPORT_PRIVATE QuicConnectionVisitorInterface { virtual void OnSuccessfulVersionNegotiation( const ParsedQuicVersion& version) = 0; - // Called when a connectivity probe has been received by the connection. - virtual void OnConnectivityProbeReceived( - const QuicSocketAddress& self_address, - const QuicSocketAddress& peer_address) = 0; + // Called when a packet has been received by the connection, after being + // validated and parsed. Only called when the client receives a valid packet + // or the server receives a connectivity probing packet. + // |is_connectivity_probe| is true if the received packet is a connectivity + // probe. + virtual void OnPacketReceived(const QuicSocketAddress& self_address, + const QuicSocketAddress& peer_address, + bool is_connectivity_probe) = 0; // Called when a blocked socket becomes writable. virtual void OnCanWrite() = 0; @@ -476,6 +480,9 @@ class QUIC_EXPORT_PRIVATE QuicConnection void OnDecryptedPacket(EncryptionLevel level) override; bool OnPacketHeader(const QuicPacketHeader& header) override; void OnCoalescedPacket(const QuicEncryptedPacket& packet) override; + void OnUndecryptablePacket(const QuicEncryptedPacket& packet, + EncryptionLevel decryption_level, + bool has_decryption_key) override; bool OnStreamFrame(const QuicStreamFrame& frame) override; bool OnCryptoFrame(const QuicCryptoFrame& frame) override; bool OnAckFrameStart(QuicPacketNumber largest_acked, @@ -526,9 +533,10 @@ class QUIC_EXPORT_PRIVATE QuicConnection // ack_frame(). const QuicFrame GetUpdatedAckFrame(); - // Called by the crypto stream when the handshake completes. In the server's - // case this is when the SHLO has been ACKed. Clients call this on receipt of - // the SHLO. + // Called when the handshake completes. On the client side, handshake + // completes on receipt of SHLO. On the server side, handshake completes when + // SHLO gets ACKed (or a forward secure packet gets decrypted successfully). + // TODO(fayang): Add a guard that this only gets called once. void OnHandshakeComplete(); // Accessors @@ -683,6 +691,10 @@ class QUIC_EXPORT_PRIVATE QuicConnection // Returns the underlying sent packet manager. QuicSentPacketManager& sent_packet_manager() { return sent_packet_manager_; } + UberReceivedPacketManager& received_packet_manager() { + return uber_received_packet_manager_; + } + bool CanWrite(HasRetransmittableData retransmittable); // When the flusher is out of scope, only the outermost flusher will cause a @@ -961,6 +973,8 @@ class QUIC_EXPORT_PRIVATE QuicConnection void TearDownLocalConnectionState(QuicErrorCode error, const std::string& details, ConnectionCloseSource source); + void TearDownLocalConnectionState(const QuicConnectionCloseFrame& frame, + ConnectionCloseSource source); // Writes the given packet to socket, encrypted with packet's // encryption_level. Returns true on successful write, and false if the writer @@ -980,7 +994,7 @@ class QUIC_EXPORT_PRIVATE QuicConnection const QuicStopWaitingFrame& stop_waiting); // Sends a version negotiation packet to the peer. - void SendVersionNegotiationPacket(bool ietf_quic); + void SendVersionNegotiationPacket(bool ietf_quic, bool has_length_prefix); // Clears any accumulated frames from the last received packet. void ClearLastFrames(); @@ -1120,6 +1134,12 @@ class QUIC_EXPORT_PRIVATE QuicConnection // Whether incoming_connection_ids_ contains connection_id. bool HasIncomingConnectionId(QuicConnectionId connection_id); + // Whether connection enforces anti-amplification limit. + bool EnforceAntiAmplificationLimit() const; + + // Whether connection is limited by amplification factor. + bool LimitedByAmplificationFactor() const; + QuicFramer framer_; // Contents received in the current packet, especially used to identify @@ -1217,6 +1237,7 @@ class QUIC_EXPORT_PRIVATE QuicConnection bool pending_version_negotiation_packet_; // Used when pending_version_negotiation_packet_ is true. bool send_ietf_version_negotiation_packet_; + bool send_version_negotiation_packet_with_prefixed_lengths_; // When packets could not be sent because the socket was not writable, // they are added to this list. All corresponding frames are in @@ -1424,6 +1445,24 @@ class QUIC_EXPORT_PRIVATE QuicConnection // Indicates whether a RETRY packet has been parsed. bool retry_has_been_parsed_; + + // If max_consecutive_ptos_ > 0, close connection if consecutive PTOs is + // greater than max_consecutive_ptos. + size_t max_consecutive_ptos_; + + // Bytes received before address validation. Only used when + // EnforceAntiAmplificationLimit returns true. + size_t bytes_received_before_address_validation_; + + // Bytes sent before address validation. Only used when + // EnforceAntiAmplificationLimit returns true. + size_t bytes_sent_before_address_validation_; + + // True if peer address has been validated. Address is considered validated + // when 1) an address token is received and validated, or 2) a HANDSHAKE + // packet has been successfully processed. Only used when + // EnforceAntiAmplificationLimit returns true. + bool address_validated_; }; } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_connection_id.cc b/chromium/net/third_party/quiche/src/quic/core/quic_connection_id.cc index c560bcca98f..67b5d1325c6 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_connection_id.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_connection_id.cc @@ -53,22 +53,18 @@ class QuicConnectionIdHasher { QuicConnectionId::QuicConnectionId() : QuicConnectionId(nullptr, 0) {} QuicConnectionId::QuicConnectionId(const char* data, uint8_t length) { - static_assert( - kQuicMaxConnectionIdLength <= std::numeric_limits<uint8_t>::max(), - "kQuicMaxConnectionIdLength too high"); - if (length > kQuicMaxConnectionIdLength) { - QUIC_BUG << "Attempted to create connection ID of length " << length; - length = kQuicMaxConnectionIdLength; + static_assert(kQuicMaxConnectionIdAllVersionsLength <= + std::numeric_limits<uint8_t>::max(), + "kQuicMaxConnectionIdAllVersionsLength too high"); + if (length > kQuicMaxConnectionIdAllVersionsLength) { + QUIC_BUG << "Attempted to create connection ID of length " + << static_cast<int>(length); + length = kQuicMaxConnectionIdAllVersionsLength; } length_ = length; if (length_ == 0) { return; } - if (!GetQuicRestartFlag(quic_use_allocated_connection_ids)) { - memcpy(data_, data, length_); - return; - } - QUIC_RESTART_FLAG_COUNT_N(quic_use_allocated_connection_ids, 1, 6); if (length_ <= sizeof(data_short_)) { memcpy(data_short_, data, length_); return; @@ -79,10 +75,6 @@ QuicConnectionId::QuicConnectionId(const char* data, uint8_t length) { } QuicConnectionId::~QuicConnectionId() { - if (!GetQuicRestartFlag(quic_use_allocated_connection_ids)) { - return; - } - QUIC_RESTART_FLAG_COUNT_N(quic_use_allocated_connection_ids, 2, 6); if (length_ > sizeof(data_short_)) { free(data_long_); data_long_ = nullptr; @@ -99,10 +91,6 @@ QuicConnectionId& QuicConnectionId::operator=(const QuicConnectionId& other) { } const char* QuicConnectionId::data() const { - if (!GetQuicRestartFlag(quic_use_allocated_connection_ids)) { - return data_; - } - QUIC_RESTART_FLAG_COUNT_N(quic_use_allocated_connection_ids, 3, 6); if (length_ <= sizeof(data_short_)) { return data_short_; } @@ -110,10 +98,6 @@ const char* QuicConnectionId::data() const { } char* QuicConnectionId::mutable_data() { - if (!GetQuicRestartFlag(quic_use_allocated_connection_ids)) { - return data_; - } - QUIC_RESTART_FLAG_COUNT_N(quic_use_allocated_connection_ids, 4, 6); if (length_ <= sizeof(data_short_)) { return data_short_; } @@ -125,30 +109,32 @@ uint8_t QuicConnectionId::length() const { } void QuicConnectionId::set_length(uint8_t length) { - if (GetQuicRestartFlag(quic_use_allocated_connection_ids)) { - QUIC_RESTART_FLAG_COUNT_N(quic_use_allocated_connection_ids, 5, 6); - char temporary_data[sizeof(data_short_)]; - if (length > sizeof(data_short_)) { - if (length_ <= sizeof(data_short_)) { - // Copy data from data_short_ to data_long_. - memcpy(temporary_data, data_short_, length_); - data_long_ = reinterpret_cast<char*>(malloc(length)); - CHECK_NE(nullptr, data_long_); - memcpy(data_long_, temporary_data, length_); - } else { - // Resize data_long_. - char* realloc_result = - reinterpret_cast<char*>(realloc(data_long_, length)); - CHECK_NE(nullptr, realloc_result); - data_long_ = realloc_result; - } - } else if (length_ > sizeof(data_short_)) { - // Copy data from data_long_ to data_short_. - memcpy(temporary_data, data_long_, length); - free(data_long_); - data_long_ = nullptr; - memcpy(data_short_, temporary_data, length); + if (length > kQuicMaxConnectionIdAllVersionsLength) { + QUIC_BUG << "Attempted to set connection ID length to " + << static_cast<int>(length); + length = kQuicMaxConnectionIdAllVersionsLength; + } + char temporary_data[sizeof(data_short_)]; + if (length > sizeof(data_short_)) { + if (length_ <= sizeof(data_short_)) { + // Copy data from data_short_ to data_long_. + memcpy(temporary_data, data_short_, length_); + data_long_ = reinterpret_cast<char*>(malloc(length)); + CHECK_NE(nullptr, data_long_); + memcpy(data_long_, temporary_data, length_); + } else { + // Resize data_long_. + char* realloc_result = + reinterpret_cast<char*>(realloc(data_long_, length)); + CHECK_NE(nullptr, realloc_result); + data_long_ = realloc_result; } + } else if (length_ > sizeof(data_short_)) { + // Copy data from data_long_ to data_short_. + memcpy(temporary_data, data_long_, length); + free(data_long_); + data_long_ = nullptr; + memcpy(data_short_, temporary_data, length); } length_ = length; } @@ -160,8 +146,8 @@ bool QuicConnectionId::IsEmpty() const { size_t QuicConnectionId::Hash() const { if (!GetQuicRestartFlag(quic_connection_id_use_siphash)) { uint64_t data_bytes[3] = {0, 0, 0}; - static_assert(sizeof(data_bytes) >= kQuicMaxConnectionIdLength, - "kQuicMaxConnectionIdLength changed"); + static_assert(sizeof(data_bytes) >= kQuicMaxConnectionIdAllVersionsLength, + "kQuicMaxConnectionIdAllVersionsLength changed"); memcpy(data_bytes, data(), length_); // This Hash function is designed to return the same value as the host byte // order representation when the connection ID length is 64 bits. diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_connection_id.h b/chromium/net/third_party/quiche/src/quic/core/quic_connection_id.h index 6b1b0bc5ff5..431cc741f60 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_connection_id.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_connection_id.h @@ -25,8 +25,16 @@ enum QuicConnectionIdIncluded : uint8_t { CONNECTION_ID_ABSENT = 2, }; -// Connection IDs can be 0-18 bytes per IETF specifications. -const uint8_t kQuicMaxConnectionIdLength = 18; +// Maximum connection ID length that we support in any packet or version. +const uint8_t kQuicMaxConnectionIdAllVersionsLength = 20; + +// Maximum connection ID length supported by versions that use the encoding from +// draft-ietf-quic-invariants-06. +const uint8_t kQuicMaxConnectionIdWithLengthPrefixLength = 20; + +// Maximum connection ID length supported by versions that use the encoding from +// draft-ietf-quic-invariants-05. +const uint8_t kQuicMaxConnectionId4BitLength = 18; // kQuicDefaultConnectionIdLength is the only supported length for QUIC // versions < v99, and is the default picked for all versions. @@ -97,15 +105,12 @@ class QUIC_EXPORT_PRIVATE QuicConnectionId { uint8_t length_; // length of the connection ID, in bytes. // The connection ID is represented in network byte order. union { - // When quic_use_allocated_connection_ids is false, the connection ID is - // stored in the first |length_| bytes of |data_|. - char data_[kQuicMaxConnectionIdLength]; - // When quic_use_allocated_connection_ids is true, if the connection ID - // fits in |data_short_|, it is stored in the first |length_| bytes of - // |data_short_|. Otherwise it is stored in |data_long_| which is - // guaranteed to have a size equal to |length_|. A value of 11 was chosen - // because our commonly used connection ID length is 8 and with the length, - // the class is padded to 12 bytes anyway. + // If the connection ID fits in |data_short_|, it is stored in the + // first |length_| bytes of |data_short_|. + // Otherwise it is stored in |data_long_| which is guaranteed to have a size + // equal to |length_|. + // A value of 11 was chosen because our commonly used connection ID length + // is 8 and with the length, the class is padded to 12 bytes anyway. char data_short_[11]; char* data_long_; }; diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_connection_id_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_connection_id_test.cc index 0d4b190dbd8..d80babbfa66 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_connection_id_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_connection_id_test.cc @@ -98,10 +98,10 @@ TEST_F(QuicConnectionIdTest, Hash) { // 32bit platforms. return; } - const char connection_id_bytes[kQuicMaxConnectionIdLength] = {}; - for (uint8_t i = 0; i < kQuicMaxConnectionIdLength - 1; ++i) { + const char connection_id_bytes[kQuicMaxConnectionIdAllVersionsLength] = {}; + for (uint8_t i = 0; i < sizeof(connection_id_bytes) - 1; ++i) { QuicConnectionId connection_id_i(connection_id_bytes, i); - for (uint8_t j = i + 1; j < kQuicMaxConnectionIdLength; ++j) { + for (uint8_t j = i + 1; j < sizeof(connection_id_bytes); ++j) { QuicConnectionId connection_id_j(connection_id_bytes, j); EXPECT_NE(connection_id_i.Hash(), connection_id_j.Hash()); } diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_connection_stats.cc b/chromium/net/third_party/quiche/src/quic/core/quic_connection_stats.cc index 246b9f1200c..98859acce32 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_connection_stats.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_connection_stats.cc @@ -34,6 +34,7 @@ QuicConnectionStats::QuicConnectionStats() loss_timeout_count(0), tlp_count(0), rto_count(0), + pto_count(0), min_rtt_us(0), srtt_us(0), max_packet_size(0), @@ -77,6 +78,7 @@ std::ostream& operator<<(std::ostream& os, const QuicConnectionStats& s) { os << " loss_timeout_count: " << s.loss_timeout_count; os << " tlp_count: " << s.tlp_count; os << " rto_count: " << s.rto_count; + os << " pto_count: " << s.pto_count; os << " min_rtt_us: " << s.min_rtt_us; os << " srtt_us: " << s.srtt_us; os << " max_packet_size: " << s.max_packet_size; diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_connection_stats.h b/chromium/net/third_party/quiche/src/quic/core/quic_connection_stats.h index 5317c7a6180..56bb510cda5 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_connection_stats.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_connection_stats.h @@ -76,6 +76,7 @@ struct QUIC_EXPORT_PRIVATE QuicConnectionStats { size_t loss_timeout_count; size_t tlp_count; size_t rto_count; // Count of times the rto timer fired. + size_t pto_count; int64_t min_rtt_us; // Minimum RTT in microseconds. int64_t srtt_us; // Smoothed RTT in microseconds. diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_connection_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_connection_test.cc index ee7b6511193..ec096866656 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_connection_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_connection_test.cc @@ -341,7 +341,10 @@ class TestPacketWriter : public QuicPacketWriter { clock_(clock), write_pause_time_delta_(QuicTime::Delta::Zero()), max_packet_size_(kMaxOutgoingPacketSize), - supports_release_time_(false) {} + supports_release_time_(false) { + QuicFramerPeer::SetLastSerializedServerConnectionId(framer_.framer(), + TestConnectionId()); + } TestPacketWriter(const TestPacketWriter&) = delete; TestPacketWriter& operator=(const TestPacketWriter&) = delete; @@ -664,6 +667,12 @@ class TestConnection : public QuicConnection { if (!QuicUtils::IsCryptoStreamId(transport_version(), id) && this->encryption_level() == ENCRYPTION_INITIAL) { this->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); + if (perspective() == Perspective::IS_CLIENT && !IsHandshakeConfirmed()) { + OnHandshakeComplete(); + } + if (version().SupportsAntiAmplificationLimit()) { + QuicConnectionPeer::SetAddressValidated(this); + } } struct iovec iov; MakeIOVector(data, &iov); @@ -820,6 +829,16 @@ class TestConnection : public QuicConnection { next_effective_peer_addr_ = QuicMakeUnique<QuicSocketAddress>(addr); } + bool PtoEnabled() { + if (QuicConnectionPeer::GetSentPacketManager(this)->pto_enabled()) { + // PTO mode is default enabled for T099. And TLP/RTO related tests are + // stale. + DCHECK_EQ(ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_99), version()); + return true; + } + return false; + } + SimpleDataProducer* producer() { return &producer_; } using QuicConnection::active_effective_peer_migration_type; @@ -1010,7 +1029,7 @@ class QuicConnectionTest : public QuicTestWithParam<TestParams> { EXPECT_CALL(visitor_, ShouldKeepConnectionAlive()) .WillRepeatedly(Return(false)); EXPECT_CALL(visitor_, OnCongestionWindowChange(_)).Times(AnyNumber()); - EXPECT_CALL(visitor_, OnConnectivityProbeReceived(_, _)).Times(AnyNumber()); + EXPECT_CALL(visitor_, OnPacketReceived(_, _, _)).Times(AnyNumber()); EXPECT_CALL(visitor_, OnForwardProgressConfirmed()).Times(AnyNumber()); EXPECT_CALL(*loss_algorithm_, GetLossTimeout()) @@ -1133,9 +1152,7 @@ class QuicConnectionTest : public QuicTestWithParam<TestParams> { header.destination_connection_id = connection_id_; header.packet_number_length = packet_number_length_; header.destination_connection_id_included = connection_id_included_; - if ((VersionHasIetfInvariantHeader(peer_framer_.transport_version()) || - GetQuicRestartFlag(quic_do_not_override_connection_id)) && - peer_framer_.perspective() == Perspective::IS_SERVER) { + if (peer_framer_.perspective() == Perspective::IS_SERVER) { header.destination_connection_id_included = CONNECTION_ID_ABSENT; } if (level == ENCRYPTION_INITIAL && @@ -1146,10 +1163,7 @@ class QuicConnectionTest : public QuicTestWithParam<TestParams> { header.length_length = VARIABLE_LENGTH_INTEGER_LENGTH_2; } } - if ((GetQuicRestartFlag(quic_do_not_override_connection_id) || - (level == ENCRYPTION_INITIAL && - peer_framer_.version().KnowsWhichDecrypterToUse())) && - header.version_flag && + if (header.version_flag && peer_framer_.perspective() == Perspective::IS_SERVER) { header.source_connection_id = connection_id_; header.source_connection_id_included = CONNECTION_ID_PRESENT; @@ -1217,6 +1231,7 @@ class QuicConnectionTest : public QuicTestWithParam<TestParams> { } else { frames.push_back(QuicFrame(frame1_)); } + frames.push_back(QuicFrame(QuicPaddingFrame(-1))); std::unique_ptr<QuicPacket> packet = ConstructPacket(header, frames); char buffer[kMaxOutgoingPacketSize]; peer_creator_.set_encryption_level(ENCRYPTION_INITIAL); @@ -1268,8 +1283,10 @@ class QuicConnectionTest : public QuicTestWithParam<TestParams> { StreamSendingState state, QuicPacketNumber* last_packet) { QuicByteCount packet_size; + // Save the last packet's size. EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)) - .WillOnce(SaveArg<3>(&packet_size)); + .Times(AnyNumber()) + .WillRepeatedly(SaveArg<3>(&packet_size)); connection_.SendStreamDataWithString(id, data, offset, state); if (last_packet != nullptr) { *last_packet = creator_->packet_number(); @@ -1371,8 +1388,7 @@ class QuicConnectionTest : public QuicTestWithParam<TestParams> { } // Set connection_id to peer's in memory representation as this data packet // is created by peer_framer. - if (GetQuicRestartFlag(quic_do_not_override_connection_id) && - peer_framer_.perspective() == Perspective::IS_SERVER) { + if (peer_framer_.perspective() == Perspective::IS_SERVER) { header.source_connection_id = connection_id_; header.source_connection_id_included = connection_id_included_; header.destination_connection_id_included = CONNECTION_ID_ABSENT; @@ -1425,8 +1441,7 @@ class QuicConnectionTest : public QuicTestWithParam<TestParams> { QuicPacketHeader header; // Set connection_id to peer's in memory representation as this connection // close packet is created by peer_framer. - if (GetQuicRestartFlag(quic_do_not_override_connection_id) && - peer_framer_.perspective() == Perspective::IS_SERVER) { + if (peer_framer_.perspective() == Perspective::IS_SERVER) { header.source_connection_id = connection_id_; header.destination_connection_id_included = CONNECTION_ID_ABSENT; if (!VersionHasIetfInvariantHeader(peer_framer_.transport_version())) { @@ -1441,11 +1456,26 @@ class QuicConnectionTest : public QuicTestWithParam<TestParams> { header.packet_number = QuicPacketNumber(number); - QuicConnectionCloseFrame qccf(QUIC_PEER_GOING_AWAY, ""); + QuicErrorCode kQuicErrorCode = QUIC_PEER_GOING_AWAY; + // This QuicConnectionCloseFrame will default to being for a Google QUIC + // close. If doing IETF QUIC then set fields appropriately for CC/T or CC/A, + // depending on the mapping. + QuicConnectionCloseFrame qccf(kQuicErrorCode, ""); if (VersionHasIetfQuicFrames(peer_framer_.transport_version())) { - // Default close-type is Google QUIC. If doing IETF QUIC then - // set close type to be IETF CC/T. - qccf.close_type = IETF_QUIC_TRANSPORT_CONNECTION_CLOSE; + QuicErrorCodeToIetfMapping mapping = + QuicErrorCodeToTransportErrorCode(kQuicErrorCode); + if (mapping.is_transport_close_) { + // Maps to a transport close + qccf.close_type = IETF_QUIC_TRANSPORT_CONNECTION_CLOSE; + qccf.transport_error_code = mapping.transport_error_code_; + // Frame type is not important for the tests that invoke this method. + qccf.transport_close_frame_type = 0; + } else { + // Maps to an application close. + qccf.close_type = IETF_QUIC_APPLICATION_CONNECTION_CLOSE; + qccf.application_error_code = mapping.application_error_code_; + } + qccf.extracted_error_code = kQuicErrorCode; } QuicFrames frames; @@ -1560,7 +1590,32 @@ class QuicConnectionTest : public QuicTestWithParam<TestParams> { const std::vector<QuicConnectionCloseFrame>& connection_close_frames = writer_->connection_close_frames(); ASSERT_EQ(1u, connection_close_frames.size()); - EXPECT_EQ(expected_code, connection_close_frames[0].quic_error_code); + if (!VersionHasIetfQuicFrames(version().transport_version)) { + EXPECT_EQ(expected_code, connection_close_frames[0].quic_error_code); + EXPECT_EQ(GOOGLE_QUIC_CONNECTION_CLOSE, + connection_close_frames[0].close_type); + return; + } + + QuicErrorCodeToIetfMapping mapping = + QuicErrorCodeToTransportErrorCode(expected_code); + + if (mapping.is_transport_close_) { + // This Google QUIC Error Code maps to a transport close, + EXPECT_EQ(IETF_QUIC_TRANSPORT_CONNECTION_CLOSE, + connection_close_frames[0].close_type); + EXPECT_EQ(mapping.transport_error_code_, + connection_close_frames[0].transport_error_code); + // TODO(fkastenholz): when the extracted error code CL lands, + // need to test that extracted==expected. + } else { + // This maps to an application close. + EXPECT_EQ(expected_code, connection_close_frames[0].quic_error_code); + EXPECT_EQ(IETF_QUIC_APPLICATION_CONNECTION_CLOSE, + connection_close_frames[0].close_type); + // TODO(fkastenholz): when the extracted error code CL lands, + // need to test that extracted==expected. + } } QuicConnectionId connection_id_; @@ -1601,6 +1656,36 @@ INSTANTIATE_TEST_SUITE_P(SupportedVersion, QuicConnectionTest, ::testing::ValuesIn(GetTestParams())); +// These two tests ensure that the QuicErrorCode mapping works correctly. +// Both tests expect to see a Google QUIC close if not running IETF QUIC. +// If running IETF QUIC, the first will generate a transport connection +// close, the second an application connection close. +// The connection close codes for the two tests are manually chosen; +// they are expected to always map to transport- and application- +// closes, respectively. If that changes, mew codes should be chosen. +TEST_P(QuicConnectionTest, CloseErrorCodeTestTransport) { + EXPECT_TRUE(connection_.connected()); + EXPECT_CALL(visitor_, OnConnectionClosed(_, _)); + connection_.CloseConnection( + IETF_QUIC_PROTOCOL_VIOLATION, "Should be transport close", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + EXPECT_FALSE(connection_.connected()); + TestConnectionCloseQuicErrorCode(IETF_QUIC_PROTOCOL_VIOLATION); +} + +// Test that the IETF QUIC Error code mapping function works +// properly for application connection close codes. +TEST_P(QuicConnectionTest, CloseErrorCodeTestApplication) { + EXPECT_TRUE(connection_.connected()); + EXPECT_CALL(visitor_, OnConnectionClosed(_, _)); + connection_.CloseConnection( + QUIC_HEADERS_STREAM_DATA_DECOMPRESS_FAILURE, + "Should be application close", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + EXPECT_FALSE(connection_.connected()); + TestConnectionCloseQuicErrorCode(QUIC_HEADERS_STREAM_DATA_DECOMPRESS_FAILURE); +} + TEST_P(QuicConnectionTest, SelfAddressChangeAtClient) { EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); @@ -1889,7 +1974,7 @@ TEST_P(QuicConnectionTest, ReceivePaddedPingAtServer) { EXPECT_EQ(kPeerAddress, connection_.effective_peer_address()); EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(0); - EXPECT_CALL(visitor_, OnConnectivityProbeReceived(_, _)).Times(0); + EXPECT_CALL(visitor_, OnPacketReceived(_, _, false)).Times(0); // Process a padded PING or PATH CHALLENGE packet with no peer address change // on server side will be ignored. @@ -2004,7 +2089,7 @@ TEST_P(QuicConnectionTest, ReceiveConnectivityProbingAtServer) { EXPECT_EQ(kPeerAddress, connection_.effective_peer_address()); EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(0); - EXPECT_CALL(visitor_, OnConnectivityProbeReceived(_, _)).Times(1); + EXPECT_CALL(visitor_, OnPacketReceived(_, _, true)).Times(1); // Process a padded PING packet from a new peer address on server side // is effectively receiving a connectivity probing. @@ -2067,7 +2152,7 @@ TEST_P(QuicConnectionTest, ReceiveReorderedConnectivityProbingAtServer) { QuicPacketCreatorPeer::SetPacketNumber(&peer_creator_, 4); EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(0); - EXPECT_CALL(visitor_, OnConnectivityProbeReceived(_, _)).Times(1); + EXPECT_CALL(visitor_, OnPacketReceived(_, _, true)).Times(1); // Process a padded PING packet from a new peer address on server side // is effectively receiving a connectivity probing, even if a newer packet has @@ -2120,7 +2205,7 @@ TEST_P(QuicConnectionTest, MigrateAfterProbingAtServer) { EXPECT_EQ(kPeerAddress, connection_.effective_peer_address()); EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(0); - EXPECT_CALL(visitor_, OnConnectivityProbeReceived(_, _)).Times(1); + EXPECT_CALL(visitor_, OnPacketReceived(_, _, true)).Times(1); // Process a padded PING packet from a new peer address on server side // is effectively receiving a connectivity probing. @@ -2175,7 +2260,7 @@ TEST_P(QuicConnectionTest, ReceivePaddedPingAtClient) { // Client takes all padded PING packet as speculative connectivity // probing packet, and reports to visitor. EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(0); - EXPECT_CALL(visitor_, OnConnectivityProbeReceived(_, _)).Times(1); + EXPECT_CALL(visitor_, OnPacketReceived(_, _, false)).Times(1); OwningSerializedPacketPointer probing_packet = ConstructProbingPacket(); std::unique_ptr<QuicReceivedPacket> received(ConstructReceivedPacket( @@ -2222,7 +2307,7 @@ TEST_P(QuicConnectionTest, ReceiveConnectivityProbingAtClient) { // Process a padded PING packet with a different self address on client side // is effectively receiving a connectivity probing. EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(0); - EXPECT_CALL(visitor_, OnConnectivityProbeReceived(_, _)).Times(1); + EXPECT_CALL(visitor_, OnPacketReceived(_, _, true)).Times(1); const QuicSocketAddress kNewSelfAddress = QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/23456); @@ -3380,7 +3465,12 @@ TEST_P(QuicConnectionTest, CancelRetransmissionAlarmAfterResetStream) { // Ensure that the data is still in flight, but the retransmission alarm is no // longer set. EXPECT_GT(manager_->GetBytesInFlight(), 0u); - EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet()); + if (QuicConnectionPeer::GetSentPacketManager(&connection_) + ->fix_rto_retransmission()) { + EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet()); + } else { + EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet()); + } } TEST_P(QuicConnectionTest, RetransmitForQuicRstStreamNoErrorOnRTO) { @@ -3543,6 +3633,9 @@ TEST_P(QuicConnectionTest, RetransmitNackedLargestObserved) { } TEST_P(QuicConnectionTest, QueueAfterTwoRTOs) { + if (connection_.PtoEnabled()) { + return; + } connection_.SetMaxTailLossProbes(0); for (int i = 0; i < 10; ++i) { @@ -3623,7 +3716,12 @@ TEST_P(QuicConnectionTest, RetransmitWriteBlockedAckedOriginalThenSent) { writer_->SetWritable(); connection_.OnCanWrite(); - EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet()); + if (QuicConnectionPeer::GetSentPacketManager(&connection_) + ->fix_rto_retransmission()) { + EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet()); + } else { + EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet()); + } EXPECT_FALSE(QuicConnectionPeer::HasRetransmittableFrames(&connection_, 2)); } @@ -3875,7 +3973,8 @@ TEST_P(QuicConnectionTest, TLP) { } TEST_P(QuicConnectionTest, TailLossProbeDelayForStreamDataInTLPR) { - if (!connection_.session_decides_what_to_write()) { + if (!connection_.session_decides_what_to_write() || + connection_.PtoEnabled()) { return; } @@ -3910,7 +4009,8 @@ TEST_P(QuicConnectionTest, TailLossProbeDelayForStreamDataInTLPR) { } TEST_P(QuicConnectionTest, TailLossProbeDelayForNonStreamDataInTLPR) { - if (!connection_.session_decides_what_to_write()) { + if (!connection_.session_decides_what_to_write() || + connection_.PtoEnabled()) { return; } @@ -4051,6 +4151,9 @@ TEST_P(QuicConnectionTest, TailLossProbeDelayForNonStreamDataInTLPR) { } TEST_P(QuicConnectionTest, RTO) { + if (connection_.PtoEnabled()) { + return; + } connection_.SetMaxTailLossProbes(0); QuicTime default_retransmission_time = @@ -4072,7 +4175,8 @@ TEST_P(QuicConnectionTest, RTO) { // Regression test of b/133771183. TEST_P(QuicConnectionTest, RtoWithNoDataToRetransmit) { - if (!connection_.session_decides_what_to_write()) { + if (!connection_.session_decides_what_to_write() || + connection_.PtoEnabled()) { return; } connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); @@ -4087,25 +4191,25 @@ TEST_P(QuicConnectionTest, RtoWithNoDataToRetransmit) { // Simulate the retransmission alarm firing. clock_.AdvanceTime(DefaultRetransmissionTime()); // RTO fires, but there is no packet to be RTOed. - if (GetQuicReloadableFlag(quic_fix_rto_retransmission)) { + if (GetQuicReloadableFlag(quic_fix_rto_retransmission3)) { EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); } else { EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0); } connection_.GetRetransmissionAlarm()->Fire(); - if (GetQuicReloadableFlag(quic_fix_rto_retransmission)) { + if (GetQuicReloadableFlag(quic_fix_rto_retransmission3)) { EXPECT_EQ(1u, writer_->rst_stream_frames().size()); } EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(40); EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(20); - if (GetQuicReloadableFlag(quic_fix_rto_retransmission)) { + if (GetQuicReloadableFlag(quic_fix_rto_retransmission3)) { EXPECT_CALL(visitor_, WillingAndAbleToWrite()) .WillRepeatedly(Return(false)); } else { EXPECT_CALL(visitor_, WillingAndAbleToWrite()).WillRepeatedly(Return(true)); } - if (GetQuicReloadableFlag(quic_fix_rto_retransmission)) { + if (GetQuicReloadableFlag(quic_fix_rto_retransmission3)) { EXPECT_CALL(visitor_, OnAckNeedsRetransmittableFrame()).Times(1); } else { // Since there is a buffered RST_STREAM, no retransmittable frame is bundled @@ -4266,6 +4370,9 @@ TEST_P(QuicConnectionTest, BufferNonDecryptablePackets) { } TEST_P(QuicConnectionTest, TestRetransmitOrder) { + if (connection_.PtoEnabled()) { + return; + } connection_.SetMaxTailLossProbes(0); QuicByteCount first_packet_size; @@ -4351,6 +4458,9 @@ TEST_P(QuicConnectionTest, SetRTOAfterWritingToSocket) { } TEST_P(QuicConnectionTest, DelayRTOWithAckReceipt) { + if (connection_.PtoEnabled()) { + return; + } connection_.SetMaxTailLossProbes(0); EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); @@ -5054,6 +5164,9 @@ TEST_P(QuicConnectionTest, TimeoutAfterSend) { } TEST_P(QuicConnectionTest, TimeoutAfterRetransmission) { + if (connection_.PtoEnabled()) { + return; + } EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); EXPECT_TRUE(connection_.connected()); EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); @@ -5204,6 +5317,9 @@ TEST_P(QuicConnectionTest, NewTimeoutAfterSendSilentClose) { } TEST_P(QuicConnectionTest, TimeoutAfterSendSilentCloseAndTLP) { + if (connection_.PtoEnabled()) { + return; + } // Same test as above, but complete a handshake which enables silent close, // but sending TLPs causes the connection close to be sent. EXPECT_TRUE(connection_.connected()); @@ -5430,6 +5546,9 @@ TEST_P(QuicConnectionTest, TimeoutAfterReceiveNotSendWhenUnacked) { } TEST_P(QuicConnectionTest, TimeoutAfter5ClientRTOs) { + if (connection_.PtoEnabled()) { + return; + } connection_.SetMaxTailLossProbes(2); EXPECT_TRUE(connection_.connected()); EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); @@ -6774,16 +6893,20 @@ TEST_P(QuicConnectionTest, MissingPacketsBeforeLeastUnacked) { } TEST_P(QuicConnectionTest, ClientHandlesVersionNegotiation) { - // Start out with an unsupported version. - QuicConnectionPeer::GetFramer(&connection_) - ->set_version_for_tests(QuicVersionReservedForNegotiation()); + // All supported versions except the one the connection supports. + ParsedQuicVersionVector versions; + for (auto version : AllSupportedVersions()) { + if (version != connection_.version()) { + versions.push_back(version); + } + } // Send a version negotiation packet. std::unique_ptr<QuicEncryptedPacket> encrypted( QuicFramer::BuildVersionNegotiationPacket( connection_id_, EmptyQuicConnectionId(), VersionHasIetfInvariantHeader(connection_.transport_version()), - AllSupportedVersions())); + connection_.version().HasLengthPrefixedConnectionIds(), versions)); std::unique_ptr<QuicReceivedPacket> received( ConstructReceivedPacket(*encrypted, QuicTime::Zero())); EXPECT_CALL(visitor_, OnConnectionClosed(_, ConnectionCloseSource::FROM_SELF)) @@ -6804,6 +6927,7 @@ TEST_P(QuicConnectionTest, BadVersionNegotiation) { QuicFramer::BuildVersionNegotiationPacket( connection_id_, EmptyQuicConnectionId(), VersionHasIetfInvariantHeader(connection_.transport_version()), + connection_.version().HasLengthPrefixedConnectionIds(), AllSupportedVersions())); std::unique_ptr<QuicReceivedPacket> received( ConstructReceivedPacket(*encrypted, QuicTime::Zero())); @@ -6814,6 +6938,9 @@ TEST_P(QuicConnectionTest, BadVersionNegotiation) { } TEST_P(QuicConnectionTest, CheckSendStats) { + if (connection_.PtoEnabled()) { + return; + } connection_.SetMaxTailLossProbes(0); EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)); @@ -6874,8 +7001,7 @@ TEST_P(QuicConnectionTest, CheckSendStats) { TEST_P(QuicConnectionTest, ProcessFramesIfPacketClosedConnection) { // Construct a packet with stream frame and connection close frame. QuicPacketHeader header; - if (GetQuicRestartFlag(quic_do_not_override_connection_id) && - peer_framer_.perspective() == Perspective::IS_SERVER) { + if (peer_framer_.perspective() == Perspective::IS_SERVER) { header.source_connection_id = connection_id_; header.destination_connection_id_included = CONNECTION_ID_ABSENT; if (!VersionHasIetfInvariantHeader(peer_framer_.transport_version())) { @@ -6890,11 +7016,27 @@ TEST_P(QuicConnectionTest, ProcessFramesIfPacketClosedConnection) { header.packet_number = QuicPacketNumber(1); header.version_flag = false; - QuicConnectionCloseFrame qccf(QUIC_PEER_GOING_AWAY, ""); + QuicErrorCode kQuicErrorCode = QUIC_PEER_GOING_AWAY; + // This QuicConnectionCloseFrame will default to being for a Google QUIC + // close. If doing IETF QUIC then set fields appropriately for CC/T or CC/A, + // depending on the mapping. + QuicConnectionCloseFrame qccf(kQuicErrorCode, ""); if (VersionHasIetfQuicFrames(peer_framer_.transport_version())) { - // Default close-type is Google QUIC. If doing IETF QUIC then - // set close type to be IETF CC/T. - qccf.close_type = IETF_QUIC_TRANSPORT_CONNECTION_CLOSE; + QuicErrorCodeToIetfMapping mapping = + QuicErrorCodeToTransportErrorCode(kQuicErrorCode); + if (mapping.is_transport_close_) { + // Maps to a transport close + qccf.close_type = IETF_QUIC_TRANSPORT_CONNECTION_CLOSE; + qccf.transport_error_code = mapping.transport_error_code_; + // TODO(fkastenholz) need to change "0" to get the frame type currently + // being processed so that it can be inserted into the frame. + qccf.transport_close_frame_type = 0; + } else { + // Maps to an application close. + qccf.close_type = IETF_QUIC_APPLICATION_CONNECTION_CLOSE; + qccf.application_error_code = mapping.application_error_code_; + } + // qccf.extracted_error_code = kQuicErrorCode; } QuicFrames frames; @@ -6917,7 +7059,7 @@ TEST_P(QuicConnectionTest, ProcessFramesIfPacketClosedConnection) { QuicReceivedPacket(buffer, encrypted_length, QuicTime::Zero(), false)); EXPECT_EQ(1, connection_close_frame_count_); EXPECT_EQ(QUIC_PEER_GOING_AWAY, - saved_connection_close_frame_.quic_error_code); + saved_connection_close_frame_.extracted_error_code); } TEST_P(QuicConnectionTest, SelectMutualVersion) { @@ -7109,6 +7251,7 @@ TEST_P(QuicConnectionTest, SendPingImmediately) { connection_.set_debug_visitor(&debug_visitor); CongestionBlockWrites(); + connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); EXPECT_CALL(debug_visitor, OnPacketSent(_, _, _, _)).Times(1); EXPECT_CALL(debug_visitor, OnPingSent()).Times(1); @@ -7120,6 +7263,7 @@ TEST_P(QuicConnectionTest, SendBlockedImmediately) { MockQuicConnectionDebugVisitor debug_visitor; connection_.set_debug_visitor(&debug_visitor); + connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); EXPECT_CALL(debug_visitor, OnPacketSent(_, _, _, _)).Times(1); EXPECT_EQ(0u, connection_.GetStats().blocked_frames_sent); @@ -7128,6 +7272,22 @@ TEST_P(QuicConnectionTest, SendBlockedImmediately) { EXPECT_FALSE(connection_.HasQueuedData()); } +TEST_P(QuicConnectionTest, FailedToSendBlockedFrames) { + if (!connection_.SupportsMultiplePacketNumberSpaces()) { + return; + } + MockQuicConnectionDebugVisitor debug_visitor; + connection_.set_debug_visitor(&debug_visitor); + QuicBlockedFrame blocked(1, 3); + + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0); + EXPECT_CALL(debug_visitor, OnPacketSent(_, _, _, _)).Times(0); + EXPECT_EQ(0u, connection_.GetStats().blocked_frames_sent); + connection_.SendControlFrame(QuicFrame(&blocked)); + EXPECT_EQ(0u, connection_.GetStats().blocked_frames_sent); + EXPECT_FALSE(connection_.HasQueuedData()); +} + TEST_P(QuicConnectionTest, SendingUnencryptedStreamDataFails) { // EXPECT_QUIC_BUG tests are expensive so only run one instance of them. if (!IsDefaultTestConfiguration()) { @@ -8443,7 +8603,6 @@ TEST_P(QuicConnectionTest, CancelAckAlarmOnWriteBlocked) { // Make sure a packet received with the right client connection ID is processed. TEST_P(QuicConnectionTest, ValidClientConnectionId) { - SetQuicRestartFlag(quic_do_not_override_connection_id, true); if (!framer_.version().SupportsClientConnectionIds()) { return; } @@ -8473,7 +8632,6 @@ TEST_P(QuicConnectionTest, ValidClientConnectionId) { // Make sure a packet received with a different client connection ID is dropped. TEST_P(QuicConnectionTest, InvalidClientConnectionId) { - SetQuicRestartFlag(quic_do_not_override_connection_id, true); if (!framer_.version().SupportsClientConnectionIds()) { return; } @@ -8503,7 +8661,6 @@ TEST_P(QuicConnectionTest, InvalidClientConnectionId) { // Make sure the first packet received with a different client connection ID on // the server is processed and it changes the client connection ID. TEST_P(QuicConnectionTest, UpdateClientConnectionIdFromFirstPacket) { - SetQuicRestartFlag(quic_do_not_override_connection_id, true); if (!framer_.version().SupportsClientConnectionIds()) { return; } @@ -8565,6 +8722,488 @@ TEST_P(QuicConnectionTest, CheckConnectedBeforeFlush) { EXPECT_FALSE(ack_alarm->IsSet()); } +// Verify that a packet containing three coalesced packets is parsed correctly. +TEST_P(QuicConnectionTest, CoalescedPacket) { + if (!QuicVersionHasLongHeaderLengths(connection_.transport_version())) { + // Coalesced packets can only be encoded using long header lengths. + return; + } + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_TRUE(connection_.connected()); + if (QuicVersionUsesCryptoFrames(connection_.transport_version())) { + EXPECT_CALL(visitor_, OnCryptoFrame(_)).Times(3); + } else { + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(3); + } + + uint64_t packet_numbers[3] = {1, 2, 3}; + EncryptionLevel encryption_levels[3] = { + ENCRYPTION_INITIAL, ENCRYPTION_INITIAL, ENCRYPTION_FORWARD_SECURE}; + char buffer[kMaxOutgoingPacketSize] = {}; + size_t total_encrypted_length = 0; + for (int i = 0; i < 3; i++) { + QuicPacketHeader header = + ConstructPacketHeader(packet_numbers[i], encryption_levels[i]); + QuicFrames frames; + if (QuicVersionUsesCryptoFrames(connection_.transport_version())) { + frames.push_back(QuicFrame(&crypto_frame_)); + } else { + frames.push_back(QuicFrame(frame1_)); + } + std::unique_ptr<QuicPacket> packet = ConstructPacket(header, frames); + peer_creator_.set_encryption_level(encryption_levels[i]); + size_t encrypted_length = peer_framer_.EncryptPayload( + encryption_levels[i], QuicPacketNumber(packet_numbers[i]), *packet, + buffer + total_encrypted_length, + sizeof(buffer) - total_encrypted_length); + EXPECT_GT(encrypted_length, 0u); + total_encrypted_length += encrypted_length; + } + connection_.ProcessUdpPacket( + kSelfAddress, kPeerAddress, + QuicReceivedPacket(buffer, total_encrypted_length, clock_.Now(), false)); + if (connection_.GetSendAlarm()->IsSet()) { + connection_.GetSendAlarm()->Fire(); + } + + EXPECT_TRUE(connection_.connected()); +} + +// Regression test for crbug.com/992831. +TEST_P(QuicConnectionTest, CoalescedPacketThatSavesFrames) { + if (!QuicVersionHasLongHeaderLengths(connection_.transport_version())) { + // Coalesced packets can only be encoded using long header lengths. + return; + } + if (connection_.SupportsMultiplePacketNumberSpaces()) { + // TODO(b/129151114) Enable this test with multiple packet number spaces. + return; + } + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_TRUE(connection_.connected()); + if (QuicVersionUsesCryptoFrames(connection_.transport_version())) { + EXPECT_CALL(visitor_, OnCryptoFrame(_)) + .Times(3) + .WillRepeatedly([this](const QuicCryptoFrame& /*frame*/) { + // QuicFrame takes ownership of the QuicBlockedFrame. + connection_.SendControlFrame(QuicFrame(new QuicBlockedFrame(1, 3))); + }); + } else { + EXPECT_CALL(visitor_, OnStreamFrame(_)) + .Times(3) + .WillRepeatedly([this](const QuicStreamFrame& /*frame*/) { + // QuicFrame takes ownership of the QuicBlockedFrame. + connection_.SendControlFrame(QuicFrame(new QuicBlockedFrame(1, 3))); + }); + } + + uint64_t packet_numbers[3] = {1, 2, 3}; + EncryptionLevel encryption_levels[3] = { + ENCRYPTION_INITIAL, ENCRYPTION_INITIAL, ENCRYPTION_FORWARD_SECURE}; + char buffer[kMaxOutgoingPacketSize] = {}; + size_t total_encrypted_length = 0; + for (int i = 0; i < 3; i++) { + QuicPacketHeader header = + ConstructPacketHeader(packet_numbers[i], encryption_levels[i]); + QuicFrames frames; + if (QuicVersionUsesCryptoFrames(connection_.transport_version())) { + frames.push_back(QuicFrame(&crypto_frame_)); + } else { + frames.push_back(QuicFrame(frame1_)); + } + std::unique_ptr<QuicPacket> packet = ConstructPacket(header, frames); + peer_creator_.set_encryption_level(encryption_levels[i]); + size_t encrypted_length = peer_framer_.EncryptPayload( + encryption_levels[i], QuicPacketNumber(packet_numbers[i]), *packet, + buffer + total_encrypted_length, + sizeof(buffer) - total_encrypted_length); + EXPECT_GT(encrypted_length, 0u); + total_encrypted_length += encrypted_length; + } + connection_.ProcessUdpPacket( + kSelfAddress, kPeerAddress, + QuicReceivedPacket(buffer, total_encrypted_length, clock_.Now(), false)); + if (connection_.GetSendAlarm()->IsSet()) { + connection_.GetSendAlarm()->Fire(); + } + + EXPECT_TRUE(connection_.connected()); + + SendAckPacketToPeer(); +} + +// Regresstion test for b/138962304. +TEST_P(QuicConnectionTest, RtoAndWriteBlocked) { + if (!QuicConnectionPeer::GetSentPacketManager(&connection_) + ->fix_rto_retransmission()) { + return; + } + EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet()); + + QuicStreamId stream_id = 2; + QuicPacketNumber last_data_packet; + SendStreamDataToPeer(stream_id, "foo", 0, NO_FIN, &last_data_packet); + EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet()); + + // Writer gets blocked. + writer_->SetWriteBlocked(); + + // Cancel the stream. + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0); + EXPECT_CALL(visitor_, OnWriteBlocked()).Times(AtLeast(1)); + EXPECT_CALL(visitor_, WillingAndAbleToWrite()) + .WillRepeatedly( + Invoke(¬ifier_, &SimpleSessionNotifier::WillingToWrite)); + SendRstStream(stream_id, QUIC_ERROR_PROCESSING_STREAM, 3); + + // Retransmission timer fires in RTO mode. + connection_.GetRetransmissionAlarm()->Fire(); + // Verify no packets get flushed when writer is blocked. + EXPECT_EQ(0u, connection_.NumQueuedPackets()); +} + +// Regresstion test for b/138962304. +TEST_P(QuicConnectionTest, TlpAndWriteBlocked) { + if (!QuicConnectionPeer::GetSentPacketManager(&connection_) + ->fix_rto_retransmission()) { + return; + } + EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet()); + connection_.SetMaxTailLossProbes(1); + + QuicStreamId stream_id = 2; + QuicPacketNumber last_data_packet; + SendStreamDataToPeer(stream_id, "foo", 0, NO_FIN, &last_data_packet); + SendStreamDataToPeer(4, "foo", 0, NO_FIN, &last_data_packet); + EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet()); + + // Writer gets blocked. + writer_->SetWriteBlocked(); + + // Cancel stream 2. + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0); + EXPECT_CALL(visitor_, OnWriteBlocked()).Times(AtLeast(1)); + SendRstStream(stream_id, QUIC_ERROR_PROCESSING_STREAM, 3); + + // Retransmission timer fires in TLP mode. + connection_.GetRetransmissionAlarm()->Fire(); + // Verify one packets is forced flushed when writer is blocked. + EXPECT_EQ(1u, connection_.NumQueuedPackets()); +} + +// Regresstion test for b/139375344. +TEST_P(QuicConnectionTest, RtoForcesSendingPing) { + if (!QuicConnectionPeer::GetSentPacketManager(&connection_) + ->fix_rto_retransmission() || + connection_.PtoEnabled()) { + return; + } + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + connection_.SetMaxTailLossProbes(2); + EXPECT_EQ(0u, connection_.GetStats().tlp_count); + EXPECT_EQ(0u, connection_.GetStats().rto_count); + + SendStreamDataToPeer(2, "foo", 0, NO_FIN, nullptr); + QuicTime retransmission_time = + connection_.GetRetransmissionAlarm()->deadline(); + EXPECT_NE(QuicTime::Zero(), retransmission_time); + // TLP fires. + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, QuicPacketNumber(2), _, _)); + clock_.AdvanceTime(retransmission_time - clock_.Now()); + connection_.GetRetransmissionAlarm()->Fire(); + EXPECT_EQ(1u, connection_.GetStats().tlp_count); + EXPECT_EQ(0u, connection_.GetStats().rto_count); + EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet()); + + // Packet 1 gets acked. + QuicAckFrame frame = InitAckFrame(1); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(_, _, _, _, _)); + ProcessAckPacket(1, &frame); + EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet()); + retransmission_time = connection_.GetRetransmissionAlarm()->deadline(); + + // RTO fires, verify a PING packet gets sent because there is no data to send. + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, QuicPacketNumber(3), _, _)); + EXPECT_CALL(visitor_, SendPing()).WillOnce(Invoke([this]() { SendPing(); })); + clock_.AdvanceTime(retransmission_time - clock_.Now()); + connection_.GetRetransmissionAlarm()->Fire(); + EXPECT_EQ(1u, connection_.GetStats().tlp_count); + EXPECT_EQ(1u, connection_.GetStats().rto_count); + EXPECT_EQ(1u, writer_->ping_frames().size()); +} + +TEST_P(QuicConnectionTest, ProbeTimeout) { + if (!connection_.session_decides_what_to_write() || + !GetQuicReloadableFlag(quic_fix_rto_retransmission3)) { + return; + } + SetQuicReloadableFlag(quic_enable_pto, true); + SetQuicReloadableFlag(quic_fix_rto_retransmission3, true); + QuicConfig config; + QuicTagVector connection_options; + connection_options.push_back(k2PTO); + config.SetConnectionOptionsToSend(connection_options); + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + connection_.SetFromConfig(config); + EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet()); + + QuicStreamId stream_id = 2; + QuicPacketNumber last_packet; + SendStreamDataToPeer(stream_id, "foooooo", 0, NO_FIN, &last_packet); + SendStreamDataToPeer(stream_id, "foooooo", 7, NO_FIN, &last_packet); + EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet()); + + // Reset stream. + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); + SendRstStream(stream_id, QUIC_ERROR_PROCESSING_STREAM, 3); + + // Fire the PTO and verify only the RST_STREAM is resent, not stream data. + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); + connection_.GetRetransmissionAlarm()->Fire(); + EXPECT_EQ(0u, writer_->stream_frames().size()); + EXPECT_EQ(1u, writer_->rst_stream_frames().size()); + EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet()); +} + +TEST_P(QuicConnectionTest, CloseConnectionAfter7ClientPTOs) { + if (!connection_.session_decides_what_to_write() || + !GetQuicReloadableFlag(quic_fix_rto_retransmission3)) { + return; + } + SetQuicReloadableFlag(quic_enable_pto, true); + QuicConfig config; + QuicTagVector connection_options; + connection_options.push_back(k2PTO); + connection_options.push_back(k7PTO); + config.SetConnectionOptionsToSend(connection_options); + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + connection_.SetFromConfig(config); + EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet()); + + // Send stream data. + SendStreamDataToPeer( + GetNthClientInitiatedStreamId(1, connection_.transport_version()), "foo", + 0, FIN, nullptr); + + // Fire the retransmission alarm 6 times. + for (int i = 0; i < 6; ++i) { + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)); + connection_.GetRetransmissionAlarm()->Fire(); + EXPECT_TRUE(connection_.GetTimeoutAlarm()->IsSet()); + EXPECT_TRUE(connection_.connected()); + } + + EXPECT_EQ(0u, connection_.sent_packet_manager().GetConsecutiveTlpCount()); + EXPECT_EQ(0u, connection_.sent_packet_manager().GetConsecutiveRtoCount()); + EXPECT_EQ(6u, connection_.sent_packet_manager().GetConsecutivePtoCount()); + // Closes connection on 7th PTO. + EXPECT_CALL(visitor_, + OnConnectionClosed(_, ConnectionCloseSource::FROM_SELF)); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)); + connection_.GetRetransmissionAlarm()->Fire(); + EXPECT_FALSE(connection_.GetTimeoutAlarm()->IsSet()); + EXPECT_FALSE(connection_.connected()); + TestConnectionCloseQuicErrorCode(QUIC_TOO_MANY_RTOS); +} + +TEST_P(QuicConnectionTest, CloseConnectionAfter8ClientPTOs) { + if (!connection_.session_decides_what_to_write() || + !GetQuicReloadableFlag(quic_fix_rto_retransmission3)) { + return; + } + SetQuicReloadableFlag(quic_enable_pto, true); + QuicConfig config; + QuicTagVector connection_options; + connection_options.push_back(k2PTO); + connection_options.push_back(k8PTO); + config.SetConnectionOptionsToSend(connection_options); + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + connection_.SetFromConfig(config); + EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet()); + + // Send stream data. + SendStreamDataToPeer( + GetNthClientInitiatedStreamId(1, connection_.transport_version()), "foo", + 0, FIN, nullptr); + + // Fire the retransmission alarm 7 times. + for (int i = 0; i < 7; ++i) { + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)); + connection_.GetRetransmissionAlarm()->Fire(); + EXPECT_TRUE(connection_.GetTimeoutAlarm()->IsSet()); + EXPECT_TRUE(connection_.connected()); + } + + EXPECT_EQ(0u, connection_.sent_packet_manager().GetConsecutiveTlpCount()); + EXPECT_EQ(0u, connection_.sent_packet_manager().GetConsecutiveRtoCount()); + EXPECT_EQ(7u, connection_.sent_packet_manager().GetConsecutivePtoCount()); + // Closes connection on 8th PTO. + EXPECT_CALL(visitor_, + OnConnectionClosed(_, ConnectionCloseSource::FROM_SELF)); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)); + connection_.GetRetransmissionAlarm()->Fire(); + EXPECT_FALSE(connection_.GetTimeoutAlarm()->IsSet()); + EXPECT_FALSE(connection_.connected()); + TestConnectionCloseQuicErrorCode(QUIC_TOO_MANY_RTOS); +} + +TEST_P(QuicConnectionTest, DeprecateHandshakeMode) { + if (!connection_.version().SupportsAntiAmplificationLimit()) { + return; + } + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet()); + + // Send CHLO. + connection_.SendCryptoStreamData(); + EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet()); + + EXPECT_CALL(*loss_algorithm_, DetectLosses(_, _, _, _, _, _)); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0); + QuicAckFrame frame1 = InitAckFrame(1); + // Received ACK for packet 1. + ProcessFramePacketAtLevel(1, QuicFrame(&frame1), ENCRYPTION_INITIAL); + + // Verify retransmission alarm is still set because handshake is not + // confirmed although there is nothing in flight. + EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet()); + EXPECT_EQ(0u, connection_.GetStats().pto_count); + EXPECT_EQ(0u, connection_.GetStats().crypto_retransmit_count); + + // PTO fires, verify a PING packet gets sent because there is no data to send. + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, QuicPacketNumber(2), _, _)); + EXPECT_CALL(visitor_, SendPing()).WillOnce(Invoke([this]() { SendPing(); })); + connection_.GetRetransmissionAlarm()->Fire(); + EXPECT_EQ(1u, connection_.GetStats().pto_count); + EXPECT_EQ(0u, connection_.GetStats().crypto_retransmit_count); + EXPECT_EQ(1u, writer_->ping_frames().size()); +} + +TEST_P(QuicConnectionTest, AntiAmplificationLimit) { + if (!connection_.version().SupportsAntiAmplificationLimit()) { + return; + } + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_CALL(visitor_, OnCryptoFrame(_)).Times(AnyNumber()); + + set_perspective(Perspective::IS_SERVER); + // Verify no data can be sent at the beginning because bytes received is 0. + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0); + connection_.SendCryptoDataWithString("foo", 0); + EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet()); + + // Receives packet 1. + ProcessCryptoPacketAtLevel(1, ENCRYPTION_INITIAL); + + const size_t anti_amplification_factor = + GetQuicFlag(FLAGS_quic_anti_amplification_factor); + // Verify now packets can be sent. + for (size_t i = 0; i < anti_amplification_factor; ++i) { + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); + connection_.SendCryptoDataWithString("foo", i * 3); + // Verify retransmission alarm is not set if throttled by anti-amplification + // limit. + EXPECT_EQ(i != anti_amplification_factor - 1, + connection_.GetRetransmissionAlarm()->IsSet()); + } + // Verify server is throttled by anti-amplification limit. + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0); + connection_.SendCryptoDataWithString("foo", anti_amplification_factor * 3); + + // Receives packet 2. + ProcessCryptoPacketAtLevel(2, ENCRYPTION_INITIAL); + // Verify more packets can be sent. + for (size_t i = anti_amplification_factor; i < anti_amplification_factor * 2; + ++i) { + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); + connection_.SendCryptoDataWithString("foo", i * 3); + } + // Verify server is throttled by anti-amplification limit. + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0); + connection_.SendCryptoDataWithString("foo", + 2 * anti_amplification_factor * 3); + + ProcessPacket(3); + // Verify anti-amplification limit is gone after address validation. + for (size_t i = 0; i < 100; ++i) { + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); + connection_.SendStreamDataWithString(3, "first", i * 0, NO_FIN); + } +} + +TEST_P(QuicConnectionTest, ConnectionCloseFrameType) { + if (!VersionHasIetfQuicFrames(version().transport_version)) { + // Test relevent only for IETF QUIC. + return; + } + const QuicErrorCode kQuicErrorCode = IETF_QUIC_PROTOCOL_VIOLATION; + // Use the (unknown) frame type of 9999 to avoid triggering any logic + // which might be associated with the processing of a known frame type. + const uint64_t kTransportCloseFrameType = 9999u; + QuicFramerPeer::set_current_received_frame_type( + QuicConnectionPeer::GetFramer(&connection_), kTransportCloseFrameType); + // Do a transport connection close + EXPECT_CALL(visitor_, OnConnectionClosed(_, _)); + connection_.CloseConnection( + kQuicErrorCode, "Some random error message", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + const std::vector<QuicConnectionCloseFrame>& connection_close_frames = + writer_->connection_close_frames(); + ASSERT_EQ(1u, connection_close_frames.size()); + EXPECT_EQ(IETF_QUIC_TRANSPORT_CONNECTION_CLOSE, + connection_close_frames[0].close_type); + EXPECT_EQ(kQuicErrorCode, connection_close_frames[0].extracted_error_code); + EXPECT_EQ(kTransportCloseFrameType, + connection_close_frames[0].transport_close_frame_type); +} + +// Regression test for b/137401387 and b/138962304. +TEST_P(QuicConnectionTest, RtoPacketAsTwo) { + if (!QuicConnectionPeer::GetSentPacketManager(&connection_) + ->fix_rto_retransmission() || + connection_.PtoEnabled()) { + return; + } + connection_.SetMaxTailLossProbes(1); + connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); + std::string stream_data(3000, 's'); + // Send packets 1 - 66 and exhaust cwnd. + for (size_t i = 0; i < 22; ++i) { + // 3 packets for each stream, the first 2 are guaranteed to be full packets. + SendStreamDataToPeer(i + 2, stream_data, 0, FIN, nullptr); + } + CongestionBlockWrites(); + + // Fires TLP. Please note, this tail loss probe has 1 byte less stream data + // compared to packet 1 because packet number length increases. + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, QuicPacketNumber(67), _, _)); + connection_.GetRetransmissionAlarm()->Fire(); + // Fires RTO. Please note, although packets 2 and 3 *should* be RTOed, but + // packet 2 gets RTOed to two packets because packet number length increases. + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, QuicPacketNumber(68), _, _)); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, QuicPacketNumber(69), _, _)); + connection_.GetRetransmissionAlarm()->Fire(); + + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + // Resets all streams except 2 and ack packets 1 and 2. Now, packet 3 is the + // only one containing retransmittable frames. + for (size_t i = 1; i < 22; ++i) { + notifier_.OnStreamReset(i + 2, QUIC_STREAM_CANCELLED); + } + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(_, _, _, _, _)); + QuicAckFrame frame = + InitAckFrame({{QuicPacketNumber(1), QuicPacketNumber(3)}}); + ProcessAckPacket(1, &frame); + CongestionUnblockWrites(); + + // Fires TLP, verify a PING gets sent because packet 3 is marked + // RTO_RETRANSMITTED. + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, QuicPacketNumber(70), _, _)); + EXPECT_CALL(visitor_, SendPing()).WillOnce(Invoke([this]() { SendPing(); })); + connection_.GetRetransmissionAlarm()->Fire(); +} + } // namespace } // namespace test } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_constants.h b/chromium/net/third_party/quiche/src/quic/core/quic_constants.h index 442fb0907aa..cdee0f991d1 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_constants.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_constants.h @@ -79,11 +79,6 @@ const QuicByteCount kSessionReceiveWindowLimit = 24 * 1024 * 1024; // 24 MB // TODO(bnc): Move this constant to quic/core/http/. const QuicByteCount kDefaultMaxUncompressedHeaderSize = 16 * 1024; // 16 KB -// Default maximum dynamic table capacity, communicated via -// SETTINGS_QPACK_MAX_TABLE_CAPACITY. -// TODO(bnc): Move this constant to quic/core/http/. -const QuicByteCount kDefaultQpackMaxDynamicTableCapacity = 64 * 1024; // 64 KB - // Minimum size of the CWND, in packets, when doing bandwidth resumption. const QuicPacketCount kMinCongestionWindowForBandwidthResumption = 10; @@ -118,6 +113,9 @@ QUIC_EXPORT_PRIVATE extern const char* const kFinalOffsetHeaderKey; // in low-bandwidth (< ~384 kbps), where an ack is sent per packet. const int64_t kDefaultDelayedAckTimeMs = 25; +// Default shift of the ACK delay in the IETF QUIC ACK frame. +const uint32_t kDefaultAckDelayExponent = 3; + // Minimum tail loss probe time in ms. static const int64_t kMinTailLossProbeTimeoutMs = 10; diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_control_frame_manager.cc b/chromium/net/third_party/quiche/src/quic/core/quic_control_frame_manager.cc index 511ef2be82b..ac4488b1446 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_control_frame_manager.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_control_frame_manager.cc @@ -14,11 +14,25 @@ namespace quic { +namespace { + +// The maximum number of buffered control frames which are waiting to be ACKed +// or sent for the first time. +const size_t kMaxNumControlFrames = 1000; + +} // namespace + QuicControlFrameManager::QuicControlFrameManager(QuicSession* session) : last_control_frame_id_(kInvalidControlFrameId), least_unacked_(1), least_unsent_(1), - session_(session) {} + session_(session), + add_upper_limit_(GetQuicReloadableFlag( + quic_add_upper_limit_of_buffered_control_frames)) { + if (add_upper_limit_) { + QUIC_RELOADABLE_FLAG_COUNT(quic_add_upper_limit_of_buffered_control_frames); + } +} QuicControlFrameManager::~QuicControlFrameManager() { while (!control_frames_.empty()) { @@ -30,6 +44,15 @@ QuicControlFrameManager::~QuicControlFrameManager() { void QuicControlFrameManager::WriteOrBufferQuicFrame(QuicFrame frame) { const bool had_buffered_frames = HasBufferedFrames(); control_frames_.emplace_back(frame); + if (add_upper_limit_ && control_frames_.size() > kMaxNumControlFrames) { + session_->connection()->CloseConnection( + QUIC_TOO_MANY_BUFFERED_CONTROL_FRAMES, + QuicStrCat("More than ", kMaxNumControlFrames, + "buffered control frames, least_unacked: ", least_unacked_, + ", least_unsent_: ", least_unsent_), + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return; + } if (had_buffered_frames) { return; } @@ -101,6 +124,15 @@ void QuicControlFrameManager::WritePing() { } control_frames_.emplace_back( QuicFrame(QuicPingFrame(++last_control_frame_id_))); + if (add_upper_limit_ && control_frames_.size() > kMaxNumControlFrames) { + session_->connection()->CloseConnection( + QUIC_TOO_MANY_BUFFERED_CONTROL_FRAMES, + QuicStrCat("More than ", kMaxNumControlFrames, + "buffered control frames, least_unacked: ", least_unacked_, + ", least_unsent_: ", least_unsent_), + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return; + } WriteBufferedFrames(); } @@ -172,6 +204,9 @@ void QuicControlFrameManager::OnControlFrameLost(const QuicFrame& frame) { } if (!QuicContainsKey(pending_retransmissions_, id)) { pending_retransmissions_[id] = true; + QUIC_BUG_IF(pending_retransmissions_.size() > control_frames_.size()) + << "least_unacked_: " << least_unacked_ + << ", least_unsent_: " << least_unsent_; } } diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_control_frame_manager.h b/chromium/net/third_party/quiche/src/quic/core/quic_control_frame_manager.h index a4c26780d40..54ca4794405 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_control_frame_manager.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_control_frame_manager.h @@ -146,6 +146,9 @@ class QUIC_EXPORT_PRIVATE QuicControlFrameManager { // Last sent window update frame for each stream. QuicSmallMap<QuicStreamId, QuicControlFrameId, 10> window_update_frames_; + + // Latched value of quic_add_upper_limit_of_buffered_control_frames. + const bool add_upper_limit_; }; } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_control_frame_manager_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_control_frame_manager_test.cc index 216ba741132..8059ff6a967 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_control_frame_manager_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_control_frame_manager_test.cc @@ -4,6 +4,7 @@ #include "net/third_party/quiche/src/quic/core/quic_control_frame_manager.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h" #include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" #include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" #include "net/third_party/quiche/src/quic/platform/api/quic_test.h" @@ -288,6 +289,27 @@ TEST_F(QuicControlFrameManagerTest, RetransmitWindowUpdateOfDifferentStreams) { EXPECT_FALSE(manager_->WillingToWrite()); } +TEST_F(QuicControlFrameManagerTest, TooManyBufferedControlFrames) { + SetQuicReloadableFlag(quic_add_upper_limit_of_buffered_control_frames, true); + Initialize(); + EXPECT_CALL(*connection_, SendControlFrame(_)) + .Times(5) + .WillRepeatedly(Invoke(&ClearControlFrame)); + // Flush buffered frames. + manager_->OnCanWrite(); + // Write 995 control frames. + EXPECT_CALL(*connection_, SendControlFrame(_)).WillOnce(Return(false)); + for (size_t i = 0; i < 995; ++i) { + manager_->WriteOrBufferRstStream(kTestStreamId, QUIC_STREAM_CANCELLED, 0); + } + // Verify write one more control frame causes connection close. + EXPECT_CALL( + *connection_, + CloseConnection(QUIC_TOO_MANY_BUFFERED_CONTROL_FRAMES, _, + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET)); + manager_->WriteOrBufferRstStream(kTestStreamId, QUIC_STREAM_CANCELLED, 0); +} + } // namespace } // namespace test } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_handshaker.cc b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_handshaker.cc index bf432ff4967..ac15301efe3 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_handshaker.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_handshaker.cc @@ -141,6 +141,11 @@ CryptoMessageParser* QuicCryptoClientHandshaker::crypto_message_parser() { return QuicCryptoHandshaker::crypto_message_parser(); } +size_t QuicCryptoClientHandshaker::BufferSizeLimitForLevel( + EncryptionLevel level) const { + return QuicCryptoHandshaker::BufferSizeLimitForLevel(level); +} + void QuicCryptoClientHandshaker::HandleServerConfigUpdateMessage( const CryptoHandshakeMessage& server_config_update) { DCHECK(server_config_update.tag() == kSCUP); @@ -321,10 +326,8 @@ void QuicCryptoClientHandshaker::DoSendCHLO( std::move(crypto_negotiated_params_->initial_crypters.encrypter)); session()->connection()->SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT); - // TODO(ianswett): Merge ENCRYPTION_REESTABLISHED and - // ENCRYPTION_FIRST_ESTABLSIHED encryption_established_ = true; - session()->OnCryptoHandshakeEvent(QuicSession::ENCRYPTION_REESTABLISHED); + session()->OnCryptoHandshakeEvent(QuicSession::ENCRYPTION_ESTABLISHED); } void QuicCryptoClientHandshaker::DoReceiveREJ( diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_handshaker.h b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_handshaker.h index 5b7ce35e29b..d33ebfe32fe 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_handshaker.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_handshaker.h @@ -44,6 +44,7 @@ class QUIC_EXPORT_PRIVATE QuicCryptoClientHandshaker const QuicCryptoNegotiatedParameters& crypto_negotiated_params() const override; CryptoMessageParser* crypto_message_parser() override; + size_t BufferSizeLimitForLevel(EncryptionLevel level) const override; // From QuicCryptoHandshaker void OnHandshakeMessage(const CryptoHandshakeMessage& message) override; diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_stream.cc b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_stream.cc index 93f2a61104a..8f89e9a8bec 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_stream.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_stream.cc @@ -84,6 +84,11 @@ CryptoMessageParser* QuicCryptoClientStream::crypto_message_parser() { return handshaker_->crypto_message_parser(); } +size_t QuicCryptoClientStream::BufferSizeLimitForLevel( + EncryptionLevel level) const { + return handshaker_->BufferSizeLimitForLevel(level); +} + std::string QuicCryptoClientStream::chlo_hash() const { return handshaker_->chlo_hash(); } diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_stream.h b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_stream.h index b8dff7e9d34..89f0d2e28b7 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_stream.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_stream.h @@ -99,6 +99,10 @@ class QUIC_EXPORT_PRIVATE QuicCryptoClientStream // Used by QuicCryptoStream to parse data received on this stream. virtual CryptoMessageParser* crypto_message_parser() = 0; + + // Used by QuicCryptoStream to know how much unprocessed data can be + // buffered at each encryption level. + virtual size_t BufferSizeLimitForLevel(EncryptionLevel level) const = 0; }; // ProofHandler is an interface that handles callbacks from the crypto @@ -142,6 +146,7 @@ class QUIC_EXPORT_PRIVATE QuicCryptoClientStream const QuicCryptoNegotiatedParameters& crypto_negotiated_params() const override; CryptoMessageParser* crypto_message_parser() override; + size_t BufferSizeLimitForLevel(EncryptionLevel level) const override; std::string chlo_hash() const; diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_handshaker.cc b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_handshaker.cc index fa0f78a51c9..d608ead807c 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_handshaker.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_handshaker.cc @@ -45,5 +45,9 @@ CryptoMessageParser* QuicCryptoHandshaker::crypto_message_parser() { return &crypto_framer_; } +size_t QuicCryptoHandshaker::BufferSizeLimitForLevel(EncryptionLevel) const { + return GetQuicFlag(FLAGS_quic_max_buffered_crypto_bytes); +} + #undef ENDPOINT // undef for jumbo builds } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_handshaker.h b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_handshaker.h index 231acfcdb30..e5d8d51894e 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_handshaker.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_handshaker.h @@ -27,6 +27,7 @@ class QUIC_EXPORT_PRIVATE QuicCryptoHandshaker void OnHandshakeMessage(const CryptoHandshakeMessage& message) override; CryptoMessageParser* crypto_message_parser(); + size_t BufferSizeLimitForLevel(EncryptionLevel level) const; protected: QuicTag last_sent_handshake_message_tag() const { diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_server_handshaker.cc b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_server_handshaker.cc index 26134d7185b..ab381d7ea11 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_server_handshaker.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_server_handshaker.cc @@ -373,6 +373,11 @@ CryptoMessageParser* QuicCryptoServerHandshaker::crypto_message_parser() { return QuicCryptoHandshaker::crypto_message_parser(); } +size_t QuicCryptoServerHandshaker::BufferSizeLimitForLevel( + EncryptionLevel level) const { + return QuicCryptoHandshaker::BufferSizeLimitForLevel(level); +} + void QuicCryptoServerHandshaker::ProcessClientHello( QuicReferenceCountedPointer<ValidateClientHelloResultCallback::Result> result, diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_server_handshaker.h b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_server_handshaker.h index f6644083768..4b7b3bac474 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_server_handshaker.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_server_handshaker.h @@ -58,6 +58,7 @@ class QUIC_EXPORT_PRIVATE QuicCryptoServerHandshaker const QuicCryptoNegotiatedParameters& crypto_negotiated_params() const override; CryptoMessageParser* crypto_message_parser() override; + size_t BufferSizeLimitForLevel(EncryptionLevel level) const override; // From QuicCryptoHandshaker void OnHandshakeMessage(const CryptoHandshakeMessage& message) override; diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_server_stream.cc b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_server_stream.cc index 09344cde675..353de98a125 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_server_stream.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_server_stream.cc @@ -111,6 +111,11 @@ CryptoMessageParser* QuicCryptoServerStream::crypto_message_parser() { return handshaker()->crypto_message_parser(); } +size_t QuicCryptoServerStream::BufferSizeLimitForLevel( + EncryptionLevel level) const { + return handshaker()->BufferSizeLimitForLevel(level); +} + void QuicCryptoServerStream::OnSuccessfulVersionNegotiation( const ParsedQuicVersion& version) { DCHECK_EQ(version, session()->connection()->version()); diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_server_stream.h b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_server_stream.h index c5e0abc0fb4..3a7d6e74b1a 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_server_stream.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_server_stream.h @@ -119,18 +119,16 @@ class QUIC_EXPORT_PRIVATE QuicCryptoServerStream // Used by QuicCryptoStream to parse data received on this stream. virtual CryptoMessageParser* crypto_message_parser() = 0; + + // Used by QuicCryptoStream to know how much unprocessed data can be + // buffered at each encryption level. + virtual size_t BufferSizeLimitForLevel(EncryptionLevel level) const = 0; }; class Helper { public: virtual ~Helper() {} - // Given the current connection_id, generates a new ConnectionId to - // be returned with a reject. - virtual QuicConnectionId GenerateConnectionIdForReject( - QuicTransportVersion version, - QuicConnectionId connection_id) const = 0; - // Returns true if |message|, which was received on |self_address| is // acceptable according to the visitor's policy. Otherwise, returns false // and populates |error_details|. @@ -178,6 +176,7 @@ class QUIC_EXPORT_PRIVATE QuicCryptoServerStream const QuicCryptoNegotiatedParameters& crypto_negotiated_params() const override; CryptoMessageParser* crypto_message_parser() override; + size_t BufferSizeLimitForLevel(EncryptionLevel level) const override; void OnSuccessfulVersionNegotiation( const ParsedQuicVersion& version) override; diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_server_stream_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_server_stream_test.cc index 238162c96ef..b58d987e6ee 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_server_stream_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_server_stream_test.cc @@ -90,8 +90,6 @@ class QuicCryptoServerStreamTest : public QuicTestWithParam<bool> { server_session_.reset(server_session); EXPECT_CALL(*server_session_->helper(), CanAcceptClientHello(_, _, _, _, _)) .Times(testing::AnyNumber()); - EXPECT_CALL(*server_session_->helper(), GenerateConnectionIdForReject(_, _)) - .Times(testing::AnyNumber()); crypto_test_utils::SetupCryptoServerConfigForTest( server_connection_->clock(), server_connection_->random_generator(), &server_crypto_config_); diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_stream.cc b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_stream.cc index 395b4906b25..e5b13f1f034 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_stream.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_stream.cc @@ -78,6 +78,11 @@ void QuicCryptoStream::OnCryptoFrame(const QuicCryptoFrame& frame) { << "Versions less than 47 shouldn't receive CRYPTO frames"; EncryptionLevel level = session()->connection()->last_decrypted_level(); substreams_[level].sequencer.OnCryptoFrame(frame); + if (substreams_[level].sequencer.NumBytesBuffered() > + BufferSizeLimitForLevel(frame.level)) { + CloseConnectionWithDetails(QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA, + "Too much crypto data received"); + } } void QuicCryptoStream::OnStreamFrame(const QuicStreamFrame& frame) { @@ -154,6 +159,7 @@ void QuicCryptoStream::WriteCryptoData(EncryptionLevel level, QUIC_BUG << "Empty crypto data being written"; return; } + const bool had_buffered_data = HasBufferedCryptoFrames(); // Append |data| to the send buffer for this encryption level. struct iovec iov(QuicUtils::MakeIovec(data)); QuicStreamSendBuffer* send_buffer = &substreams_[level].send_buffer; @@ -167,19 +173,23 @@ void QuicCryptoStream::WriteCryptoData(EncryptionLevel level, CloseConnectionWithDetails(QUIC_STREAM_LENGTH_OVERFLOW, "Writing too much crypto handshake data"); } + if (had_buffered_data) { + // Do not try to write if there is buffered data. + return; + } EncryptionLevel current_level = session()->connection()->encryption_level(); session()->connection()->SetDefaultEncryptionLevel(level); size_t bytes_consumed = session()->connection()->SendCryptoData(level, data.length(), offset); session()->connection()->SetDefaultEncryptionLevel(current_level); - // Since CRYPTO frames aren't flow controlled, SendCryptoData should have sent - // all data we asked it to send. - DCHECK_EQ(bytes_consumed, data.length()); - send_buffer->OnStreamDataConsumed(bytes_consumed); } +size_t QuicCryptoStream::BufferSizeLimitForLevel(EncryptionLevel) const { + return GetQuicFlag(FLAGS_quic_max_buffered_crypto_bytes); +} + void QuicCryptoStream::OnSuccessfulVersionNegotiation( const ParsedQuicVersion& /*version*/) {} @@ -412,6 +422,48 @@ void QuicCryptoStream::RetransmitData(QuicCryptoFrame* crypto_frame) { session()->connection()->SetDefaultEncryptionLevel(current_encryption_level); } +void QuicCryptoStream::WriteBufferedCryptoFrames() { + QUIC_BUG_IF(!QuicVersionUsesCryptoFrames( + session()->connection()->transport_version())) + << "Versions less than 47 don't use CRYPTO frames"; + EncryptionLevel current_encryption_level = + session()->connection()->encryption_level(); + for (EncryptionLevel level : + {ENCRYPTION_INITIAL, ENCRYPTION_ZERO_RTT, ENCRYPTION_FORWARD_SECURE}) { + QuicStreamSendBuffer* send_buffer = &substreams_[level].send_buffer; + const size_t data_length = + send_buffer->stream_offset() - send_buffer->stream_bytes_written(); + if (data_length == 0) { + // No buffered data for this encryption level. + continue; + } + session()->connection()->SetDefaultEncryptionLevel(level); + size_t bytes_consumed = session()->connection()->SendCryptoData( + level, data_length, send_buffer->stream_bytes_written()); + send_buffer->OnStreamDataConsumed(bytes_consumed); + if (bytes_consumed < data_length) { + // Connection is write blocked. + break; + } + } + session()->connection()->SetDefaultEncryptionLevel(current_encryption_level); +} + +bool QuicCryptoStream::HasBufferedCryptoFrames() const { + QUIC_BUG_IF(!QuicVersionUsesCryptoFrames( + session()->connection()->transport_version())) + << "Versions less than 47 don't use CRYPTO frames"; + for (EncryptionLevel level : + {ENCRYPTION_INITIAL, ENCRYPTION_ZERO_RTT, ENCRYPTION_FORWARD_SECURE}) { + const QuicStreamSendBuffer& send_buffer = substreams_[level].send_buffer; + DCHECK_GE(send_buffer.stream_offset(), send_buffer.stream_bytes_written()); + if (send_buffer.stream_offset() > send_buffer.stream_bytes_written()) { + return true; + } + } + return false; +} + bool QuicCryptoStream::IsFrameOutstanding(EncryptionLevel level, size_t offset, size_t length) const { diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_stream.h b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_stream.h index 1b641b19cec..12a36f894b6 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_stream.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_stream.h @@ -80,6 +80,10 @@ class QUIC_EXPORT_PRIVATE QuicCryptoStream : public QuicStream { // Provides the message parser to use when data is received on this stream. virtual CryptoMessageParser* crypto_message_parser() = 0; + // Returns the maximum number of bytes that can be buffered at a particular + // encryption level |level|. + virtual size_t BufferSizeLimitForLevel(EncryptionLevel level) const; + // Called when the underlying QuicConnection has agreed upon a QUIC version to // use. virtual void OnSuccessfulVersionNegotiation(const ParsedQuicVersion& version); @@ -130,6 +134,12 @@ class QUIC_EXPORT_PRIVATE QuicCryptoStream : public QuicStream { // encryption level, offset, and length in |crypto_frame|. void RetransmitData(QuicCryptoFrame* crypto_frame); + // Called to write buffered crypto frames. + void WriteBufferedCryptoFrames(); + + // Returns true if there is buffered crypto frames. + bool HasBufferedCryptoFrames() const; + // Returns true if any portion of the data at encryption level |level| // starting at |offset| for |length| bytes is outstanding. bool IsFrameOutstanding(EncryptionLevel level, diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_stream_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_stream_test.cc index dd4450eab57..af5a8ccf2f8 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_stream_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_stream_test.cc @@ -23,6 +23,7 @@ using testing::_; using testing::InSequence; using testing::Invoke; using testing::InvokeWithoutArgs; +using testing::Return; namespace quic { namespace test { @@ -519,16 +520,73 @@ TEST_F(QuicCryptoStreamTest, CryptoMessageFramingOverhead) { SCOPED_TRACE(version); QuicByteCount expected_overhead = 48; if (VersionHasIetfInvariantHeader(version)) { - expected_overhead = 52; + expected_overhead += 4; } if (QuicVersionHasLongHeaderLengths(version)) { - expected_overhead = 55; + expected_overhead += 3; + } + if (VersionHasLengthPrefixedConnectionIds(version)) { + expected_overhead += 1; } EXPECT_EQ(expected_overhead, QuicCryptoStream::CryptoMessageFramingOverhead( version, TestConnectionId())); } } +TEST_F(QuicCryptoStreamTest, WriteBufferedCryptoFrames) { + if (!QuicVersionUsesCryptoFrames(connection_->transport_version())) { + return; + } + EXPECT_FALSE(stream_->HasBufferedCryptoFrames()); + InSequence s; + // Send [0, 1350) in ENCRYPTION_INITIAL. + EXPECT_EQ(ENCRYPTION_INITIAL, connection_->encryption_level()); + std::string data(1350, 'a'); + // Only consumed 1000 bytes. + EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_INITIAL, 1350, 0)) + .WillOnce(Return(1000)); + stream_->WriteCryptoData(ENCRYPTION_INITIAL, data); + EXPECT_TRUE(stream_->HasBufferedCryptoFrames()); + + // Send [1350, 2700) in ENCRYPTION_ZERO_RTT and verify no write is attempted + // because there is buffered data. + EXPECT_CALL(*connection_, SendCryptoData(_, _, _)).Times(0); + connection_->SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT); + stream_->WriteCryptoData(ENCRYPTION_ZERO_RTT, data); + EXPECT_EQ(ENCRYPTION_ZERO_RTT, connection_->encryption_level()); + + EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_INITIAL, 350, 1000)) + .WillOnce(Return(350)); + // Partial write of ENCRYPTION_ZERO_RTT data. + EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_ZERO_RTT, 1350, 0)) + .WillOnce(Return(1000)); + stream_->WriteBufferedCryptoFrames(); + EXPECT_TRUE(stream_->HasBufferedCryptoFrames()); + EXPECT_EQ(ENCRYPTION_ZERO_RTT, connection_->encryption_level()); + + EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_ZERO_RTT, 350, 1000)) + .WillOnce(Return(350)); + stream_->WriteBufferedCryptoFrames(); + EXPECT_FALSE(stream_->HasBufferedCryptoFrames()); +} + +TEST_F(QuicCryptoStreamTest, LimitBufferedCryptoData) { + if (!QuicVersionUsesCryptoFrames(connection_->transport_version())) { + return; + } + + EXPECT_CALL(*connection_, + CloseConnection(QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA, _, _)); + std::string large_frame(2 * GetQuicFlag(FLAGS_quic_max_buffered_crypto_bytes), + 'a'); + + // Set offset to 1 so that we guarantee the data gets buffered instead of + // immediately processed. + QuicStreamOffset offset = 1; + stream_->OnCryptoFrame( + QuicCryptoFrame(ENCRYPTION_INITIAL, offset, large_frame)); +} + } // namespace } // namespace test } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_data_reader.cc b/chromium/net/third_party/quiche/src/quic/core/quic_data_reader.cc index e2871d760ba..c9a76be8f57 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_data_reader.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_data_reader.cc @@ -136,7 +136,7 @@ bool QuicDataReader::ReadStringPiece(QuicStringPiece* result, size_t size) { bool QuicDataReader::ReadConnectionId(QuicConnectionId* connection_id, uint8_t length) { - if (length > kQuicMaxConnectionIdLength) { + if (length > kQuicMaxConnectionIdAllVersionsLength) { QUIC_BUG << "Attempted to read connection ID with length too high " << static_cast<int>(length); return false; @@ -146,21 +146,13 @@ bool QuicDataReader::ReadConnectionId(QuicConnectionId* connection_id, return true; } - if (!GetQuicRestartFlag(quic_use_allocated_connection_ids)) { - const bool ok = ReadBytes(connection_id->mutable_data(), length); - if (ok) { - connection_id->set_length(length); - } - return ok; - } - QUIC_RESTART_FLAG_COUNT_N(quic_use_allocated_connection_ids, 6, 6); - if (BytesRemaining() < length) { return false; } connection_id->set_length(length); - const bool ok = ReadBytes(connection_id->mutable_data(), length); + const bool ok = + ReadBytes(connection_id->mutable_data(), connection_id->length()); DCHECK(ok); return ok; } @@ -171,7 +163,7 @@ bool QuicDataReader::ReadLengthPrefixedConnectionId( if (!ReadUInt8(&connection_id_length)) { return false; } - if (connection_id_length > kQuicMaxConnectionIdLength) { + if (connection_id_length > kQuicMaxConnectionIdAllVersionsLength) { return false; } return ReadConnectionId(connection_id, connection_id_length); @@ -191,6 +183,10 @@ QuicStringPiece QuicDataReader::PeekRemainingPayload() const { return QuicStringPiece(data_ + pos_, len_ - pos_); } +QuicStringPiece QuicDataReader::FullPayload() const { + return QuicStringPiece(data_, len_); +} + bool QuicDataReader::ReadBytes(void* result, size_t size) { // Make sure that we have enough data to read. if (!CanRead(size)) { diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_data_reader.h b/chromium/net/third_party/quiche/src/quic/core/quic_data_reader.h index 72e2d132258..74ed2269d0c 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_data_reader.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_data_reader.h @@ -110,6 +110,14 @@ class QUIC_EXPORT_PRIVATE QuicDataReader { // DOES NOT forward the internal iterator. QuicStringPiece PeekRemainingPayload() const; + // Returns the entire payload as a QuicStringPiece. + // + // NOTE: Does not copy but rather references strings in the underlying buffer. + // This should be kept in mind when handling memory management! + // + // DOES NOT forward the internal iterator. + QuicStringPiece FullPayload() const; + // Reads a given number of bytes into the given buffer. The buffer // must be of adequate size. // Forwards the internal iterator on success. diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_data_writer_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_data_writer_test.cc index 727c8133301..64ad5dd22ae 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_data_writer_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_data_writer_test.cc @@ -262,8 +262,8 @@ TEST_P(QuicDataWriterTest, WriteConnectionId) { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, }; EXPECT_EQ(connection_id.length(), QUIC_ARRAYSIZE(big_endian)); - ASSERT_LE(connection_id.length(), kQuicMaxConnectionIdLength); - char buffer[kQuicMaxConnectionIdLength]; + ASSERT_LE(connection_id.length(), 255); + char buffer[255]; QuicDataWriter writer(connection_id.length(), buffer, GetParam().endianness); EXPECT_TRUE(writer.WriteConnectionId(connection_id)); test::CompareCharArraysWithHexError("connection_id", buffer, @@ -285,7 +285,7 @@ TEST_P(QuicDataWriterTest, LengthPrefixedConnectionId) { }; EXPECT_EQ(QUIC_ARRAYSIZE(length_prefixed_connection_id), kConnectionIdLengthSize + connection_id.length()); - char buffer[kConnectionIdLengthSize + kQuicMaxConnectionIdLength] = {}; + char buffer[kConnectionIdLengthSize + 255] = {}; QuicDataWriter writer(QUIC_ARRAYSIZE(buffer), buffer); EXPECT_TRUE(writer.WriteLengthPrefixedConnectionId(connection_id)); test::CompareCharArraysWithHexError( @@ -1149,11 +1149,12 @@ TEST_P(QuicDataWriterTest, PeekVarInt62Length) { } TEST_P(QuicDataWriterTest, InvalidConnectionIdLengthRead) { - static const uint8_t bad_connection_id_length = 19; - static_assert(bad_connection_id_length > kQuicMaxConnectionIdLength, - "bad lengths"); - char buffer[20] = {}; - QuicDataReader reader(buffer, 20); + static const uint8_t bad_connection_id_length = 200; + static_assert( + bad_connection_id_length > kQuicMaxConnectionIdAllVersionsLength, + "bad lengths"); + char buffer[255] = {}; + QuicDataReader reader(buffer, sizeof(buffer)); QuicConnectionId connection_id; bool ok; EXPECT_QUIC_BUG( @@ -1231,6 +1232,37 @@ TEST_P(QuicDataWriterTest, SeekTooFarFails) { } } +TEST_P(QuicDataWriterTest, PayloadReads) { + char buffer[16] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; + char expected_first_read[4] = {1, 2, 3, 4}; + char expected_remaining[12] = {5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; + QuicDataReader reader(buffer, sizeof(buffer)); + char first_read_buffer[4] = {}; + EXPECT_TRUE(reader.ReadBytes(first_read_buffer, sizeof(first_read_buffer))); + test::CompareCharArraysWithHexError( + "first read", first_read_buffer, sizeof(first_read_buffer), + expected_first_read, sizeof(expected_first_read)); + QuicStringPiece peeked_remaining_payload = reader.PeekRemainingPayload(); + test::CompareCharArraysWithHexError( + "peeked_remaining_payload", peeked_remaining_payload.data(), + peeked_remaining_payload.length(), expected_remaining, + sizeof(expected_remaining)); + QuicStringPiece full_payload = reader.FullPayload(); + test::CompareCharArraysWithHexError("full_payload", full_payload.data(), + full_payload.length(), buffer, + sizeof(buffer)); + QuicStringPiece read_remaining_payload = reader.ReadRemainingPayload(); + test::CompareCharArraysWithHexError( + "read_remaining_payload", read_remaining_payload.data(), + read_remaining_payload.length(), expected_remaining, + sizeof(expected_remaining)); + EXPECT_TRUE(reader.IsDoneReading()); + QuicStringPiece full_payload2 = reader.FullPayload(); + test::CompareCharArraysWithHexError("full_payload2", full_payload2.data(), + full_payload2.length(), buffer, + sizeof(buffer)); +} + } // namespace } // namespace test } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_dispatcher.cc b/chromium/net/third_party/quiche/src/quic/core/quic_dispatcher.cc index c6ee81634c7..4d7b70e53d8 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_dispatcher.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_dispatcher.cc @@ -228,18 +228,34 @@ void QuicDispatcher::ProcessPacket(const QuicSocketAddress& self_address, QuicStringPiece(packet.data(), packet.length())); ReceivedPacketInfo packet_info(self_address, peer_address, packet); std::string detailed_error; - const QuicErrorCode error = QuicFramer::ProcessPacketDispatcher( - packet, expected_server_connection_id_length_, &packet_info.form, - &packet_info.version_flag, &packet_info.version_label, - &packet_info.destination_connection_id, &packet_info.source_connection_id, - &detailed_error); + QuicErrorCode error; + if (!GetQuicReloadableFlag(quic_use_parse_public_header)) { + error = QuicFramer::ProcessPacketDispatcher( + packet, expected_server_connection_id_length_, &packet_info.form, + &packet_info.version_flag, &packet_info.version_label, + &packet_info.destination_connection_id, + &packet_info.source_connection_id, &detailed_error); + } else { + QUIC_RELOADABLE_FLAG_COUNT(quic_use_parse_public_header); + bool retry_token_present; + QuicStringPiece retry_token; + error = QuicFramer::ParsePublicHeaderDispatcher( + packet, expected_server_connection_id_length_, &packet_info.form, + &packet_info.version_flag, &packet_info.use_length_prefix, + &packet_info.version_label, &packet_info.version, + &packet_info.destination_connection_id, + &packet_info.source_connection_id, &retry_token_present, &retry_token, + &detailed_error); + } if (error != QUIC_NO_ERROR) { // Packet has framing error. SetLastError(error); QUIC_DLOG(ERROR) << detailed_error; return; } - packet_info.version = ParseQuicVersionLabel(packet_info.version_label); + if (!GetQuicReloadableFlag(quic_use_parse_public_header)) { + packet_info.version = ParseQuicVersionLabel(packet_info.version_label); + } if (packet_info.destination_connection_id.length() != expected_server_connection_id_length_ && !should_update_expected_server_connection_id_length_ && @@ -249,6 +265,26 @@ void QuicDispatcher::ProcessPacket(const QuicSocketAddress& self_address, QUIC_DLOG(ERROR) << "Invalid Connection Id Length"; return; } + + if (packet_info.version_flag && IsSupportedVersion(packet_info.version)) { + if (!QuicUtils::IsConnectionIdValidForVersion( + packet_info.destination_connection_id, + packet_info.version.transport_version)) { + SetLastError(QUIC_INVALID_PACKET_HEADER); + QUIC_DLOG(ERROR) + << "Invalid destination connection ID length for version"; + return; + } + if (packet_info.version.SupportsClientConnectionIds() && + !QuicUtils::IsConnectionIdValidForVersion( + packet_info.source_connection_id, + packet_info.version.transport_version)) { + SetLastError(QUIC_INVALID_PACKET_HEADER); + QUIC_DLOG(ERROR) << "Invalid source connection ID length for version"; + return; + } + } + if (should_update_expected_server_connection_id_length_) { expected_server_connection_id_length_ = packet_info.destination_connection_id.length(); @@ -269,24 +305,26 @@ QuicConnectionId QuicDispatcher::MaybeReplaceServerConnectionId( } DCHECK(QuicUtils::VariableLengthConnectionIdAllowedForVersion( version.transport_version)); - auto it = connection_id_map_.find(server_connection_id); - if (it != connection_id_map_.end()) { - return it->second; - } + QuicConnectionId new_connection_id = - session_helper_->GenerateConnectionIdForReject(version.transport_version, - server_connection_id); + GenerateNewServerConnectionId(version, server_connection_id); DCHECK_EQ(expected_server_connection_id_length_, new_connection_id.length()); - // TODO(dschinazi) Prevent connection_id_map_ from growing indefinitely - // before we ship a version that supports variable length connection IDs - // to production. - connection_id_map_.insert( - std::make_pair(server_connection_id, new_connection_id)); + + // Verify that GenerateNewServerConnectionId is deterministic. + DCHECK_EQ(new_connection_id, + GenerateNewServerConnectionId(version, server_connection_id)); + QUIC_DLOG(INFO) << "Replacing incoming connection ID " << server_connection_id << " with " << new_connection_id; return new_connection_id; } +QuicConnectionId QuicDispatcher::GenerateNewServerConnectionId( + ParsedQuicVersion /*version*/, + QuicConnectionId connection_id) const { + return QuicUtils::CreateReplacementConnectionId(connection_id); +} + bool QuicDispatcher::MaybeDispatchPacket( const ReceivedPacketInfo& packet_info) { // Port zero is only allowed for unidirectional UDP, so is disallowed by QUIC. @@ -319,7 +357,8 @@ bool QuicDispatcher::MaybeDispatchPacket( << " to time-wait list."; StatelesslyTerminateConnection( server_connection_id, packet_info.form, packet_info.version_flag, - packet_info.version, QUIC_HANDSHAKE_FAILED, "Reject connection", + packet_info.use_length_prefix, packet_info.version, + QUIC_HANDSHAKE_FAILED, "Reject connection", quic::QuicTimeWaitListManager::SEND_STATELESS_RESET); DCHECK(time_wait_list_manager_->IsConnectionIdInTimeWait( @@ -394,11 +433,22 @@ bool QuicDispatcher::MaybeDispatchPacket( // packet and stop processing the current packet. QuicConnectionId client_connection_id = packet_info.source_connection_id; + bool use_length_prefix = packet_info.use_length_prefix; + if (!GetQuicReloadableFlag(quic_use_length_prefix_from_packet_info)) { + use_length_prefix = packet_info.form != GOOGLE_QUIC_PACKET && + !QuicVersionLabelUses4BitConnectionIdLength( + packet_info.version_label); + } else { + QUIC_RELOADABLE_FLAG_COUNT(quic_use_length_prefix_from_packet_info); + // TODO(dschinazi) remove the client-side workaround in + // QuicFramer::ParseServerVersionNegotiationProbeResponse + // when quic_use_length_prefix_from_packet_info is deprecated. + } time_wait_list_manager()->SendVersionNegotiationPacket( server_connection_id, client_connection_id, - packet_info.form != GOOGLE_QUIC_PACKET, GetSupportedVersions(), - packet_info.self_address, packet_info.peer_address, - GetPerPacketContext()); + packet_info.form != GOOGLE_QUIC_PACKET, use_length_prefix, + GetSupportedVersions(), packet_info.self_address, + packet_info.peer_address, GetPerPacketContext()); } return true; } @@ -454,7 +504,8 @@ void QuicDispatcher::ProcessHeader(ReceivedPacketInfo* packet_info) { QUIC_CODE_COUNT(quic_reject_fate_time_wait); StatelesslyTerminateConnection( server_connection_id, packet_info->form, packet_info->version_flag, - packet_info->version, QUIC_HANDSHAKE_FAILED, "Reject connection", + packet_info->use_length_prefix, packet_info->version, + QUIC_HANDSHAKE_FAILED, "Reject connection", quic::QuicTimeWaitListManager::SEND_STATELESS_RESET); DCHECK(time_wait_list_manager_->IsConnectionIdInTimeWait( @@ -482,6 +533,37 @@ QuicDispatcher::QuicPacketFate QuicDispatcher::ValidityChecks( // set. Since this may be a client continuing a connection we lost track of // via server restart, send a rejection to fast-fail the connection. if (!packet_info.version_flag) { + if (GetQuicReloadableFlag(quic_reply_to_old_android_conformance_test)) { + QUIC_RELOADABLE_FLAG_COUNT(quic_reply_to_old_android_conformance_test); + // The Android network conformance test contains a UDP test that sends a + // 12-byte packet with the following format: + // - 0x0c (public flags: 8-byte connection ID, 1-byte packet number) + // - randomized 8-byte connection ID + // - 0x01 (1-byte packet number) + // - 0x00 (private flags) + // - 0x07 (PING frame). + // That packet is invalid and we would normally drop it but in order to + // unblock this conformance testing we have the following workaround that + // will be removed once the fixed test is deployed. + // TODO(b/139691956) Remove this workaround once fixed test is deployed. + if (packet_info.packet.length() == 12 && + packet_info.packet.data()[0] == 0x0c && + packet_info.packet.data()[9] == 0x01 && + packet_info.packet.data()[10] == 0x00 && + packet_info.packet.data()[11] == 0x07) { + QUIC_DLOG(INFO) << "Received Android UDP network conformance test " + "packet with connection ID " + << packet_info.destination_connection_id; + // Respond with a public reset that the test will know how to parse + // then return kFateDrop to stop processing of this packet. + time_wait_list_manager()->SendPublicReset( + packet_info.self_address, packet_info.peer_address, + packet_info.destination_connection_id, + /*ietf_quic=*/false, GetPerPacketContext()); + return kFateDrop; + } + } + QUIC_DLOG(INFO) << "Packet without version arrived for unknown connection ID " << packet_info.destination_connection_id; @@ -521,7 +603,9 @@ void QuicDispatcher::CleanUpSession(SessionMap::iterator it, VersionHasIetfInvariantHeader(connection->transport_version()) ? IETF_QUIC_LONG_HEADER_PACKET : GOOGLE_QUIC_PACKET, - /*version_flag=*/true, connection->version(), QUIC_HANDSHAKE_FAILED, + /*version_flag=*/true, + connection->version().HasLengthPrefixedConnectionIds(), + connection->version(), QUIC_HANDSHAKE_FAILED, "Connection is closed by server before handshake confirmed", // Although it is our intention to send termination packets, the // |action| argument is not used by this call to @@ -665,6 +749,7 @@ void QuicDispatcher::StatelesslyTerminateConnection( QuicConnectionId server_connection_id, PacketHeaderFormat format, bool version_flag, + bool use_length_prefix, ParsedQuicVersion version, QuicErrorCode error_code, const std::string& error_details, @@ -707,7 +792,7 @@ void QuicDispatcher::StatelesslyTerminateConnection( std::vector<std::unique_ptr<QuicEncryptedPacket>> termination_packets; termination_packets.push_back(QuicFramer::BuildVersionNegotiationPacket( server_connection_id, EmptyQuicConnectionId(), - /*ietf_quic=*/format != GOOGLE_QUIC_PACKET, + /*ietf_quic=*/format != GOOGLE_QUIC_PACKET, use_length_prefix, /*versions=*/{})); time_wait_list_manager()->AddConnectionIdToTimeWait( server_connection_id, /*ietf_quic=*/format != GOOGLE_QUIC_PACKET, @@ -728,8 +813,10 @@ void QuicDispatcher::OnExpiredPackets( server_connection_id, early_arrived_packets.ietf_quic ? IETF_QUIC_LONG_HEADER_PACKET : GOOGLE_QUIC_PACKET, - /*version_flag=*/true, early_arrived_packets.version, - QUIC_HANDSHAKE_FAILED, "Packets buffered for too long", + /*version_flag=*/true, + early_arrived_packets.version.HasLengthPrefixedConnectionIds(), + early_arrived_packets.version, QUIC_HANDSHAKE_FAILED, + "Packets buffered for too long", quic::QuicTimeWaitListManager::SEND_STATELESS_RESET); } @@ -820,7 +907,8 @@ void QuicDispatcher::ProcessChlo(const std::string& alpn, QUIC_CODE_COUNT(quic_reject_stop_accepting_new_connections); StatelesslyTerminateConnection( packet_info->destination_connection_id, packet_info->form, - /*version_flag=*/true, packet_info->version, QUIC_HANDSHAKE_FAILED, + /*version_flag=*/true, packet_info->use_length_prefix, + packet_info->version, QUIC_HANDSHAKE_FAILED, "Stop accepting new connections", quic::QuicTimeWaitListManager::SEND_STATELESS_RESET); // Time wait list will reject the packet correspondingly. diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_dispatcher.h b/chromium/net/third_party/quiche/src/quic/core/quic_dispatcher.h index cfaafa02360..abcd4f2f733 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_dispatcher.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_dispatcher.h @@ -113,10 +113,6 @@ class QuicDispatcher : public QuicTimeWaitListManager::Visitor, QuicConnectionId, QuicConnectionIdHash>; - const ConnectionIdMap& connection_id_map() const { - return connection_id_map_; - } - // The largest packet number we expect to receive with a connection // ID for a connection that is not established yet. The current design will // send a handshake and then up to 50 or so data packets, and then it may @@ -152,6 +148,13 @@ class QuicDispatcher : public QuicTimeWaitListManager::Visitor, // otherwise, returns false and the packet needs further processing. virtual bool MaybeDispatchPacket(const ReceivedPacketInfo& packet_info); + // Generate a connection ID with a length that is expected by the dispatcher. + // Note that this MUST produce a deterministic result (calling this method + // with two connection IDs that are equal must produce the same result). + virtual QuicConnectionId GenerateNewServerConnectionId( + ParsedQuicVersion version, + QuicConnectionId connection_id) const; + // Values to be returned by ValidityChecks() to indicate what should be done // with a packet. Fates with greater values are considered to be higher // priority. ValidityChecks should return fate based on the priority order @@ -254,6 +257,7 @@ class QuicDispatcher : public QuicTimeWaitListManager::Visitor, QuicConnectionId server_connection_id, PacketHeaderFormat format, bool version_flag, + bool use_length_prefix, ParsedQuicVersion version, QuicErrorCode error_code, const std::string& error_details, @@ -326,9 +330,6 @@ class QuicDispatcher : public QuicTimeWaitListManager::Visitor, SessionMap session_map_; - // Map of connection IDs with bad lengths to their replacements. - ConnectionIdMap connection_id_map_; - // Entity that manages connection_ids in time wait state. std::unique_ptr<QuicTimeWaitListManager> time_wait_list_manager_; diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_dispatcher_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_dispatcher_test.cc index b18bddb983f..d0e5092c9ef 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_dispatcher_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_dispatcher_test.cc @@ -118,9 +118,10 @@ class TestDispatcher : public QuicDispatcher { version_manager, QuicMakeUnique<MockQuicConnectionHelper>(), std::unique_ptr<QuicCryptoServerStream::Helper>( - new QuicSimpleCryptoServerStreamHelper(random)), + new QuicSimpleCryptoServerStreamHelper()), QuicMakeUnique<MockAlarmFactory>(), - kQuicDefaultConnectionIdLength) {} + kQuicDefaultConnectionIdLength), + random_(random) {} MOCK_METHOD4(CreateQuicSession, QuicServerSessionBase*(QuicConnectionId connection_id, @@ -152,6 +153,8 @@ class TestDispatcher : public QuicDispatcher { using QuicDispatcher::SetAllowShortInitialServerConnectionIds; using QuicDispatcher::writer; + + QuicRandom* random_; }; // A Connection class which unregisters the session from the dispatcher when @@ -187,9 +190,7 @@ class QuicDispatcherTest : public QuicTest { : QuicDispatcherTest(crypto_test_utils::ProofSourceForTesting()) {} explicit QuicDispatcherTest(std::unique_ptr<ProofSource> proof_source) - : - - version_manager_(AllSupportedVersions()), + : version_manager_(AllSupportedVersions()), crypto_config_(QuicCryptoServerConfig::TESTING, QuicRandom::GetInstance(), std::move(proof_source), @@ -524,13 +525,14 @@ TEST_F(QuicDispatcherTest, DispatcherDoesNotRejectPacketNumberZero) { } TEST_F(QuicDispatcherTest, StatelessVersionNegotiation) { + SetQuicReloadableFlag(quic_use_parse_public_header, true); CreateTimeWaitListManager(); QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _)).Times(0); EXPECT_CALL( *time_wait_list_manager_, - SendVersionNegotiationPacket(TestConnectionId(1), _, _, _, _, _, _)) + SendVersionNegotiationPacket(TestConnectionId(1), _, _, _, _, _, _, _)) .Times(1); // Pad the CHLO message with enough data to make the packet large enough // to trigger version negotiation. @@ -542,14 +544,14 @@ TEST_F(QuicDispatcherTest, StatelessVersionNegotiation) { } TEST_F(QuicDispatcherTest, StatelessVersionNegotiationWithClientConnectionId) { - SetQuicRestartFlag(quic_do_not_override_connection_id, true); + SetQuicReloadableFlag(quic_use_parse_public_header, true); CreateTimeWaitListManager(); QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _)).Times(0); EXPECT_CALL(*time_wait_list_manager_, - SendVersionNegotiationPacket(TestConnectionId(1), - TestConnectionId(2), _, _, _, _, _)) + SendVersionNegotiationPacket( + TestConnectionId(1), TestConnectionId(2), _, _, _, _, _, _)) .Times(1); // Pad the CHLO message with enough data to make the packet large enough // to trigger version negotiation. @@ -567,7 +569,7 @@ TEST_F(QuicDispatcherTest, NoVersionNegotiationWithSmallPacket) { EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _)).Times(0); EXPECT_CALL(*time_wait_list_manager_, - SendVersionNegotiationPacket(_, _, _, _, _, _, _)) + SendVersionNegotiationPacket(_, _, _, _, _, _, _, _)) .Times(0); std::string chlo = SerializeCHLO() + std::string(1200, 'a'); // Truncate to 1100 bytes of payload which results in a packet just @@ -583,6 +585,7 @@ TEST_F(QuicDispatcherTest, NoVersionNegotiationWithSmallPacket) { // Disabling CHLO size validation allows the dispatcher to send version // negotiation packets in response to a CHLO that is otherwise too small. TEST_F(QuicDispatcherTest, VersionNegotiationWithoutChloSizeValidation) { + SetQuicReloadableFlag(quic_use_parse_public_header, true); crypto_config_.set_validate_chlo_size(false); CreateTimeWaitListManager(); @@ -590,7 +593,7 @@ TEST_F(QuicDispatcherTest, VersionNegotiationWithoutChloSizeValidation) { EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _)).Times(0); EXPECT_CALL(*time_wait_list_manager_, - SendVersionNegotiationPacket(_, _, _, _, _, _, _)) + SendVersionNegotiationPacket(_, _, _, _, _, _, _, _)) .Times(1); std::string chlo = SerializeCHLO() + std::string(1200, 'a'); // Truncate to 1100 bytes of payload which results in a packet just @@ -747,7 +750,7 @@ TEST_F(QuicDispatcherTest, LongConnectionIdLengthReplaced) { QuicConnectionId bad_connection_id = TestConnectionIdNineBytesLong(2); QuicConnectionId fixed_connection_id = - QuicUtils::CreateRandomConnectionId(mock_helper_.GetRandomGenerator()); + QuicUtils::CreateReplacementConnectionId(bad_connection_id); EXPECT_CALL(*dispatcher_, CreateQuicSession(fixed_connection_id, client_address, @@ -780,7 +783,7 @@ TEST_F(QuicDispatcherTest, InvalidShortConnectionIdLengthReplaced) { QuicConnectionId bad_connection_id = EmptyQuicConnectionId(); QuicConnectionId fixed_connection_id = - QuicUtils::CreateRandomConnectionId(mock_helper_.GetRandomGenerator()); + QuicUtils::CreateReplacementConnectionId(bad_connection_id); // Disable validation of invalid short connection IDs. dispatcher_->SetAllowShortInitialServerConnectionIds(true); @@ -817,7 +820,7 @@ TEST_F(QuicDispatcherTest, MixGoodAndBadConnectionIdLengthPackets) { QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); QuicConnectionId bad_connection_id = TestConnectionIdNineBytesLong(2); QuicConnectionId fixed_connection_id = - QuicUtils::CreateRandomConnectionId(mock_helper_.GetRandomGenerator()); + QuicUtils::CreateReplacementConnectionId(bad_connection_id); EXPECT_CALL(*dispatcher_, CreateQuicSession(TestConnectionId(1), client_address, @@ -927,12 +930,13 @@ TEST_F(QuicDispatcherTest, OKSeqNoPacketProcessed) { } TEST_F(QuicDispatcherTest, SupportedTransportVersionsChangeInFlight) { - static_assert(QUIC_ARRAYSIZE(kSupportedTransportVersions) == 7u, + SetQuicRestartFlag(quic_dispatcher_hands_chlo_extractor_one_version, true); + SetQuicReloadableFlag(quic_use_parse_public_header, true); + static_assert(QUIC_ARRAYSIZE(kSupportedTransportVersions) == 6u, "Supported versions out of sync"); SetQuicReloadableFlag(quic_disable_version_39, false); - SetQuicReloadableFlag(quic_disable_version_44, false); SetQuicReloadableFlag(quic_enable_version_47, true); - SetQuicReloadableFlag(quic_enable_version_48, true); + SetQuicReloadableFlag(quic_enable_version_48_2, true); SetQuicReloadableFlag(quic_enable_version_99, true); VerifyVersionNotSupported(QuicVersionReservedForNegotiation()); @@ -942,12 +946,12 @@ TEST_F(QuicDispatcherTest, SupportedTransportVersionsChangeInFlight) { VerifyVersionSupported(QuicVersionMax()); // Turn off version 48. - SetQuicReloadableFlag(quic_enable_version_48, false); + SetQuicReloadableFlag(quic_enable_version_48_2, false); VerifyVersionNotSupported( ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_48)); // Turn on version 48. - SetQuicReloadableFlag(quic_enable_version_48, true); + SetQuicReloadableFlag(quic_enable_version_48_2, true); VerifyVersionSupported( ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_48)); @@ -961,16 +965,6 @@ TEST_F(QuicDispatcherTest, SupportedTransportVersionsChangeInFlight) { VerifyVersionSupported( ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_47)); - // Turn off version 44. - SetQuicReloadableFlag(quic_disable_version_44, true); - VerifyVersionNotSupported( - ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_44)); - - // Turn on version 44. - SetQuicReloadableFlag(quic_disable_version_44, false); - VerifyVersionSupported( - ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_44)); - // Turn off version 39. SetQuicReloadableFlag(quic_disable_version_39, true); VerifyVersionNotSupported( @@ -983,7 +977,7 @@ TEST_F(QuicDispatcherTest, SupportedTransportVersionsChangeInFlight) { } TEST_F(QuicDispatcherTest, RejectDeprecatedVersionsWithVersionNegotiation) { - static_assert(QUIC_ARRAYSIZE(kSupportedTransportVersions) == 7u, + static_assert(QUIC_ARRAYSIZE(kSupportedTransportVersions) == 6u, "Please add deprecated versions to this test"); QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); CreateTimeWaitListManager(); @@ -994,9 +988,345 @@ TEST_F(QuicDispatcherTest, RejectDeprecatedVersionsWithVersionNegotiation) { QuicTime::Zero()); EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _)).Times(0); EXPECT_CALL(*time_wait_list_manager_, - SendVersionNegotiationPacket(_, _, _, _, _, _, _)) + SendVersionNegotiationPacket(_, _, _, _, _, _, _, _)) .Times(1); dispatcher_->ProcessPacket(server_address_, client_address, packet); + + char packet44[kMinPacketSizeForVersionNegotiation] = { + 0xFF, 'Q', '0', '4', '4', /*connection ID length byte*/ 0x50}; + QuicReceivedPacket packet2(packet44, kMinPacketSizeForVersionNegotiation, + QuicTime::Zero()); + EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _)).Times(0); + EXPECT_CALL(*time_wait_list_manager_, + SendVersionNegotiationPacket(_, _, _, _, _, _, _, _)) + .Times(1); + dispatcher_->ProcessPacket(server_address_, client_address, packet2); +} + +TEST_F(QuicDispatcherTest, VersionNegotiationProbeOld) { + SetQuicFlag(FLAGS_quic_prober_uses_length_prefixed_connection_ids, false); + SetQuicReloadableFlag(quic_use_length_prefix_from_packet_info, true); + QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); + CreateTimeWaitListManager(); + char packet[1200]; + char destination_connection_id_bytes[] = {0x56, 0x4e, 0x20, 0x70, + 0x6c, 0x7a, 0x20, 0x21}; + EXPECT_TRUE(QuicFramer::WriteClientVersionNegotiationProbePacket( + packet, sizeof(packet), destination_connection_id_bytes, + sizeof(destination_connection_id_bytes))); + QuicEncryptedPacket encrypted(packet, sizeof(packet), false); + std::unique_ptr<QuicReceivedPacket> received_packet( + ConstructReceivedPacket(encrypted, mock_helper_.GetClock()->Now())); + QuicConnectionId client_connection_id = EmptyQuicConnectionId(); + QuicConnectionId server_connection_id( + destination_connection_id_bytes, sizeof(destination_connection_id_bytes)); + bool ietf_quic = true; + bool use_length_prefix = + GetQuicFlag(FLAGS_quic_prober_uses_length_prefixed_connection_ids); + EXPECT_CALL( + *time_wait_list_manager_, + SendVersionNegotiationPacket(server_connection_id, client_connection_id, + ietf_quic, use_length_prefix, _, _, _, _)) + .Times(1); + EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _)).Times(0); + + dispatcher_->ProcessPacket(server_address_, client_address, *received_packet); +} + +TEST_F(QuicDispatcherTest, VersionNegotiationProbe) { + SetQuicFlag(FLAGS_quic_prober_uses_length_prefixed_connection_ids, true); + SetQuicReloadableFlag(quic_use_parse_public_header, true); + SetQuicReloadableFlag(quic_use_length_prefix_from_packet_info, true); + QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); + CreateTimeWaitListManager(); + char packet[1200]; + char destination_connection_id_bytes[] = {0x56, 0x4e, 0x20, 0x70, + 0x6c, 0x7a, 0x20, 0x21}; + EXPECT_TRUE(QuicFramer::WriteClientVersionNegotiationProbePacket( + packet, sizeof(packet), destination_connection_id_bytes, + sizeof(destination_connection_id_bytes))); + QuicEncryptedPacket encrypted(packet, sizeof(packet), false); + std::unique_ptr<QuicReceivedPacket> received_packet( + ConstructReceivedPacket(encrypted, mock_helper_.GetClock()->Now())); + QuicConnectionId client_connection_id = EmptyQuicConnectionId(); + QuicConnectionId server_connection_id( + destination_connection_id_bytes, sizeof(destination_connection_id_bytes)); + bool ietf_quic = true; + bool use_length_prefix = + GetQuicFlag(FLAGS_quic_prober_uses_length_prefixed_connection_ids); + EXPECT_CALL( + *time_wait_list_manager_, + SendVersionNegotiationPacket(server_connection_id, client_connection_id, + ietf_quic, use_length_prefix, _, _, _, _)) + .Times(1); + EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _)).Times(0); + + dispatcher_->ProcessPacket(server_address_, client_address, *received_packet); +} + +// Testing packet writer that saves all packets instead of sending them. +// Useful for tests that need access to sent packets. +class SavingWriter : public QuicPacketWriterWrapper { + public: + bool IsWriteBlocked() const override { return false; } + + WriteResult WritePacket(const char* buffer, + size_t buf_len, + const QuicIpAddress& /*self_client_address*/, + const QuicSocketAddress& /*peer_client_address*/, + PerPacketOptions* /*options*/) override { + packets_.push_back( + QuicEncryptedPacket(buffer, buf_len, /*owns_buffer=*/false).Clone()); + return WriteResult(WRITE_STATUS_OK, buf_len); + } + + std::vector<std::unique_ptr<QuicEncryptedPacket>>* packets() { + return &packets_; + } + + private: + std::vector<std::unique_ptr<QuicEncryptedPacket>> packets_; +}; + +TEST_F(QuicDispatcherTest, VersionNegotiationProbeEndToEndOld) { + SetQuicFlag(FLAGS_quic_prober_uses_length_prefixed_connection_ids, false); + SetQuicReloadableFlag(quic_use_length_prefix_from_packet_info, true); + + SavingWriter* saving_writer = new SavingWriter(); + // dispatcher_ takes ownership of saving_writer. + QuicDispatcherPeer::UseWriter(dispatcher_.get(), saving_writer); + + QuicTimeWaitListManager* time_wait_list_manager = new QuicTimeWaitListManager( + saving_writer, dispatcher_.get(), mock_helper_.GetClock(), + &mock_alarm_factory_); + // dispatcher_ takes ownership of time_wait_list_manager. + QuicDispatcherPeer::SetTimeWaitListManager(dispatcher_.get(), + time_wait_list_manager); + char packet[1200] = {}; + char destination_connection_id_bytes[] = {0x56, 0x4e, 0x20, 0x70, + 0x6c, 0x7a, 0x20, 0x21}; + EXPECT_TRUE(QuicFramer::WriteClientVersionNegotiationProbePacket( + packet, sizeof(packet), destination_connection_id_bytes, + sizeof(destination_connection_id_bytes))); + QuicEncryptedPacket encrypted(packet, sizeof(packet), false); + std::unique_ptr<QuicReceivedPacket> received_packet( + ConstructReceivedPacket(encrypted, mock_helper_.GetClock()->Now())); + EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _)).Times(0); + + QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); + dispatcher_->ProcessPacket(server_address_, client_address, *received_packet); + ASSERT_EQ(1u, saving_writer->packets()->size()); + + char source_connection_id_bytes[255] = {}; + uint8_t source_connection_id_length = 0; + std::string detailed_error = "foobar"; + EXPECT_TRUE(QuicFramer::ParseServerVersionNegotiationProbeResponse( + (*(saving_writer->packets()))[0]->data(), + (*(saving_writer->packets()))[0]->length(), source_connection_id_bytes, + &source_connection_id_length, &detailed_error)); + EXPECT_EQ("", detailed_error); + + // The source connection ID of the probe response should match the + // destination connection ID of the probe request. + test::CompareCharArraysWithHexError( + "parsed probe", source_connection_id_bytes, source_connection_id_length, + destination_connection_id_bytes, sizeof(destination_connection_id_bytes)); +} + +TEST_F(QuicDispatcherTest, VersionNegotiationProbeEndToEnd) { + SetQuicFlag(FLAGS_quic_prober_uses_length_prefixed_connection_ids, true); + SetQuicReloadableFlag(quic_use_parse_public_header, true); + SetQuicReloadableFlag(quic_use_length_prefix_from_packet_info, true); + + SavingWriter* saving_writer = new SavingWriter(); + // dispatcher_ takes ownership of saving_writer. + QuicDispatcherPeer::UseWriter(dispatcher_.get(), saving_writer); + + QuicTimeWaitListManager* time_wait_list_manager = new QuicTimeWaitListManager( + saving_writer, dispatcher_.get(), mock_helper_.GetClock(), + &mock_alarm_factory_); + // dispatcher_ takes ownership of time_wait_list_manager. + QuicDispatcherPeer::SetTimeWaitListManager(dispatcher_.get(), + time_wait_list_manager); + char packet[1200] = {}; + char destination_connection_id_bytes[] = {0x56, 0x4e, 0x20, 0x70, + 0x6c, 0x7a, 0x20, 0x21}; + EXPECT_TRUE(QuicFramer::WriteClientVersionNegotiationProbePacket( + packet, sizeof(packet), destination_connection_id_bytes, + sizeof(destination_connection_id_bytes))); + QuicEncryptedPacket encrypted(packet, sizeof(packet), false); + std::unique_ptr<QuicReceivedPacket> received_packet( + ConstructReceivedPacket(encrypted, mock_helper_.GetClock()->Now())); + EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _)).Times(0); + + QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); + dispatcher_->ProcessPacket(server_address_, client_address, *received_packet); + ASSERT_EQ(1u, saving_writer->packets()->size()); + + char source_connection_id_bytes[255] = {}; + uint8_t source_connection_id_length = 0; + std::string detailed_error = "foobar"; + EXPECT_TRUE(QuicFramer::ParseServerVersionNegotiationProbeResponse( + (*(saving_writer->packets()))[0]->data(), + (*(saving_writer->packets()))[0]->length(), source_connection_id_bytes, + &source_connection_id_length, &detailed_error)); + EXPECT_EQ("", detailed_error); + + // The source connection ID of the probe response should match the + // destination connection ID of the probe request. + test::CompareCharArraysWithHexError( + "parsed probe", source_connection_id_bytes, source_connection_id_length, + destination_connection_id_bytes, sizeof(destination_connection_id_bytes)); +} + +TEST_F(QuicDispatcherTest, AndroidConformanceTestOld) { + // TODO(b/139691956) Remove this test once the workaround is removed. + // This test requires the workaround behind this flag to pass. + SetQuicReloadableFlag(quic_reply_to_old_android_conformance_test, true); + SavingWriter* saving_writer = new SavingWriter(); + // dispatcher_ takes ownership of saving_writer. + QuicDispatcherPeer::UseWriter(dispatcher_.get(), saving_writer); + + QuicTimeWaitListManager* time_wait_list_manager = new QuicTimeWaitListManager( + saving_writer, dispatcher_.get(), mock_helper_.GetClock(), + &mock_alarm_factory_); + // dispatcher_ takes ownership of time_wait_list_manager. + QuicDispatcherPeer::SetTimeWaitListManager(dispatcher_.get(), + time_wait_list_manager); + // clang-format off + static const unsigned char packet[] = { + // Android UDP network conformance test packet as it was before this change: + // https://android-review.googlesource.com/c/platform/cts/+/1104285 + 0x0c, // public flags: 8-byte connection ID, 1-byte packet number + 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, // 8-byte connection ID + 0x01, // 1-byte packet number + 0x00, // private flags + 0x07, // PING frame + }; + // clang-format on + + QuicEncryptedPacket encrypted(reinterpret_cast<const char*>(packet), + sizeof(packet), false); + std::unique_ptr<QuicReceivedPacket> received_packet( + ConstructReceivedPacket(encrypted, mock_helper_.GetClock()->Now())); + EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _)).Times(0); + + QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); + dispatcher_->ProcessPacket(server_address_, client_address, *received_packet); + ASSERT_EQ(1u, saving_writer->packets()->size()); + + // The Android UDP network conformance test directly checks that bytes 1-9 + // of the response match the connection ID that was sent. + static const char connection_id_bytes[] = {0x71, 0x72, 0x73, 0x74, + 0x75, 0x76, 0x77, 0x78}; + ASSERT_GE((*(saving_writer->packets()))[0]->length(), + 1u + sizeof(connection_id_bytes)); + test::CompareCharArraysWithHexError( + "response connection ID", &(*(saving_writer->packets()))[0]->data()[1], + sizeof(connection_id_bytes), connection_id_bytes, + sizeof(connection_id_bytes)); +} + +TEST_F(QuicDispatcherTest, AndroidConformanceTestNewWithWorkaround) { + // TODO(b/139691956) Remove this test once the workaround is removed. + // This test doesn't need the workaround but we make sure that it passes even + // when the flag is true, also see AndroidConformanceTest below. + SetQuicReloadableFlag(quic_reply_to_old_android_conformance_test, true); + SavingWriter* saving_writer = new SavingWriter(); + // dispatcher_ takes ownership of saving_writer. + QuicDispatcherPeer::UseWriter(dispatcher_.get(), saving_writer); + + QuicTimeWaitListManager* time_wait_list_manager = new QuicTimeWaitListManager( + saving_writer, dispatcher_.get(), mock_helper_.GetClock(), + &mock_alarm_factory_); + // dispatcher_ takes ownership of time_wait_list_manager. + QuicDispatcherPeer::SetTimeWaitListManager(dispatcher_.get(), + time_wait_list_manager); + // clang-format off + static const unsigned char packet[1200] = { + // Android UDP network conformance test packet as it was after this change: + // https://android-review.googlesource.com/c/platform/cts/+/1104285 + 0x0d, // public flags: version, 8-byte connection ID, 1-byte packet number + 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, // 8-byte connection ID + 0xaa, 0xda, 0xca, 0xaa, // reserved-space version number + 0x01, // 1-byte packet number + 0x00, // private flags + 0x07, // PING frame + }; + // clang-format on + + QuicEncryptedPacket encrypted(reinterpret_cast<const char*>(packet), + sizeof(packet), false); + std::unique_ptr<QuicReceivedPacket> received_packet( + ConstructReceivedPacket(encrypted, mock_helper_.GetClock()->Now())); + EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _)).Times(0); + + QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); + dispatcher_->ProcessPacket(server_address_, client_address, *received_packet); + ASSERT_EQ(1u, saving_writer->packets()->size()); + + // The Android UDP network conformance test directly checks that bytes 1-9 + // of the response match the connection ID that was sent. + static const char connection_id_bytes[] = {0x71, 0x72, 0x73, 0x74, + 0x75, 0x76, 0x77, 0x78}; + ASSERT_GE((*(saving_writer->packets()))[0]->length(), + 1u + sizeof(connection_id_bytes)); + test::CompareCharArraysWithHexError( + "response connection ID", &(*(saving_writer->packets()))[0]->data()[1], + sizeof(connection_id_bytes), connection_id_bytes, + sizeof(connection_id_bytes)); +} + +TEST_F(QuicDispatcherTest, AndroidConformanceTest) { + // WARNING: do not remove or modify this test without making sure that we + // still have adequate coverage for the Android conformance test. + + // Set the flag to false to make sure this test passes even when the + // workaround is disabled. + SetQuicReloadableFlag(quic_reply_to_old_android_conformance_test, false); + SavingWriter* saving_writer = new SavingWriter(); + // dispatcher_ takes ownership of saving_writer. + QuicDispatcherPeer::UseWriter(dispatcher_.get(), saving_writer); + + QuicTimeWaitListManager* time_wait_list_manager = new QuicTimeWaitListManager( + saving_writer, dispatcher_.get(), mock_helper_.GetClock(), + &mock_alarm_factory_); + // dispatcher_ takes ownership of time_wait_list_manager. + QuicDispatcherPeer::SetTimeWaitListManager(dispatcher_.get(), + time_wait_list_manager); + // clang-format off + static const unsigned char packet[1200] = { + // Android UDP network conformance test packet as it was after this change: + // https://android-review.googlesource.com/c/platform/cts/+/1104285 + 0x0d, // public flags: version, 8-byte connection ID, 1-byte packet number + 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, // 8-byte connection ID + 0xaa, 0xda, 0xca, 0xaa, // reserved-space version number + 0x01, // 1-byte packet number + 0x00, // private flags + 0x07, // PING frame + }; + // clang-format on + + QuicEncryptedPacket encrypted(reinterpret_cast<const char*>(packet), + sizeof(packet), false); + std::unique_ptr<QuicReceivedPacket> received_packet( + ConstructReceivedPacket(encrypted, mock_helper_.GetClock()->Now())); + EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _)).Times(0); + + QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); + dispatcher_->ProcessPacket(server_address_, client_address, *received_packet); + ASSERT_EQ(1u, saving_writer->packets()->size()); + + // The Android UDP network conformance test directly checks that bytes 1-9 + // of the response match the connection ID that was sent. + static const char connection_id_bytes[] = {0x71, 0x72, 0x73, 0x74, + 0x75, 0x76, 0x77, 0x78}; + ASSERT_GE((*(saving_writer->packets()))[0]->length(), + 1u + sizeof(connection_id_bytes)); + test::CompareCharArraysWithHexError( + "response connection ID", &(*(saving_writer->packets()))[0]->data()[1], + sizeof(connection_id_bytes), connection_id_bytes, + sizeof(connection_id_bytes)); } // Verify the stopgap test: Packets with truncated connection IDs should be diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_error_codes.cc b/chromium/net/third_party/quiche/src/quic/core/quic_error_codes.cc index f13eec4ae3a..1be1786297f 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_error_codes.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_error_codes.cc @@ -157,6 +157,7 @@ const char* QuicErrorCodeToString(QuicErrorCode error) { RETURN_STRING_LITERAL(QUIC_IETF_GQUIC_ERROR_MISSING); RETURN_STRING_LITERAL( QUIC_WINDOW_UPDATE_RECEIVED_ON_READ_UNIDIRECTIONAL_STREAM); + RETURN_STRING_LITERAL(QUIC_TOO_MANY_BUFFERED_CONTROL_FRAMES); RETURN_STRING_LITERAL(QUIC_LAST_ERROR); // Intentionally have no default case, so we'll break the build diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_error_codes.h b/chromium/net/third_party/quiche/src/quic/core/quic_error_codes.h index abc666df908..534d0b5e4c3 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_error_codes.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_error_codes.h @@ -334,8 +334,11 @@ enum QuicErrorCode { // Received WindowUpdate on a READ_UNIDIRECTIONAL stream. QUIC_WINDOW_UPDATE_RECEIVED_ON_READ_UNIDIRECTIONAL_STREAM = 123, + // There are too many buffered control frames in control frame manager. + QUIC_TOO_MANY_BUFFERED_CONTROL_FRAMES = 124, + // No error. Used as bound while iterating. - QUIC_LAST_ERROR = 124, + QUIC_LAST_ERROR = 125, }; // QuicErrorCodes is encoded as a single octet on-the-wire. static_assert(static_cast<int>(QUIC_LAST_ERROR) <= diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_framer.cc b/chromium/net/third_party/quiche/src/quic/core/quic_framer.cc index e9d6fa9f598..9890f3627a5 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_framer.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_framer.cc @@ -24,6 +24,7 @@ #include "net/third_party/quiche/src/quic/core/quic_data_reader.h" #include "net/third_party/quiche/src/quic/core/quic_data_writer.h" #include "net/third_party/quiche/src/quic/core/quic_error_codes.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" #include "net/third_party/quiche/src/quic/core/quic_socket_address_coder.h" #include "net/third_party/quiche/src/quic/core/quic_stream_frame_data_producer.h" #include "net/third_party/quiche/src/quic/core/quic_types.h" @@ -50,11 +51,6 @@ namespace { #define ENDPOINT \ (perspective_ == Perspective::IS_SERVER ? "Server: " : "Client: ") -// How much to shift the timestamp in the IETF Ack frame. -// TODO(fkastenholz) when we get real IETF QUIC, need to get -// the currect shift from the transport parameters. -const int kIetfAckTimestampShift = 3; - // Number of bits the packet number length bits are shifted from the right // edge of the header. const uint8_t kPublicHeaderSequenceNumberShift = 4; @@ -195,66 +191,25 @@ QuicPacketNumberLength ReadAckPacketNumberLength( } uint8_t PacketNumberLengthToOnWireValue( - QuicTransportVersion version, QuicPacketNumberLength packet_number_length) { - if (version > QUIC_VERSION_44) { - return packet_number_length - 1; - } - switch (packet_number_length) { - case PACKET_1BYTE_PACKET_NUMBER: - return 0; - case PACKET_2BYTE_PACKET_NUMBER: - return 1; - case PACKET_4BYTE_PACKET_NUMBER: - return 2; - default: - QUIC_BUG << "Invalid packet number length."; - return 0; - } + return packet_number_length - 1; } -bool GetShortHeaderPacketNumberLength( - QuicTransportVersion version, - uint8_t type, - bool infer_packet_header_type_from_version, - QuicPacketNumberLength* packet_number_length) { +QuicPacketNumberLength GetShortHeaderPacketNumberLength(uint8_t type) { DCHECK(!(type & FLAGS_LONG_HEADER)); - const bool two_bits_packet_number_length = - infer_packet_header_type_from_version ? version > QUIC_VERSION_44 - : (type & FLAGS_FIXED_BIT); - if (two_bits_packet_number_length) { - *packet_number_length = - static_cast<QuicPacketNumberLength>((type & 0x03) + 1); - return true; - } - switch (type & 0x07) { - case 0: - *packet_number_length = PACKET_1BYTE_PACKET_NUMBER; - break; - case 1: - *packet_number_length = PACKET_2BYTE_PACKET_NUMBER; - break; - case 2: - *packet_number_length = PACKET_4BYTE_PACKET_NUMBER; - break; - default: - *packet_number_length = PACKET_6BYTE_PACKET_NUMBER; - return false; - } - return true; + return static_cast<QuicPacketNumberLength>((type & 0x03) + 1); } -uint8_t LongHeaderTypeToOnWireValue(QuicTransportVersion version, - QuicLongHeaderType type) { +uint8_t LongHeaderTypeToOnWireValue(QuicLongHeaderType type) { switch (type) { case INITIAL: - return version > QUIC_VERSION_44 ? 0 : 0x7F; + return 0; case ZERO_RTT_PROTECTED: - return version > QUIC_VERSION_44 ? 1 << 4 : 0x7C; + return 1 << 4; case HANDSHAKE: - return version > QUIC_VERSION_44 ? 2 << 4 : 0x7D; + return 2 << 4; case RETRY: - return version > QUIC_VERSION_44 ? 3 << 4 : 0x7E; + return 3 << 4; case VERSION_NEGOTIATION: return 0xF0; // Value does not matter default: @@ -263,61 +218,31 @@ uint8_t LongHeaderTypeToOnWireValue(QuicTransportVersion version, } } -bool GetLongHeaderType(QuicTransportVersion version, - uint8_t type, - QuicLongHeaderType* long_header_type) { - DCHECK((type & FLAGS_LONG_HEADER) && version != QUIC_VERSION_UNSUPPORTED); - if (version > QUIC_VERSION_44) { - switch ((type & 0x30) >> 4) { - case 0: - *long_header_type = INITIAL; - break; - case 1: - *long_header_type = ZERO_RTT_PROTECTED; - break; - case 2: - *long_header_type = HANDSHAKE; - break; - case 3: - *long_header_type = RETRY; - break; - default: - QUIC_BUG << "Unreachable statement"; - *long_header_type = INVALID_PACKET_TYPE; - return false; - } - return true; - } - - switch (type & 0x7F) { - case 0x7F: +bool GetLongHeaderType(uint8_t type, QuicLongHeaderType* long_header_type) { + DCHECK((type & FLAGS_LONG_HEADER)); + switch ((type & 0x30) >> 4) { + case 0: *long_header_type = INITIAL; break; - case 0x7C: + case 1: *long_header_type = ZERO_RTT_PROTECTED; break; - case 0x7D: + case 2: *long_header_type = HANDSHAKE; break; - case 0x7E: + case 3: *long_header_type = RETRY; break; default: - // Invalid packet header type. Whether a packet is version negotiation is - // determined by the version field. + QUIC_BUG << "Unreachable statement"; *long_header_type = INVALID_PACKET_TYPE; return false; } return true; } -QuicPacketNumberLength GetLongHeaderPacketNumberLength( - QuicTransportVersion version, - uint8_t type) { - if (version > QUIC_VERSION_44) { - return static_cast<QuicPacketNumberLength>((type & 0x03) + 1); - } - return PACKET_4BYTE_PACKET_NUMBER; +QuicPacketNumberLength GetLongHeaderPacketNumberLength(uint8_t type) { + return static_cast<QuicPacketNumberLength>((type & 0x03) + 1); } // Used to get packet number space before packet gets decrypted. @@ -408,6 +333,7 @@ bool IsValidFullPacketNumber(uint64_t full_packet_number, } bool AppendIetfConnectionIds(bool version_flag, + bool use_length_prefix, QuicConnectionId destination_connection_id, QuicConnectionId source_connection_id, QuicDataWriter* writer) { @@ -415,6 +341,11 @@ bool AppendIetfConnectionIds(bool version_flag, return writer->WriteConnectionId(destination_connection_id); } + if (use_length_prefix) { + return writer->WriteLengthPrefixedConnectionId(destination_connection_id) && + writer->WriteLengthPrefixedConnectionId(source_connection_id); + } + // Compute connection ID length byte. uint8_t dcil = GetConnectionIdLengthValue( static_cast<QuicConnectionIdLength>(destination_connection_id.length())); @@ -454,6 +385,17 @@ PacketHeaderFormat GetIetfPacketHeaderFormat(uint8_t type_byte) { : IETF_QUIC_SHORT_HEADER_PACKET; } +std::string GenerateErrorString(std::string initial_error_string, + QuicErrorCode quic_error_code) { + if (quic_error_code == QUIC_IETF_GQUIC_ERROR_MISSING) { + // QUIC_IETF_GQUIC_ERROR_MISSING is special -- it means not to encode + // the error value in the string. + return initial_error_string; + } + return QuicStrCat(std::to_string(static_cast<unsigned>(quic_error_code)), ":", + initial_error_string); +} + } // namespace QuicFramer::QuicFramer(const ParsedQuicVersionVector& supported_versions, @@ -482,7 +424,10 @@ QuicFramer::QuicFramer(const ParsedQuicVersionVector& supported_versions, expected_server_connection_id_length), expected_client_connection_id_length_(0), supports_multiple_packet_number_spaces_(false), - last_written_packet_number_length_(0) { + last_written_packet_number_length_(0), + peer_ack_delay_exponent_(kDefaultAckDelayExponent), + local_ack_delay_exponent_(kDefaultAckDelayExponent), + current_received_frame_type_(0) { DCHECK(!supported_versions.empty()); version_ = supported_versions_[0]; decrypter_[ENCRYPTION_INITIAL] = QuicMakeUnique<NullDecrypter>(perspective); @@ -573,18 +518,19 @@ size_t QuicFramer::GetConnectionCloseFrameSize( kQuicErrorDetailsLengthSize + TruncatedErrorStringSize(frame.error_details); } - // TODO(fkastenholz): For complete support of IETF QUIC CONNECTION_CLOSE, - // check if the frame is a Transport close and if the frame's - // extracted_error_code is not QUIC_IETF_GQUIC_ERROR_MISSING. If so, - // extend the error string to include " QuicErrorCode: #" - const size_t truncated_error_string_size = - TruncatedErrorStringSize(frame.error_details); + + // Prepend the extra error information to the string and get the result's + // length. + const size_t truncated_error_string_size = TruncatedErrorStringSize( + GenerateErrorString(frame.error_details, frame.extracted_error_code)); + uint64_t close_code = 0; if (frame.close_type == IETF_QUIC_TRANSPORT_CONNECTION_CLOSE) { close_code = static_cast<uint64_t>(frame.transport_error_code); } else if (frame.close_type == IETF_QUIC_APPLICATION_CONNECTION_CLOSE) { close_code = static_cast<uint64_t>(frame.application_error_code); } + const size_t frame_size = truncated_error_string_size + QuicDataWriter::GetVarInt62Len(truncated_error_string_size) + @@ -592,7 +538,8 @@ size_t QuicFramer::GetConnectionCloseFrameSize( if (frame.close_type == IETF_QUIC_APPLICATION_CONNECTION_CLOSE) { return frame_size; } - // frame includes the transport_close_frame_type, so include its length. + // The Transport close frame has the transport_close_frame_type, so include + // its length. return frame_size + QuicDataWriter::GetVarInt62Len(frame.transport_close_frame_type); } @@ -1373,8 +1320,7 @@ std::unique_ptr<QuicEncryptedPacket> QuicFramer::BuildIetfStatelessResetPacket( type |= FLAGS_FIXED_BIT; type |= FLAGS_SHORT_HEADER_RESERVED_1; type |= FLAGS_SHORT_HEADER_RESERVED_2; - type |= PacketNumberLengthToOnWireValue(QUIC_VERSION_UNSUPPORTED, - PACKET_1BYTE_PACKET_NUMBER); + type |= PacketNumberLengthToOnWireValue(PACKET_1BYTE_PACKET_NUMBER); // Append type byte. if (!writer.WriteUInt8(type)) { @@ -1399,6 +1345,7 @@ std::unique_ptr<QuicEncryptedPacket> QuicFramer::BuildVersionNegotiationPacket( QuicConnectionId server_connection_id, QuicConnectionId client_connection_id, bool ietf_quic, + bool use_length_prefix, const ParsedQuicVersionVector& versions) { ParsedQuicVersionVector wire_versions = versions; if (!GetQuicReloadableFlag(quic_version_negotiation_grease)) { @@ -1431,11 +1378,14 @@ std::unique_ptr<QuicEncryptedPacket> QuicFramer::BuildVersionNegotiationPacket( } if (ietf_quic) { return BuildIetfVersionNegotiationPacket( - server_connection_id, client_connection_id, wire_versions); + use_length_prefix, server_connection_id, client_connection_id, + wire_versions); } // The GQUIC encoding does not support encoding client connection IDs. DCHECK(client_connection_id.IsEmpty()); + // The GQUIC encoding does not support length-prefixed connection IDs. + DCHECK(!use_length_prefix); DCHECK(!wire_versions.empty()); size_t len = kPublicFlagsSize + server_connection_id.length() + @@ -1467,17 +1417,25 @@ std::unique_ptr<QuicEncryptedPacket> QuicFramer::BuildVersionNegotiationPacket( // static std::unique_ptr<QuicEncryptedPacket> QuicFramer::BuildIetfVersionNegotiationPacket( + bool use_length_prefix, QuicConnectionId server_connection_id, QuicConnectionId client_connection_id, const ParsedQuicVersionVector& versions) { - QUIC_DVLOG(1) << "Building IETF version negotiation packet: " + QUIC_DVLOG(1) << "Building IETF version negotiation packet with" + << (use_length_prefix ? "" : "out") + << " length prefix, server_connection_id " + << server_connection_id << " client_connection_id " + << client_connection_id << " versions " << ParsedQuicVersionVectorToString(versions); - DCHECK(client_connection_id.IsEmpty() || - GetQuicRestartFlag(quic_do_not_override_connection_id)); DCHECK(!versions.empty()); size_t len = kPacketHeaderTypeSize + kConnectionIdLengthSize + client_connection_id.length() + server_connection_id.length() + (versions.size() + 1) * kQuicVersionSize; + if (use_length_prefix) { + // When using length-prefixed connection IDs, packets carry two lengths + // instead of one. + len += kConnectionIdLengthSize; + } std::unique_ptr<char[]> buffer(new char[len]); QuicDataWriter writer(len, buffer.get()); @@ -1491,8 +1449,8 @@ QuicFramer::BuildIetfVersionNegotiationPacket( return nullptr; } - if (!AppendIetfConnectionIds(true, client_connection_id, server_connection_id, - &writer)) { + if (!AppendIetfConnectionIds(true, use_length_prefix, client_connection_id, + server_connection_id, &writer)) { return nullptr; } @@ -1630,16 +1588,32 @@ bool QuicFramer::ProcessRetryPacket(QuicDataReader* reader, const QuicPacketHeader& header) { DCHECK_EQ(Perspective::IS_CLIENT, perspective_); - // Parse Original Destination Connection ID Length. - uint8_t odcil = header.type_byte & 0xf; - if (odcil != 0) { - odcil += kConnectionIdLengthAdjustment; + QuicConnectionId original_destination_connection_id; + if (version_.HasLengthPrefixedConnectionIds()) { + // Parse Original Destination Connection ID. + if (!reader->ReadLengthPrefixedConnectionId( + &original_destination_connection_id)) { + set_detailed_error("Unable to read Original Destination ConnectionId."); + return false; + } + } else { + // Parse Original Destination Connection ID Length. + uint8_t odcil = header.type_byte & 0xf; + if (odcil != 0) { + odcil += kConnectionIdLengthAdjustment; + } + + // Parse Original Destination Connection ID. + if (!reader->ReadConnectionId(&original_destination_connection_id, odcil)) { + set_detailed_error("Unable to read Original Destination ConnectionId."); + return false; + } } - // Parse Original Destination Connection ID. - QuicConnectionId original_destination_connection_id; - if (!reader->ReadConnectionId(&original_destination_connection_id, odcil)) { - set_detailed_error("Unable to read Original Destination ConnectionId."); + if (!QuicUtils::IsConnectionIdValidForVersion( + original_destination_connection_id, transport_version())) { + set_detailed_error( + "Received Original Destination ConnectionId with invalid length."); return false; } @@ -1649,42 +1623,6 @@ bool QuicFramer::ProcessRetryPacket(QuicDataReader* reader, return true; } -bool QuicFramer::MaybeProcessIetfInitialRetryToken( - QuicDataReader* encrypted_reader, - QuicPacketHeader* header) { - if (!QuicVersionHasLongHeaderLengths(header->version.transport_version) || - header->form != IETF_QUIC_LONG_HEADER_PACKET || - header->long_packet_type != INITIAL) { - return true; - } - uint64_t retry_token_length = 0; - header->retry_token_length_length = encrypted_reader->PeekVarInt62Length(); - if (!encrypted_reader->ReadVarInt62(&retry_token_length)) { - set_detailed_error("Unable to read INITIAL retry token length."); - return RaiseError(QUIC_INVALID_PACKET_HEADER); - } - header->retry_token = encrypted_reader->PeekRemainingPayload(); - // Safety check to avoid spending ressources if malformed. - // At this point header->retry_token contains the rest of the packet - // so its length() is the amount of data remaining in the packet. - if (retry_token_length > header->retry_token.length()) { - set_detailed_error("INITIAL token length longer than packet."); - return RaiseError(QUIC_INVALID_PACKET_HEADER); - } - // Resize retry_token to make it only contain the retry token. - header->retry_token.remove_suffix(header->retry_token.length() - - retry_token_length); - // Advance encrypted_reader by retry_token_length. - uint8_t wasted_byte; - for (uint64_t i = 0; i < retry_token_length; ++i) { - if (!encrypted_reader->ReadUInt8(&wasted_byte)) { - set_detailed_error("Unable to read INITIAL retry token."); - return RaiseError(QUIC_INVALID_PACKET_HEADER); - } - } - return true; -} - // Seeks the current packet to check for a coalesced packet at the end. // If the IETF length field only spans part of the outer packet, // then there is a coalesced packet after this one. @@ -1769,8 +1707,6 @@ bool QuicFramer::ProcessIetfDataPacket(QuicDataReader* encrypted_reader, size_t buffer_length) { DCHECK_NE(GOOGLE_QUIC_PACKET, header->form); DCHECK(!header->has_possible_stateless_reset_token); - header->retry_token_length_length = VARIABLE_LENGTH_INTEGER_LENGTH_0; - header->retry_token = QuicStringPiece(); header->length_length = VARIABLE_LENGTH_INTEGER_LENGTH_0; header->remaining_packet_length = 0; if (header->form == IETF_QUIC_SHORT_HEADER_PACKET && @@ -1787,10 +1723,6 @@ bool QuicFramer::ProcessIetfDataPacket(QuicDataReader* encrypted_reader, } } - if (!MaybeProcessIetfInitialRetryToken(encrypted_reader, header)) { - return false; - } - if (!MaybeProcessIetfLength(encrypted_reader, header)) { return false; } @@ -1840,6 +1772,16 @@ bool QuicFramer::ProcessIetfDataPacket(QuicDataReader* encrypted_reader, return true; } if (hp_removal_failed) { + if (GetQuicRestartFlag(quic_framer_uses_undecryptable_upcall)) { + QUIC_RESTART_FLAG_COUNT_N(quic_framer_uses_undecryptable_upcall, 5, + 7); + const EncryptionLevel decryption_level = GetEncryptionLevel(*header); + const bool has_decryption_key = + decrypter_[decryption_level] != nullptr; + visitor_->OnUndecryptablePacket( + QuicEncryptedPacket(encrypted_reader->FullPayload()), + decryption_level, has_decryption_key); + } set_detailed_error("Unable to decrypt header protection."); return RaiseError(QUIC_DECRYPTION_FAILURE); } @@ -1898,6 +1840,15 @@ bool QuicFramer::ProcessIetfDataPacket(QuicDataReader* encrypted_reader, visitor_->OnAuthenticatedIetfStatelessResetPacket(packet); return true; } + if (GetQuicRestartFlag(quic_framer_uses_undecryptable_upcall)) { + QUIC_RESTART_FLAG_COUNT_N(quic_framer_uses_undecryptable_upcall, 6, 7); + const EncryptionLevel decryption_level = GetEncryptionLevel(*header); + const bool has_decryption_key = version_.KnowsWhichDecrypterToUse() && + decrypter_[decryption_level] != nullptr; + visitor_->OnUndecryptablePacket( + QuicEncryptedPacket(encrypted_reader->FullPayload()), + decryption_level, has_decryption_key); + } set_detailed_error("Unable to decrypt payload."); RecordDroppedPacketReason(DroppedPacketReason::DECRYPTION_FAILURE); return RaiseError(QUIC_DECRYPTION_FAILURE); @@ -1927,13 +1878,16 @@ bool QuicFramer::ProcessIetfDataPacket(QuicDataReader* encrypted_reader, // Handle the payload. if (VersionHasIetfQuicFrames(version_.transport_version)) { + current_received_frame_type_ = 0; if (!ProcessIetfFrameData(&reader, *header)) { + current_received_frame_type_ = 0; DCHECK_NE(QUIC_NO_ERROR, error_); // ProcessIetfFrameData sets the error. DCHECK_NE("", detailed_error_); QUIC_DLOG(WARNING) << ENDPOINT << "Unable to process frame data. Error: " << detailed_error_; return false; } + current_received_frame_type_ = 0; } else { if (!ProcessFrameData(&reader, *header)) { DCHECK_NE(QUIC_NO_ERROR, error_); // ProcessFrameData sets the error. @@ -1976,6 +1930,16 @@ bool QuicFramer::ProcessDataPacket(QuicDataReader* encrypted_reader, EncryptionLevel decrypted_level; if (!DecryptPayload(encrypted, associated_data, *header, decrypted_buffer, buffer_length, &decrypted_length, &decrypted_level)) { + if (GetQuicRestartFlag(quic_framer_uses_undecryptable_upcall)) { + QUIC_RESTART_FLAG_COUNT_N(quic_framer_uses_undecryptable_upcall, 7, 7); + const EncryptionLevel decryption_level = decrypter_level_; + // This version uses trial decryption so we always report to our visitor + // that we are not certain we have the correct decryption key. + const bool has_decryption_key = false; + visitor_->OnUndecryptablePacket( + QuicEncryptedPacket(encrypted_reader->FullPayload()), + decryption_level, has_decryption_key); + } RecordDroppedPacketReason(DroppedPacketReason::DECRYPTION_FAILURE); set_detailed_error("Unable to decrypt payload."); return RaiseError(QUIC_DECRYPTION_FAILURE); @@ -2161,34 +2125,15 @@ bool QuicFramer::AppendPacketHeader(const QuicPacketHeader& header, bool QuicFramer::AppendIetfHeaderTypeByte(const QuicPacketHeader& header, QuicDataWriter* writer) { uint8_t type = 0; - if (transport_version() > QUIC_VERSION_44) { - if (header.version_flag) { - type = static_cast<uint8_t>( - FLAGS_LONG_HEADER | FLAGS_FIXED_BIT | - LongHeaderTypeToOnWireValue(transport_version(), - header.long_packet_type) | - PacketNumberLengthToOnWireValue(transport_version(), - header.packet_number_length)); - } else { - type = static_cast<uint8_t>( - FLAGS_FIXED_BIT | - PacketNumberLengthToOnWireValue(transport_version(), - header.packet_number_length)); - } - return writer->WriteUInt8(type); - } - if (header.version_flag) { type = static_cast<uint8_t>( - FLAGS_LONG_HEADER | LongHeaderTypeToOnWireValue( - transport_version(), header.long_packet_type)); - DCHECK_EQ(PACKET_4BYTE_PACKET_NUMBER, header.packet_number_length); + FLAGS_LONG_HEADER | FLAGS_FIXED_BIT | + LongHeaderTypeToOnWireValue(header.long_packet_type) | + PacketNumberLengthToOnWireValue(header.packet_number_length)); } else { - type |= FLAGS_SHORT_HEADER_RESERVED_1; - type |= FLAGS_SHORT_HEADER_RESERVED_2; - DCHECK_GE(PACKET_4BYTE_PACKET_NUMBER, header.packet_number_length); - type |= PacketNumberLengthToOnWireValue(transport_version(), - header.packet_number_length); + type = static_cast<uint8_t>( + FLAGS_FIXED_BIT | + PacketNumberLengthToOnWireValue(header.packet_number_length)); } return writer->WriteUInt8(type); } @@ -2218,7 +2163,7 @@ bool QuicFramer::AppendIetfPacketHeader(const QuicPacketHeader& header, // Append connection ID. if (!AppendIetfConnectionIds( - header.version_flag, + header.version_flag, version_.HasLengthPrefixedConnectionIds(), header.destination_connection_id_included != CONNECTION_ID_ABSENT ? header.destination_connection_id : EmptyQuicConnectionId(), @@ -2363,8 +2308,7 @@ bool QuicFramer::ProcessPublicHeader(QuicDataReader* reader, QuicConnectionId* header_connection_id = &header->destination_connection_id; QuicConnectionIdIncluded* header_connection_id_included = &header->destination_connection_id_included; - if (perspective_ == Perspective::IS_CLIENT && - GetQuicRestartFlag(quic_do_not_override_connection_id)) { + if (perspective_ == Perspective::IS_CLIENT) { header_connection_id = &header->source_connection_id; header_connection_id_included = &header->source_connection_id_included; } @@ -2550,7 +2494,7 @@ bool QuicFramer::ProcessIetfHeaderTypeByte(QuicDataReader* reader, QuicPacketHeader* header) { uint8_t type; if (!reader->ReadBytes(&type, 1)) { - set_detailed_error("Unable to read type."); + set_detailed_error("Unable to read first byte."); return false; } header->type_byte = type; @@ -2583,13 +2527,11 @@ bool QuicFramer::ProcessIetfHeaderTypeByte(QuicDataReader* reader, } else { header->version = ParseQuicVersionLabel(version_label); if (header->version.transport_version != QUIC_VERSION_UNSUPPORTED) { - if (header->version.transport_version > QUIC_VERSION_44 && - !(type & FLAGS_FIXED_BIT)) { + if (!(type & FLAGS_FIXED_BIT)) { set_detailed_error("Fixed bit is 0 in long header."); return false; } - if (!GetLongHeaderType(header->version.transport_version, type, - &header->long_packet_type)) { + if (!GetLongHeaderType(type, &header->long_packet_type)) { set_detailed_error("Illegal long header type value."); return false; } @@ -2603,8 +2545,7 @@ bool QuicFramer::ProcessIetfHeaderTypeByte(QuicDataReader* reader, return false; } } else if (!header->version.HasHeaderProtection()) { - header->packet_number_length = GetLongHeaderPacketNumberLength( - header->version.transport_version, type); + header->packet_number_length = GetLongHeaderPacketNumberLength(type); } } } @@ -2626,17 +2567,12 @@ bool QuicFramer::ProcessIetfHeaderTypeByte(QuicDataReader* reader, ? CONNECTION_ID_PRESENT : CONNECTION_ID_ABSENT; header->source_connection_id_included = CONNECTION_ID_ABSENT; - if (infer_packet_header_type_from_version_ && - transport_version() > QUIC_VERSION_44 && !(type & FLAGS_FIXED_BIT)) { + if (!(type & FLAGS_FIXED_BIT)) { set_detailed_error("Fixed bit is 0 in short header."); return false; } - if (!header->version.HasHeaderProtection() && - !GetShortHeaderPacketNumberLength(transport_version(), type, - infer_packet_header_type_from_version_, - &header->packet_number_length)) { - set_detailed_error("Illegal short header type value."); - return false; + if (!header->version.HasHeaderProtection()) { + header->packet_number_length = GetShortHeaderPacketNumberLength(type); } QUIC_DVLOG(1) << "packet_number_length = " << header->packet_number_length; return true; @@ -2702,8 +2638,98 @@ bool QuicFramer::ProcessAndValidateIetfConnectionIdLength( return true; } +bool QuicFramer::ValidateReceivedConnectionIds(const QuicPacketHeader& header) { + if (!QuicUtils::IsConnectionIdValidForVersion( + GetServerConnectionIdAsRecipient(header, perspective_), + transport_version())) { + set_detailed_error("Received server connection ID with invalid length."); + return false; + } + + if (version_.SupportsClientConnectionIds() && + !QuicUtils::IsConnectionIdValidForVersion( + GetClientConnectionIdAsRecipient(header, perspective_), + transport_version())) { + set_detailed_error("Received client connection ID with invalid length."); + return false; + } + return true; +} + bool QuicFramer::ProcessIetfPacketHeader(QuicDataReader* reader, QuicPacketHeader* header) { + if (version_.HasLengthPrefixedConnectionIds()) { + uint8_t expected_destination_connection_id_length = + perspective_ == Perspective::IS_CLIENT + ? expected_client_connection_id_length_ + : expected_server_connection_id_length_; + QuicVersionLabel version_label; + bool has_length_prefix; + std::string detailed_error; + QuicErrorCode parse_result = QuicFramer::ParsePublicHeader( + reader, expected_destination_connection_id_length, + VersionHasIetfInvariantHeader(version_.transport_version), + &header->type_byte, &header->form, &header->version_flag, + &has_length_prefix, &version_label, &header->version, + &header->destination_connection_id, &header->source_connection_id, + &header->long_packet_type, &header->retry_token_length_length, + &header->retry_token, &detailed_error); + if (parse_result != QUIC_NO_ERROR) { + set_detailed_error(detailed_error); + return false; + } + header->destination_connection_id_included = CONNECTION_ID_PRESENT; + header->source_connection_id_included = + header->version_flag ? CONNECTION_ID_PRESENT : CONNECTION_ID_ABSENT; + if (header->source_connection_id_included == CONNECTION_ID_ABSENT) { + DCHECK(header->source_connection_id.IsEmpty()); + if (perspective_ == Perspective::IS_CLIENT) { + header->source_connection_id = last_serialized_server_connection_id_; + } else { + header->source_connection_id = last_serialized_client_connection_id_; + } + } + + if (!ValidateReceivedConnectionIds(*header)) { + return false; + } + + if (header->version_flag && + header->long_packet_type != VERSION_NEGOTIATION && + !(header->type_byte & FLAGS_FIXED_BIT)) { + set_detailed_error("Fixed bit is 0 in long header."); + return false; + } + if (!header->version_flag && !(header->type_byte & FLAGS_FIXED_BIT)) { + set_detailed_error("Fixed bit is 0 in short header."); + return false; + } + if (!header->version_flag) { + if (!version_.HasHeaderProtection()) { + header->packet_number_length = + GetShortHeaderPacketNumberLength(header->type_byte); + } + return true; + } + if (header->long_packet_type == RETRY) { + if (!version().SupportsRetry()) { + set_detailed_error("RETRY not supported in this version."); + return false; + } + if (perspective_ == Perspective::IS_SERVER) { + set_detailed_error("Client-initiated RETRY is invalid."); + return false; + } + return true; + } + if (!header->version.HasHeaderProtection()) { + header->packet_number_length = + GetLongHeaderPacketNumberLength(header->type_byte); + } + + return true; + } + if (!ProcessIetfHeaderTypeByte(reader, header)) { return false; } @@ -2731,54 +2757,33 @@ bool QuicFramer::ProcessIetfPacketHeader(QuicDataReader* reader, } } - DCHECK_LE(destination_connection_id_length, kQuicMaxConnectionIdLength); - DCHECK_LE(source_connection_id_length, kQuicMaxConnectionIdLength); - // Read connection ID. if (!reader->ReadConnectionId(&header->destination_connection_id, destination_connection_id_length)) { - set_detailed_error("Unable to read Destination ConnectionId."); + set_detailed_error("Unable to read destination connection ID."); return false; } if (!reader->ReadConnectionId(&header->source_connection_id, source_connection_id_length)) { - set_detailed_error("Unable to read Source ConnectionId."); + set_detailed_error("Unable to read source connection ID."); return false; } - if (!GetQuicRestartFlag(quic_do_not_override_connection_id)) { - if (header->source_connection_id_included == CONNECTION_ID_PRESENT) { - DCHECK_EQ(Perspective::IS_CLIENT, perspective_); - DCHECK_EQ(IETF_QUIC_LONG_HEADER_PACKET, header->form); - if (!header->destination_connection_id.IsEmpty()) { - set_detailed_error("Client connection ID not supported yet."); - return false; - } - // Set destination connection ID to source connection ID. - header->destination_connection_id = header->source_connection_id; - } else if (header->destination_connection_id_included == - CONNECTION_ID_ABSENT) { - header->destination_connection_id = last_serialized_server_connection_id_; + if (header->source_connection_id_included == CONNECTION_ID_ABSENT) { + if (!header->source_connection_id.IsEmpty()) { + DCHECK(!version_.SupportsClientConnectionIds()); + set_detailed_error("Client connection ID not supported in this version."); + return false; } - } else { - QUIC_RESTART_FLAG_COUNT_N(quic_do_not_override_connection_id, 5, 7); - if (header->source_connection_id_included == CONNECTION_ID_ABSENT) { - if (!header->source_connection_id.IsEmpty()) { - DCHECK(!version_.SupportsClientConnectionIds()); - set_detailed_error( - "Client connection ID not supported in this version."); - return false; - } - if (perspective_ == Perspective::IS_CLIENT) { - header->source_connection_id = last_serialized_server_connection_id_; - } else { - header->source_connection_id = last_serialized_client_connection_id_; - } + if (perspective_ == Perspective::IS_CLIENT) { + header->source_connection_id = last_serialized_server_connection_id_; + } else { + header->source_connection_id = last_serialized_client_connection_id_; } } - return true; + return ValidateReceivedConnectionIds(*header); } bool QuicFramer::ProcessAndCalculatePacketNumber( @@ -2814,7 +2819,7 @@ bool QuicFramer::ProcessFrameData(QuicDataReader* reader, set_detailed_error("Unable to read frame type."); return RaiseError(QUIC_INVALID_FRAME_DATA); } - const uint8_t special_mask = transport_version() <= QUIC_VERSION_44 + const uint8_t special_mask = transport_version() <= QUIC_VERSION_43 ? kQuicFrameTypeBrokenMask : kQuicFrameTypeSpecialMask; if (frame_type & special_mask) { @@ -2945,7 +2950,7 @@ bool QuicFramer::ProcessFrameData(QuicDataReader* reader, case STOP_WAITING_FRAME: { if (GetQuicReloadableFlag(quic_do_not_accept_stop_waiting) && - version_.transport_version >= QUIC_VERSION_44) { + version_.transport_version > QUIC_VERSION_43) { QUIC_RELOADABLE_FLAG_COUNT(quic_do_not_accept_stop_waiting); set_detailed_error("STOP WAITING not supported in version 44+."); return RaiseError(QUIC_INVALID_STOP_WAITING_DATA); @@ -3044,6 +3049,7 @@ bool QuicFramer::ProcessIetfFrameData(QuicDataReader* reader, set_detailed_error("Unable to read frame type."); return RaiseError(QUIC_INVALID_FRAME_DATA); } + current_received_frame_type_ = frame_type; // Is now the number of bytes into which the frame type was encoded. encoded_bytes -= reader->BytesRemaining(); @@ -3753,12 +3759,10 @@ bool QuicFramer::ProcessIetfAckFrame(QuicDataReader* reader, return false; } - // TODO(fkastenholz) when we get real IETF QUIC, need to get - // the currect shift from the transport parameters. if (ack_delay_time_in_us == kVarInt62MaxValue) { ack_frame->ack_delay_time = QuicTime::Delta::Infinite(); } else { - ack_delay_time_in_us = (ack_delay_time_in_us << kIetfAckTimestampShift); + ack_delay_time_in_us = (ack_delay_time_in_us << peer_ack_delay_exponent_); ack_frame->ack_delay_time = QuicTime::Delta::FromMicroseconds(ack_delay_time_in_us); } @@ -3958,6 +3962,11 @@ bool QuicFramer::ProcessConnectionCloseFrame(QuicDataReader* reader, frame->quic_error_code = static_cast<QuicErrorCode>(error_code); + // For Google QUIC connection closes, copy the Google QUIC error code to + // the extracted error code field so that the Google QUIC error code is always + // available in extracted_error_code. + frame->extracted_error_code = frame->quic_error_code; + QuicStringPiece error_details; if (!reader->ReadStringPiece16(&error_details)) { set_detailed_error("Unable to read connection close error details."); @@ -4094,6 +4103,9 @@ void QuicFramer::SetDecrypter(EncryptionLevel level, DCHECK_EQ(alternative_decrypter_level_, NUM_ENCRYPTION_LEVELS); DCHECK_GE(level, decrypter_level_); DCHECK(!version_.KnowsWhichDecrypterToUse()); + QUIC_DVLOG(1) << ENDPOINT << "Setting decrypter from level " + << QuicUtils::EncryptionLevelToString(decrypter_level_) + << " to " << QuicUtils::EncryptionLevelToString(level); decrypter_[decrypter_level_] = nullptr; decrypter_[level] = std::move(decrypter); decrypter_level_ = level; @@ -4105,6 +4117,10 @@ void QuicFramer::SetAlternativeDecrypter( bool latch_once_used) { DCHECK_NE(level, decrypter_level_); DCHECK(!version_.KnowsWhichDecrypterToUse()); + QUIC_DVLOG(1) << ENDPOINT << "Setting alternative decrypter from level " + << QuicUtils::EncryptionLevelToString( + alternative_decrypter_level_) + << " to " << QuicUtils::EncryptionLevelToString(level); if (alternative_decrypter_level_ != NUM_ENCRYPTION_LEVELS) { decrypter_[alternative_decrypter_level_] = nullptr; } @@ -4116,11 +4132,15 @@ void QuicFramer::SetAlternativeDecrypter( void QuicFramer::InstallDecrypter(EncryptionLevel level, std::unique_ptr<QuicDecrypter> decrypter) { DCHECK(version_.KnowsWhichDecrypterToUse()); + QUIC_DVLOG(1) << ENDPOINT << "Installing decrypter at level " + << QuicUtils::EncryptionLevelToString(level); decrypter_[level] = std::move(decrypter); } void QuicFramer::RemoveDecrypter(EncryptionLevel level) { DCHECK(version_.KnowsWhichDecrypterToUse()); + QUIC_DVLOG(1) << ENDPOINT << "Removing decrypter at level " + << QuicUtils::EncryptionLevelToString(level); decrypter_[level] = nullptr; } @@ -4144,6 +4164,8 @@ void QuicFramer::SetEncrypter(EncryptionLevel level, std::unique_ptr<QuicEncrypter> encrypter) { DCHECK_GE(level, 0); DCHECK_LT(level, NUM_ENCRYPTION_LEVELS); + QUIC_DVLOG(1) << ENDPOINT << "Setting encrypter at level " + << QuicUtils::EncryptionLevelToString(level); encrypter_[level] = std::move(encrypter); } @@ -4231,8 +4253,7 @@ bool QuicFramer::ApplyHeaderProtection(EncryptionLevel level, QuicLongHeaderType header_type; if (IsLongHeader(type_byte)) { bitmask = 0x0f; - if (!GetLongHeaderType(version_.transport_version, type_byte, - &header_type)) { + if (!GetLongHeaderType(type_byte, &header_type)) { return false; } } @@ -4280,8 +4301,9 @@ bool QuicFramer::RemoveHeaderProtection(QuicDataReader* reader, QuicDecrypter* decrypter = decrypter_[expected_decryption_level].get(); if (decrypter == nullptr) { QUIC_DVLOG(1) + << ENDPOINT << "No decrypter available for removing header protection at level " - << expected_decryption_level; + << QuicUtils::EncryptionLevelToString(expected_decryption_level); return false; } @@ -4566,7 +4588,7 @@ size_t QuicFramer::GetIetfAckFrameSize(const QuicAckFrame& frame) { ack_frame_size += QuicDataWriter::GetVarInt62Len(largest_acked.ToUint64()); uint64_t ack_delay_time_us; ack_delay_time_us = frame.ack_delay_time.ToMicroseconds(); - ack_delay_time_us = ack_delay_time_us >> kIetfAckTimestampShift; + ack_delay_time_us = ack_delay_time_us >> local_ack_delay_exponent_; ack_frame_size += QuicDataWriter::GetVarInt62Len(ack_delay_time_us); // If |ecn_counters_populated| is true and any of the ecn counters is non-0 @@ -5423,8 +5445,7 @@ bool QuicFramer::AppendIetfAckFrameAndTypeByte(const QuicAckFrame& frame, if (!frame.ack_delay_time.IsInfinite()) { DCHECK_LE(0u, frame.ack_delay_time.ToMicroseconds()); ack_delay_time_us = frame.ack_delay_time.ToMicroseconds(); - // TODO(fkastenholz): Use the shift from TLS transport parameters. - ack_delay_time_us = ack_delay_time_us >> kIetfAckTimestampShift; + ack_delay_time_us = ack_delay_time_us >> local_ack_delay_exponent_; } if (!writer->WriteVarInt62(ack_delay_time_us)) { @@ -5695,12 +5716,13 @@ bool QuicFramer::AppendIetfConnectionCloseFrame( } } - // TODO(fkastenholz): For full IETF CONNECTION CLOSE support, - // if this is a Transport CONNECTION_CLOSE and the extended - // error is not QUIC_IETF_GQUIC_ERROR_MISSING then append the extended - // "QuicErrorCode: #" string to the phrase. + // There may be additional error information available in the extracted error + // code. Encode the error information in the reason phrase and serialize the + // result. + std::string final_error_string = + GenerateErrorString(frame.error_details, frame.extracted_error_code); if (!writer->WriteStringPieceVarInt62( - TruncateErrorString(frame.error_details))) { + TruncateErrorString(final_error_string))) { set_detailed_error("Can not write connection close phrase"); return false; } @@ -5713,11 +5735,14 @@ bool QuicFramer::ProcessIetfConnectionCloseFrame( QuicConnectionCloseFrame* frame) { frame->close_type = type; uint64_t error_code; + if (!reader->ReadVarInt62(&error_code)) { set_detailed_error("Unable to read connection close error code."); return false; } + // TODO(fkastenholz): When error codes uniformly go to uint64, remove the + // range check. if (frame->close_type == IETF_QUIC_TRANSPORT_CONNECTION_CLOSE) { if (error_code > 0xffff) { frame->transport_error_code = @@ -5736,6 +5761,7 @@ bool QuicFramer::ProcessIetfConnectionCloseFrame( frame->application_error_code = static_cast<uint16_t>(error_code); } } + if (type == IETF_QUIC_TRANSPORT_CONNECTION_CLOSE) { // The frame-type of the frame causing the error is present only // if it's a CONNECTION_CLOSE/Transport. @@ -5750,16 +5776,18 @@ bool QuicFramer::ProcessIetfConnectionCloseFrame( set_detailed_error("Unable to read connection close error details."); return false; } + QuicStringPiece phrase; if (!reader->ReadStringPiece(&phrase, static_cast<size_t>(phrase_length))) { set_detailed_error("Unable to read connection close error details."); return false; } - // TODO(fkastenholz): when full support is done, add code here - // to extract the extended error code from the reason phrase - // and set it into frame->extracted_error_code. frame->error_details = std::string(phrase); + // The frame may have an extracted error code in it. Look for it and + // extract it. If it's not present, MaybeExtract will return + // QUIC_IETF_GQUIC_ERROR_MISSING. + MaybeExtractQuicErrorCode(frame); return true; } @@ -6090,11 +6118,6 @@ bool QuicFramer::ProcessNewConnectionIdFrame(QuicDataReader* reader, return false; } - if (frame->connection_id.length() > kQuicMaxConnectionIdLength) { - set_detailed_error("New connection ID length too high."); - return false; - } - if (!QuicUtils::IsConnectionIdValidForVersion(frame->connection_id, transport_version())) { set_detailed_error("Invalid new connection ID length for version."); @@ -6208,6 +6231,7 @@ QuicErrorCode QuicFramer::ProcessPacketDispatcher( QuicConnectionId* destination_connection_id, QuicConnectionId* source_connection_id, std::string* detailed_error) { + DCHECK(!GetQuicReloadableFlag(quic_use_parse_public_header)); QuicDataReader reader(packet.data(), packet.length()); *source_connection_id = EmptyQuicConnectionId(); @@ -6270,8 +6294,7 @@ QuicErrorCode QuicFramer::ProcessPacketDispatcher( return QUIC_INVALID_PACKET_HEADER; } // Read source connection ID. - if (GetQuicRestartFlag(quic_do_not_override_connection_id) && - !reader.ReadConnectionId(source_connection_id, + if (!reader.ReadConnectionId(source_connection_id, source_connection_id_length)) { *detailed_error = "Unable to read source connection ID."; return QUIC_INVALID_PACKET_HEADER; @@ -6280,6 +6303,271 @@ QuicErrorCode QuicFramer::ProcessPacketDispatcher( } // static +QuicErrorCode QuicFramer::ParsePublicHeaderDispatcher( + const QuicEncryptedPacket& packet, + uint8_t expected_destination_connection_id_length, + PacketHeaderFormat* format, + bool* version_present, + bool* has_length_prefix, + QuicVersionLabel* version_label, + ParsedQuicVersion* parsed_version, + QuicConnectionId* destination_connection_id, + QuicConnectionId* source_connection_id, + bool* retry_token_present, + QuicStringPiece* retry_token, + std::string* detailed_error) { + QuicDataReader reader(packet.data(), packet.length()); + if (reader.IsDoneReading()) { + *detailed_error = "Unable to read first byte."; + return QUIC_INVALID_PACKET_HEADER; + } + const uint8_t first_byte = reader.PeekByte(); + const bool ietf_format = QuicUtils::IsIetfPacketHeader(first_byte); + uint8_t unused_first_byte; + QuicVariableLengthIntegerLength retry_token_length_length; + QuicLongHeaderType unused_log_packet_type; + const QuicErrorCode error_code = ParsePublicHeader( + &reader, expected_destination_connection_id_length, ietf_format, + &unused_first_byte, format, version_present, has_length_prefix, + version_label, parsed_version, destination_connection_id, + source_connection_id, &unused_log_packet_type, &retry_token_length_length, + retry_token, detailed_error); + *retry_token_present = + retry_token_length_length != VARIABLE_LENGTH_INTEGER_LENGTH_0; + return error_code; +} + +// static +QuicErrorCode QuicFramer::ParsePublicHeaderGoogleQuic( + QuicDataReader* reader, + uint8_t* first_byte, + PacketHeaderFormat* format, + bool* version_present, + QuicVersionLabel* version_label, + ParsedQuicVersion* parsed_version, + QuicConnectionId* destination_connection_id, + std::string* detailed_error) { + *format = GOOGLE_QUIC_PACKET; + *version_present = (*first_byte & PACKET_PUBLIC_FLAGS_VERSION) != 0; + uint8_t destination_connection_id_length = 0; + if ((*first_byte & PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID) != 0) { + destination_connection_id_length = kQuicDefaultConnectionIdLength; + } + if (!reader->ReadConnectionId(destination_connection_id, + destination_connection_id_length)) { + *detailed_error = "Unable to read ConnectionId."; + return QUIC_INVALID_PACKET_HEADER; + } + if (*version_present) { + if (!ProcessVersionLabel(reader, version_label)) { + *detailed_error = "Unable to read protocol version."; + return QUIC_INVALID_PACKET_HEADER; + } + *parsed_version = ParseQuicVersionLabel(*version_label); + } + return QUIC_NO_ERROR; +} + +namespace { + +inline bool PacketHasLengthPrefixedConnectionIds( + const QuicDataReader& reader, + ParsedQuicVersion parsed_version, + QuicVersionLabel version_label, + uint8_t first_byte) { + if (parsed_version.transport_version != QUIC_VERSION_UNSUPPORTED) { + return parsed_version.HasLengthPrefixedConnectionIds(); + } + + // Received unsupported version, check known old unsupported versions. + if (QuicVersionLabelUses4BitConnectionIdLength(version_label)) { + return false; + } + + // Received unknown version, check connection ID length byte. + if (reader.IsDoneReading()) { + // This check is required to safely peek the connection ID length byte. + return true; + } + const uint8_t connection_id_length_byte = reader.PeekByte(); + + // Check for packets produced by older versions of + // QuicFramer::WriteClientVersionNegotiationProbePacket + if (first_byte == 0xc0 && (connection_id_length_byte & 0x0f) == 0 && + connection_id_length_byte >= 0x50 && version_label == 0xcabadaba) { + return false; + } + + // Check for munged packets with version tag PROX. + if ((connection_id_length_byte & 0x0f) == 0 && + connection_id_length_byte >= 0x20 && version_label == 0x50524F58) { + return false; + } + + return true; +} + +inline bool ParseLongHeaderConnectionIds( + QuicDataReader* reader, + bool has_length_prefix, + QuicConnectionId* destination_connection_id, + QuicConnectionId* source_connection_id, + std::string* detailed_error) { + if (has_length_prefix) { + if (!reader->ReadLengthPrefixedConnectionId(destination_connection_id)) { + *detailed_error = "Unable to read destination connection ID."; + return false; + } + if (!reader->ReadLengthPrefixedConnectionId(source_connection_id)) { + *detailed_error = "Unable to read source connection ID."; + return false; + } + } else { + // Parse connection ID lengths. + uint8_t connection_id_lengths_byte; + if (!reader->ReadUInt8(&connection_id_lengths_byte)) { + *detailed_error = "Unable to read connection ID lengths."; + return false; + } + uint8_t destination_connection_id_length = + (connection_id_lengths_byte & kDestinationConnectionIdLengthMask) >> 4; + if (destination_connection_id_length != 0) { + destination_connection_id_length += kConnectionIdLengthAdjustment; + } + uint8_t source_connection_id_length = + connection_id_lengths_byte & kSourceConnectionIdLengthMask; + if (source_connection_id_length != 0) { + source_connection_id_length += kConnectionIdLengthAdjustment; + } + + // Read destination connection ID. + if (!reader->ReadConnectionId(destination_connection_id, + destination_connection_id_length)) { + *detailed_error = "Unable to read destination connection ID."; + return false; + } + + // Read source connection ID. + if (!reader->ReadConnectionId(source_connection_id, + source_connection_id_length)) { + *detailed_error = "Unable to read source connection ID."; + return false; + } + } + return true; +} + +} // namespace + +// static +QuicErrorCode QuicFramer::ParsePublicHeader( + QuicDataReader* reader, + uint8_t expected_destination_connection_id_length, + bool ietf_format, + uint8_t* first_byte, + PacketHeaderFormat* format, + bool* version_present, + bool* has_length_prefix, + QuicVersionLabel* version_label, + ParsedQuicVersion* parsed_version, + QuicConnectionId* destination_connection_id, + QuicConnectionId* source_connection_id, + QuicLongHeaderType* long_packet_type, + QuicVariableLengthIntegerLength* retry_token_length_length, + QuicStringPiece* retry_token, + std::string* detailed_error) { + *version_present = false; + *has_length_prefix = false; + *version_label = 0; + *parsed_version = UnsupportedQuicVersion(); + *source_connection_id = EmptyQuicConnectionId(); + *long_packet_type = INVALID_PACKET_TYPE; + *retry_token_length_length = VARIABLE_LENGTH_INTEGER_LENGTH_0; + *retry_token = QuicStringPiece(); + *detailed_error = ""; + + if (!reader->ReadUInt8(first_byte)) { + *detailed_error = "Unable to read first byte."; + return QUIC_INVALID_PACKET_HEADER; + } + + if (!ietf_format) { + return ParsePublicHeaderGoogleQuic( + reader, first_byte, format, version_present, version_label, + parsed_version, destination_connection_id, detailed_error); + } + + *format = GetIetfPacketHeaderFormat(*first_byte); + + if (*format == IETF_QUIC_SHORT_HEADER_PACKET) { + // Read destination connection ID using + // expected_destination_connection_id_length to determine its length. + if (!reader->ReadConnectionId(destination_connection_id, + expected_destination_connection_id_length)) { + *detailed_error = "Unable to read destination connection ID."; + return QUIC_INVALID_PACKET_HEADER; + } + return QUIC_NO_ERROR; + } + + DCHECK_EQ(IETF_QUIC_LONG_HEADER_PACKET, *format); + *version_present = true; + if (!ProcessVersionLabel(reader, version_label)) { + *detailed_error = "Unable to read protocol version."; + return QUIC_INVALID_PACKET_HEADER; + } + + if (*version_label == 0) { + *long_packet_type = VERSION_NEGOTIATION; + } + + // Parse version. + *parsed_version = ParseQuicVersionLabel(*version_label); + + // Figure out which IETF QUIC invariants this packet follows. + *has_length_prefix = PacketHasLengthPrefixedConnectionIds( + *reader, *parsed_version, *version_label, *first_byte); + + // Parse connection IDs. + if (!ParseLongHeaderConnectionIds(reader, *has_length_prefix, + destination_connection_id, + source_connection_id, detailed_error)) { + return QUIC_INVALID_PACKET_HEADER; + } + + if (parsed_version->transport_version == QUIC_VERSION_UNSUPPORTED) { + // Skip parsing of long packet type and retry token for unknown versions. + return QUIC_NO_ERROR; + } + + // Parse long packet type. + if (!GetLongHeaderType(*first_byte, long_packet_type)) { + *detailed_error = "Unable to parse long packet type."; + return QUIC_INVALID_PACKET_HEADER; + } + + if (!parsed_version->SupportsRetry() || *long_packet_type != INITIAL) { + // Retry token is only present on initial packets for some versions. + return QUIC_NO_ERROR; + } + + *retry_token_length_length = reader->PeekVarInt62Length(); + uint64_t retry_token_length; + if (!reader->ReadVarInt62(&retry_token_length)) { + *retry_token_length_length = VARIABLE_LENGTH_INTEGER_LENGTH_0; + *detailed_error = "Unable to read retry token length."; + return QUIC_INVALID_PACKET_HEADER; + } + + if (!reader->ReadStringPiece(retry_token, retry_token_length)) { + *detailed_error = "Unable to read retry token."; + return QUIC_INVALID_PACKET_HEADER; + } + + return QUIC_NO_ERROR; +} + +// static bool QuicFramer::WriteClientVersionNegotiationProbePacket( char* packet_bytes, QuicByteCount packet_length, @@ -6294,20 +6582,23 @@ bool QuicFramer::WriteClientVersionNegotiationProbePacket( QUIC_BUG << "Invalid packet_length"; return false; } - if (destination_connection_id_length > kQuicMaxConnectionIdLength || + if (destination_connection_id_length > kQuicMaxConnectionId4BitLength || destination_connection_id_length < kQuicMinimumInitialConnectionIdLength) { QUIC_BUG << "Invalid connection_id_length"; return false; } + const bool use_length_prefix = + GetQuicFlag(FLAGS_quic_prober_uses_length_prefixed_connection_ids); + const uint8_t last_version_byte = use_length_prefix ? 0xda : 0xba; // clang-format off - static const unsigned char packet_start_bytes[] = { + const unsigned char packet_start_bytes[] = { // IETF long header with fixed bit set, type initial, all-0 encrypted bits. 0xc0, // Version, part of the IETF space reserved for negotiation. // This intentionally differs from QuicVersionReservedForNegotiation() // to allow differentiating them over the wire. - 0xca, 0xba, 0xda, 0xba, + 0xca, 0xba, 0xda, last_version_byte, }; // clang-format on static_assert(sizeof(packet_start_bytes) == 5, "bad packet_start_bytes size"); @@ -6319,8 +6610,9 @@ bool QuicFramer::WriteClientVersionNegotiationProbePacket( QuicConnectionId destination_connection_id(destination_connection_id_bytes, destination_connection_id_length); - if (!AppendIetfConnectionIds(/*version_flag=*/true, destination_connection_id, - EmptyQuicConnectionId(), &writer)) { + if (!AppendIetfConnectionIds( + /*version_flag=*/true, use_length_prefix, destination_connection_id, + EmptyQuicConnectionId(), &writer)) { QUIC_BUG << "Failed to write connection IDs"; return false; } @@ -6404,38 +6696,101 @@ bool QuicFramer::ParseServerVersionNegotiationProbeResponse( *detailed_error = "Packet is not a version negotiation packet"; return false; } - uint8_t expected_server_connection_id_length = 0, - destination_connection_id_length = 0, source_connection_id_length = 0; - if (!ProcessAndValidateIetfConnectionIdLength( - &reader, UnsupportedQuicVersion(), Perspective::IS_CLIENT, - /*should_update_expected_server_connection_id_length=*/true, - &expected_server_connection_id_length, - &destination_connection_id_length, &source_connection_id_length, - detailed_error)) { - return false; + const bool use_length_prefix = + GetQuicFlag(FLAGS_quic_prober_uses_length_prefixed_connection_ids); + QuicConnectionId destination_connection_id, source_connection_id; + if (use_length_prefix) { + if (!reader.ReadLengthPrefixedConnectionId(&destination_connection_id)) { + *detailed_error = "Failed to read destination connection ID"; + return false; + } + if (!reader.ReadLengthPrefixedConnectionId(&source_connection_id)) { + *detailed_error = "Failed to read source connection ID"; + return false; + } + } else { + uint8_t expected_server_connection_id_length = 0, + destination_connection_id_length = 0, + source_connection_id_length = 0; + if (!ProcessAndValidateIetfConnectionIdLength( + &reader, UnsupportedQuicVersion(), Perspective::IS_CLIENT, + /*should_update_expected_server_connection_id_length=*/true, + &expected_server_connection_id_length, + &destination_connection_id_length, &source_connection_id_length, + detailed_error)) { + return false; + } + if (!reader.ReadConnectionId(&destination_connection_id, + destination_connection_id_length)) { + *detailed_error = "Failed to read destination connection ID"; + return false; + } + if (!reader.ReadConnectionId(&source_connection_id, + source_connection_id_length)) { + *detailed_error = "Failed to read source connection ID"; + return false; + } } - if (destination_connection_id_length != 0) { + + if (destination_connection_id.length() != 0) { *detailed_error = "Received unexpected destination connection ID length"; return false; } - QuicConnectionId destination_connection_id, source_connection_id; - if (!reader.ReadConnectionId(&destination_connection_id, - destination_connection_id_length)) { - *detailed_error = "Failed to read destination connection ID"; - return false; - } - if (!reader.ReadConnectionId(&source_connection_id, - source_connection_id_length)) { - *detailed_error = "Failed to read source connection ID"; - return false; + + if (!use_length_prefix && source_connection_id.length() == 0) { + // We received a bad response due to b/139330014. + // Reparse the packet assuming length prefixes. + // This is a temporary client-side workaround until cl/263172621 is + // deployed on production servers. + // TODO(dschinazi): remove this client-side workaround once the server-side + // fix is deployed. + QuicDataReader reader2(packet_bytes, packet_length); + uint8_t type_byte2 = 0; + uint32_t version2 = 0; + QuicConnectionId destination_connection_id2, source_connection_id2; + if (reader2.ReadUInt8(&type_byte2) && reader2.ReadUInt32(&version2) && + reader2.ReadLengthPrefixedConnectionId(&destination_connection_id2) && + reader2.ReadLengthPrefixedConnectionId(&source_connection_id2) && + (type_byte2 & 0x80) != 0 && version2 == 0 && + destination_connection_id2.length() == 0 && + source_connection_id2.length() != 0) { + source_connection_id = source_connection_id2; + } } memcpy(source_connection_id_bytes, source_connection_id.data(), - source_connection_id_length); - *source_connection_id_length_out = source_connection_id_length; + source_connection_id.length()); + *source_connection_id_length_out = source_connection_id.length(); return true; } +// Look for and parse the error code from the "<quic_error_code>:" text that +// may be present at the start of the CONNECTION_CLOSE error details string. +// This text, inserted by the peer if it's using Google's QUIC implementation, +// contains additional error information that narrows down the exact error. If +// the string is not found, or is not properly formed, it returns +// ErrorCode::QUIC_IETF_GQUIC_ERROR_MISSING +void MaybeExtractQuicErrorCode(QuicConnectionCloseFrame* frame) { + std::vector<QuicStringPiece> ed = + QuicTextUtils::Split(frame->error_details, ':'); + uint64_t extracted_error_code; + if (ed.size() < 2 || !QuicTextUtils::IsAllDigits(ed[0]) || + !QuicTextUtils::StringToUint64(ed[0], &extracted_error_code)) { + frame->extracted_error_code = QUIC_IETF_GQUIC_ERROR_MISSING; + return; + } + // Return the error code (numeric) and the error details string without the + // error code prefix. Note that Split returns everything up to, but not + // including, the split character, so the length of ed[0] is just the number + // of digits in the error number. In removing the prefix, 1 is added to the + // length to account for the : + QuicStringPiece x = QuicStringPiece(frame->error_details); + x.remove_prefix(ed[0].length() + 1); + frame->error_details = std::string(x); + frame->extracted_error_code = + static_cast<QuicErrorCode>(extracted_error_code); +} + #undef ENDPOINT // undef for jumbo builds } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_framer.h b/chromium/net/third_party/quiche/src/quic/core/quic_framer.h index 2349a772257..f429a83d5d6 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_framer.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_framer.h @@ -15,6 +15,7 @@ #include "net/third_party/quiche/src/quic/core/crypto/quic_random.h" #include "net/third_party/quiche/src/quic/core/quic_connection_id.h" #include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" #include "net/third_party/quiche/src/quic/platform/api/quic_export.h" #include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" @@ -127,6 +128,13 @@ class QUIC_EXPORT_PRIVATE QuicFramerVisitorInterface { // own its internal buffer, the visitor should make a copy of it. virtual void OnCoalescedPacket(const QuicEncryptedPacket& packet) = 0; + // Called when the packet being processed failed to decrypt. + // |has_decryption_key| indicates whether the framer knew which decryption + // key to use for this packet and already had a suitable key. + virtual void OnUndecryptablePacket(const QuicEncryptedPacket& packet, + EncryptionLevel decryption_level, + bool has_decryption_key) = 0; + // Called when a StreamFrame has been parsed. virtual bool OnStreamFrame(const QuicStreamFrame& frame) = 0; @@ -387,6 +395,43 @@ class QUIC_EXPORT_PRIVATE QuicFramer { QuicConnectionId* source_connection_id, std::string* detailed_error); + // Parses the unencryoted fields in a QUIC header using |reader| as input, + // stores the result in the other parameters. + // |expected_destination_connection_id_length| is only used for short headers. + static QuicErrorCode ParsePublicHeader( + QuicDataReader* reader, + uint8_t expected_destination_connection_id_length, + bool ietf_format, + uint8_t* first_byte, + PacketHeaderFormat* format, + bool* version_present, + bool* has_length_prefix, + QuicVersionLabel* version_label, + ParsedQuicVersion* parsed_version, + QuicConnectionId* destination_connection_id, + QuicConnectionId* source_connection_id, + QuicLongHeaderType* long_packet_type, + QuicVariableLengthIntegerLength* retry_token_length_length, + QuicStringPiece* retry_token, + std::string* detailed_error); + + // Parses the unencryoted fields in |packet| and stores them in the other + // parameters. This can only be called on the server. + // |expected_destination_connection_id_length| is only used for short headers. + static QuicErrorCode ParsePublicHeaderDispatcher( + const QuicEncryptedPacket& packet, + uint8_t expected_destination_connection_id_length, + PacketHeaderFormat* format, + bool* version_present, + bool* has_length_prefix, + QuicVersionLabel* version_label, + ParsedQuicVersion* parsed_version, + QuicConnectionId* destination_connection_id, + QuicConnectionId* source_connection_id, + bool* retry_token_present, + QuicStringPiece* retry_token, + std::string* detailed_error); + // Serializes a packet containing |frames| into |buffer|. // Returns the length of the packet, which must not be longer than // |packet_length|. Returns 0 if it fails to serialize. @@ -437,10 +482,12 @@ class QUIC_EXPORT_PRIVATE QuicFramer { QuicConnectionId server_connection_id, QuicConnectionId client_connection_id, bool ietf_quic, + bool use_length_prefix, const ParsedQuicVersionVector& versions); // Returns a new IETF version negotiation packet. static std::unique_ptr<QuicEncryptedPacket> BuildIetfVersionNegotiationPacket( + bool use_length_prefix, QuicConnectionId server_connection_id, QuicConnectionId client_connection_id, const ParsedQuicVersionVector& versions); @@ -563,6 +610,10 @@ class QUIC_EXPORT_PRIVATE QuicFramer { return first_sending_packet_number_; } + uint64_t current_received_frame_type() const { + return current_received_frame_type_; + } + // The connection ID length the framer expects on incoming IETF short headers // on the server. uint8_t GetExpectedServerConnectionIdLength() { @@ -611,6 +662,18 @@ class QUIC_EXPORT_PRIVATE QuicFramer { uint8_t* source_connection_id_length_out, std::string* detailed_error); + void set_local_ack_delay_exponent(uint32_t exponent) { + local_ack_delay_exponent_ = exponent; + } + uint32_t local_ack_delay_exponent() const { + return local_ack_delay_exponent_; + } + + void set_peer_ack_delay_exponent(uint32_t exponent) { + peer_ack_delay_exponent_ = exponent; + } + uint32_t peer_ack_delay_exponent() const { return peer_ack_delay_exponent_; } + private: friend class test::QuicFramerPeer; @@ -678,9 +741,6 @@ class QUIC_EXPORT_PRIVATE QuicFramer { bool ProcessRetryPacket(QuicDataReader* reader, const QuicPacketHeader& header); - bool MaybeProcessIetfInitialRetryToken(QuicDataReader* encrypted_reader, - QuicPacketHeader* header); - void MaybeProcessCoalescedPacket(const QuicDataReader& encrypted_reader, uint64_t remaining_bytes_length, const QuicPacketHeader& header); @@ -816,6 +876,18 @@ class QUIC_EXPORT_PRIVATE QuicFramer { static AckFrameInfo GetAckFrameInfo(const QuicAckFrame& frame); + static QuicErrorCode ParsePublicHeaderGoogleQuic( + QuicDataReader* reader, + uint8_t* first_byte, + PacketHeaderFormat* format, + bool* version_present, + QuicVersionLabel* version_label, + ParsedQuicVersion* parsed_version, + QuicConnectionId* destination_connection_id, + std::string* detailed_error); + + bool ValidateReceivedConnectionIds(const QuicPacketHeader& header); + // The Append* methods attempt to write the provided header or frame using the // |writer|, and return true if successful. @@ -945,6 +1017,7 @@ class QUIC_EXPORT_PRIVATE QuicFramer { void set_error(QuicErrorCode error) { error_ = error; } void set_detailed_error(const char* error) { detailed_error_ = error; } + void set_detailed_error(std::string error) { detailed_error_ = error; } std::string detailed_error_; QuicFramerVisitorInterface* visitor_; @@ -1020,8 +1093,34 @@ class QUIC_EXPORT_PRIVATE QuicFramer { // The length in bytes of the last packet number written to an IETF-framed // packet. size_t last_written_packet_number_length_; + + // The amount to shift the ack timestamp in ACK frames. The default is 3. + // Local_ is the amount this node shifts timestamps in ACK frames it + // generates. it is sent to the peer in a transport parameter negotiation. + // Peer_ is the amount the peer shifts timestamps when it sends ACK frames to + // this node. This node "unshifts" by this amount. The value is received from + // the peer in the transport parameter negotiation. IETF QUIC only. + uint32_t peer_ack_delay_exponent_; + uint32_t local_ack_delay_exponent_; + + // The type of received IETF frame currently being processed. 0 when not + // processing a frame or when processing Google QUIC frames. Used to populate + // the Transport Connection Close when there is an error during frame + // processing. + uint64_t current_received_frame_type_; }; +// Look for and parse the error code from the "<quic_error_code>:" text that +// may be present at the start of the CONNECTION_CLOSE error details string. +// This text, inserted by the peer if it's using Google's QUIC implementation, +// contains additional error information that narrows down the exact error. The +// extracted error code and (possibly updated) error_details string are returned +// in |*frame|. If an error code is not found in the error details then the +// extracted_error_code is set to QuicErrorCode::QUIC_IETF_GQUIC_ERROR_MISSING. +// If there is an error code in the string then it is removed from the string. +QUIC_EXPORT_PRIVATE void MaybeExtractQuicErrorCode( + QuicConnectionCloseFrame* frame); + } // namespace quic #endif // QUICHE_QUIC_CORE_QUIC_FRAMER_H_ diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_framer_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_framer_test.cc index d73c4127267..b13f7e18b2d 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_framer_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_framer_test.cc @@ -20,6 +20,7 @@ #include "net/third_party/quiche/src/quic/core/quic_packets.h" #include "net/third_party/quiche/src/quic/core/quic_types.h" #include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/core/quic_versions.h" #include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" #include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h" #include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" @@ -196,12 +197,14 @@ class TestQuicVisitor : public QuicFramerVisitorInterface { void OnPublicResetPacket(const QuicPublicResetPacket& packet) override { public_reset_packet_ = QuicMakeUnique<QuicPublicResetPacket>((packet)); + EXPECT_EQ(0u, framer_->current_received_frame_type()); } void OnVersionNegotiationPacket( const QuicVersionNegotiationPacket& packet) override { version_negotiation_packet_ = QuicMakeUnique<QuicVersionNegotiationPacket>((packet)); + EXPECT_EQ(0u, framer_->current_received_frame_type()); } void OnRetryPacket(QuicConnectionId original_connection_id, @@ -212,39 +215,49 @@ class TestQuicVisitor : public QuicFramerVisitorInterface { retry_new_connection_id_ = QuicMakeUnique<QuicConnectionId>(new_connection_id); retry_token_ = QuicMakeUnique<std::string>(std::string(retry_token)); + EXPECT_EQ(0u, framer_->current_received_frame_type()); } bool OnProtocolVersionMismatch(ParsedQuicVersion received_version) override { QUIC_DLOG(INFO) << "QuicFramer Version Mismatch, version: " << received_version; ++version_mismatch_; - return true; + EXPECT_EQ(0u, framer_->current_received_frame_type()); + return false; } bool OnUnauthenticatedPublicHeader(const QuicPacketHeader& header) override { header_ = QuicMakeUnique<QuicPacketHeader>((header)); + EXPECT_EQ(0u, framer_->current_received_frame_type()); return accept_public_header_; } bool OnUnauthenticatedHeader(const QuicPacketHeader& /*header*/) override { + EXPECT_EQ(0u, framer_->current_received_frame_type()); return true; } - void OnDecryptedPacket(EncryptionLevel /*level*/) override {} + void OnDecryptedPacket(EncryptionLevel /*level*/) override { + EXPECT_EQ(0u, framer_->current_received_frame_type()); + } bool OnPacketHeader(const QuicPacketHeader& header) override { ++packet_count_; header_ = QuicMakeUnique<QuicPacketHeader>((header)); + EXPECT_EQ(0u, framer_->current_received_frame_type()); return accept_packet_; } void OnCoalescedPacket(const QuicEncryptedPacket& packet) override { - size_t coalesced_data_length = packet.length(); - char* coalesced_data = new char[coalesced_data_length]; - memcpy(coalesced_data, packet.data(), coalesced_data_length); - coalesced_packets_.push_back(QuicMakeUnique<QuicEncryptedPacket>( - coalesced_data, coalesced_data_length, - /*owns_buffer=*/true)); + coalesced_packets_.push_back(packet.Clone()); + } + + void OnUndecryptablePacket(const QuicEncryptedPacket& packet, + EncryptionLevel decryption_level, + bool has_decryption_key) override { + undecryptable_packets_.push_back(packet.Clone()); + undecryptable_decryption_levels_.push_back(decryption_level); + undecryptable_has_decryption_keys_.push_back(has_decryption_key); } bool OnStreamFrame(const QuicStreamFrame& frame) override { @@ -255,6 +268,12 @@ class TestQuicVisitor : public QuicFramerVisitorInterface { stream_data_.push_back(QuicWrapUnique(string_data)); stream_frames_.push_back(QuicMakeUnique<QuicStreamFrame>( frame.stream_id, frame.fin, frame.offset, *string_data)); + if (VersionHasIetfQuicFrames(transport_version_)) { + // Low order bits of type encode flags, ignore them for this test. + EXPECT_TRUE(IS_IETF_STREAM_FRAME(framer_->current_received_frame_type())); + } else { + EXPECT_EQ(0u, framer_->current_received_frame_type()); + } return true; } @@ -266,6 +285,11 @@ class TestQuicVisitor : public QuicFramerVisitorInterface { crypto_data_.push_back(QuicWrapUnique(string_data)); crypto_frames_.push_back(QuicMakeUnique<QuicCryptoFrame>( ENCRYPTION_INITIAL, frame.offset, *string_data)); + if (VersionHasIetfQuicFrames(transport_version_)) { + EXPECT_EQ(IETF_CRYPTO, framer_->current_received_frame_type()); + } else { + EXPECT_EQ(0u, framer_->current_received_frame_type()); + } return true; } @@ -276,12 +300,22 @@ class TestQuicVisitor : public QuicFramerVisitorInterface { ack_frame.largest_acked = largest_acked; ack_frame.ack_delay_time = ack_delay_time; ack_frames_.push_back(QuicMakeUnique<QuicAckFrame>(ack_frame)); + if (VersionHasIetfQuicFrames(transport_version_)) { + EXPECT_EQ(IETF_ACK, framer_->current_received_frame_type()); + } else { + EXPECT_EQ(0u, framer_->current_received_frame_type()); + } return true; } bool OnAckRange(QuicPacketNumber start, QuicPacketNumber end) override { DCHECK(!ack_frames_.empty()); ack_frames_[ack_frames_.size() - 1]->packets.AddRange(start, end); + if (VersionHasIetfQuicFrames(transport_version_)) { + EXPECT_EQ(IETF_ACK, framer_->current_received_frame_type()); + } else { + EXPECT_EQ(0u, framer_->current_received_frame_type()); + } return true; } @@ -289,6 +323,7 @@ class TestQuicVisitor : public QuicFramerVisitorInterface { QuicTime timestamp) override { ack_frames_[ack_frames_.size() - 1]->received_packet_times.push_back( std::make_pair(packet_number, timestamp)); + EXPECT_EQ(0u, framer_->current_received_frame_type()); return true; } @@ -297,17 +332,28 @@ class TestQuicVisitor : public QuicFramerVisitorInterface { bool OnStopWaitingFrame(const QuicStopWaitingFrame& frame) override { ++frame_count_; stop_waiting_frames_.push_back(QuicMakeUnique<QuicStopWaitingFrame>(frame)); + EXPECT_EQ(0u, framer_->current_received_frame_type()); return true; } bool OnPaddingFrame(const QuicPaddingFrame& frame) override { padding_frames_.push_back(QuicMakeUnique<QuicPaddingFrame>(frame)); + if (VersionHasIetfQuicFrames(transport_version_)) { + EXPECT_EQ(IETF_PADDING, framer_->current_received_frame_type()); + } else { + EXPECT_EQ(0u, framer_->current_received_frame_type()); + } return true; } bool OnPingFrame(const QuicPingFrame& frame) override { ++frame_count_; ping_frames_.push_back(QuicMakeUnique<QuicPingFrame>(frame)); + if (VersionHasIetfQuicFrames(transport_version_)) { + EXPECT_EQ(IETF_PING, framer_->current_received_frame_type()); + } else { + EXPECT_EQ(0u, framer_->current_received_frame_type()); + } return true; } @@ -315,6 +361,14 @@ class TestQuicVisitor : public QuicFramerVisitorInterface { ++frame_count_; message_frames_.push_back( QuicMakeUnique<QuicMessageFrame>(frame.data, frame.message_length)); + if (VersionHasIetfQuicFrames(transport_version_)) { + EXPECT_TRUE(IETF_EXTENSION_MESSAGE_NO_LENGTH == + framer_->current_received_frame_type() || + IETF_EXTENSION_MESSAGE == + framer_->current_received_frame_type()); + } else { + EXPECT_EQ(0u, framer_->current_received_frame_type()); + } return true; } @@ -322,71 +376,128 @@ class TestQuicVisitor : public QuicFramerVisitorInterface { bool OnRstStreamFrame(const QuicRstStreamFrame& frame) override { rst_stream_frame_ = frame; + if (VersionHasIetfQuicFrames(transport_version_)) { + EXPECT_EQ(IETF_RST_STREAM, framer_->current_received_frame_type()); + } else { + EXPECT_EQ(0u, framer_->current_received_frame_type()); + } return true; } bool OnConnectionCloseFrame(const QuicConnectionCloseFrame& frame) override { connection_close_frame_ = frame; + if (VersionHasIetfQuicFrames(transport_version_)) { + EXPECT_NE(GOOGLE_QUIC_CONNECTION_CLOSE, frame.close_type); + if (frame.close_type == IETF_QUIC_TRANSPORT_CONNECTION_CLOSE) { + EXPECT_EQ(IETF_CONNECTION_CLOSE, + framer_->current_received_frame_type()); + } else { + EXPECT_EQ(IETF_APPLICATION_CLOSE, + framer_->current_received_frame_type()); + } + } else { + EXPECT_EQ(0u, framer_->current_received_frame_type()); + } return true; } bool OnStopSendingFrame(const QuicStopSendingFrame& frame) override { stop_sending_frame_ = frame; + EXPECT_EQ(IETF_STOP_SENDING, framer_->current_received_frame_type()); + EXPECT_TRUE(VersionHasIetfQuicFrames(transport_version_)); return true; } bool OnPathChallengeFrame(const QuicPathChallengeFrame& frame) override { path_challenge_frame_ = frame; + EXPECT_EQ(IETF_PATH_CHALLENGE, framer_->current_received_frame_type()); + EXPECT_TRUE(VersionHasIetfQuicFrames(transport_version_)); return true; } bool OnPathResponseFrame(const QuicPathResponseFrame& frame) override { path_response_frame_ = frame; + EXPECT_EQ(IETF_PATH_RESPONSE, framer_->current_received_frame_type()); + EXPECT_TRUE(VersionHasIetfQuicFrames(transport_version_)); return true; } bool OnGoAwayFrame(const QuicGoAwayFrame& frame) override { goaway_frame_ = frame; + EXPECT_FALSE(VersionHasIetfQuicFrames(transport_version_)); + EXPECT_EQ(0u, framer_->current_received_frame_type()); return true; } bool OnMaxStreamsFrame(const QuicMaxStreamsFrame& frame) override { max_streams_frame_ = frame; + EXPECT_TRUE(VersionHasIetfQuicFrames(transport_version_)); + EXPECT_TRUE(IETF_MAX_STREAMS_UNIDIRECTIONAL == + framer_->current_received_frame_type() || + IETF_MAX_STREAMS_BIDIRECTIONAL == + framer_->current_received_frame_type()); return true; } bool OnStreamsBlockedFrame(const QuicStreamsBlockedFrame& frame) override { streams_blocked_frame_ = frame; + EXPECT_TRUE(VersionHasIetfQuicFrames(transport_version_)); + EXPECT_TRUE(IETF_STREAMS_BLOCKED_UNIDIRECTIONAL == + framer_->current_received_frame_type() || + IETF_STREAMS_BLOCKED_BIDIRECTIONAL == + framer_->current_received_frame_type()); return true; } bool OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) override { window_update_frame_ = frame; + if (VersionHasIetfQuicFrames(transport_version_)) { + EXPECT_TRUE(IETF_MAX_DATA == framer_->current_received_frame_type() || + IETF_MAX_STREAM_DATA == + framer_->current_received_frame_type()); + } else { + EXPECT_EQ(0u, framer_->current_received_frame_type()); + } return true; } bool OnBlockedFrame(const QuicBlockedFrame& frame) override { blocked_frame_ = frame; + if (VersionHasIetfQuicFrames(transport_version_)) { + EXPECT_TRUE(IETF_BLOCKED == framer_->current_received_frame_type() || + IETF_STREAM_BLOCKED == + framer_->current_received_frame_type()); + } else { + EXPECT_EQ(0u, framer_->current_received_frame_type()); + } return true; } bool OnNewConnectionIdFrame(const QuicNewConnectionIdFrame& frame) override { new_connection_id_ = frame; + EXPECT_EQ(IETF_NEW_CONNECTION_ID, framer_->current_received_frame_type()); + EXPECT_TRUE(VersionHasIetfQuicFrames(transport_version_)); return true; } bool OnRetireConnectionIdFrame( const QuicRetireConnectionIdFrame& frame) override { + EXPECT_EQ(IETF_RETIRE_CONNECTION_ID, + framer_->current_received_frame_type()); + EXPECT_TRUE(VersionHasIetfQuicFrames(transport_version_)); retire_connection_id_ = frame; return true; } bool OnNewTokenFrame(const QuicNewTokenFrame& frame) override { new_token_ = frame; + EXPECT_EQ(IETF_NEW_TOKEN, framer_->current_received_frame_type()); + EXPECT_TRUE(VersionHasIetfQuicFrames(transport_version_)); return true; } bool IsValidStatelessResetToken(QuicUint128 token) const override { + EXPECT_EQ(0u, framer_->current_received_frame_type()); return token == kTestStatelessResetToken; } @@ -394,6 +505,12 @@ class TestQuicVisitor : public QuicFramerVisitorInterface { const QuicIetfStatelessResetPacket& packet) override { stateless_reset_packet_ = QuicMakeUnique<QuicIetfStatelessResetPacket>(packet); + EXPECT_EQ(0u, framer_->current_received_frame_type()); + } + + void set_framer(QuicFramer* framer) { + framer_ = framer; + transport_version_ = framer->transport_version(); } // Counters from the visitor_ callbacks. @@ -420,6 +537,9 @@ class TestQuicVisitor : public QuicFramerVisitorInterface { std::vector<std::unique_ptr<QuicPingFrame>> ping_frames_; std::vector<std::unique_ptr<QuicMessageFrame>> message_frames_; std::vector<std::unique_ptr<QuicEncryptedPacket>> coalesced_packets_; + std::vector<std::unique_ptr<QuicEncryptedPacket>> undecryptable_packets_; + std::vector<EncryptionLevel> undecryptable_decryption_levels_; + std::vector<bool> undecryptable_has_decryption_keys_; QuicRstStreamFrame rst_stream_frame_; QuicConnectionCloseFrame connection_close_frame_; QuicStopSendingFrame stop_sending_frame_; @@ -435,6 +555,8 @@ class TestQuicVisitor : public QuicFramerVisitorInterface { QuicNewTokenFrame new_token_; std::vector<std::unique_ptr<std::string>> stream_data_; std::vector<std::unique_ptr<std::string>> crypto_data_; + QuicTransportVersion transport_version_; + QuicFramer* framer_; }; // Simple struct for defining a packet's content, and associated @@ -471,6 +593,7 @@ class QuicFramerTest : public QuicTestWithParam<ParsedQuicVersion> { framer_.set_visitor(&visitor_); framer_.InferPacketHeaderTypeFromVersion(); + visitor_.set_framer(&framer_); } void SetDecrypterLevel(EncryptionLevel level) { @@ -482,21 +605,9 @@ class QuicFramerTest : public QuicTestWithParam<ParsedQuicVersion> { } // Helper function to get unsigned char representation of the handshake - // protocol byte of the current QUIC version number. - unsigned char GetQuicVersionProtocolByte() { - return (CreateQuicVersionLabel(version_) >> 24) & 0xff; - } - - // Helper function to get unsigned char representation of digit in the - // units place of the current QUIC version number. - unsigned char GetQuicVersionDigitOnes() { - return CreateQuicVersionLabel(version_) & 0xff; - } - - // Helper function to get unsigned char representation of digit in the - // tens place of the current QUIC version number. - unsigned char GetQuicVersionDigitTens() { - return (CreateQuicVersionLabel(version_) >> 8) & 0xff; + // protocol byte at position |pos| of the current QUIC version number. + unsigned char GetQuicVersionByte(int pos) { + return (CreateQuicVersionLabel(version_) >> 8 * (3 - pos)) & 0xff; } bool CheckEncryption(QuicPacketNumber packet_number, QuicPacket* packet) { @@ -695,9 +806,9 @@ class QuicFramerTest : public QuicTestWithParam<ParsedQuicVersion> { // testing, and these byte arrays contain the QUIC version. This macro explodes // the 32-bit version into four bytes in network order. Since it uses methods of // QuicFramerTest, it is only valid to use this in a QuicFramerTest. -#define QUIC_VERSION_BYTES \ - GetQuicVersionProtocolByte(), '0', GetQuicVersionDigitTens(), \ - GetQuicVersionDigitOnes() +#define QUIC_VERSION_BYTES \ + GetQuicVersionByte(0), GetQuicVersionByte(1), GetQuicVersionByte(2), \ + GetQuicVersionByte(3) // Run all framer tests with all supported versions of QUIC. INSTANTIATE_TEST_SUITE_P(QuicFramerTests, @@ -830,14 +941,6 @@ TEST_P(QuicFramerTest, LargePacket) { // private flags 0x00, }; - unsigned char packet44[kMaxIncomingPacketSize + 1] = { - // type (short header 4 byte packet number) - 0x32, - // connection_id - 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, - // packet number - 0x78, 0x56, 0x34, 0x12, - }; unsigned char packet46[kMaxIncomingPacketSize + 1] = { // type (short header 4 byte packet number) 0x43, @@ -849,12 +952,9 @@ TEST_P(QuicFramerTest, LargePacket) { // clang-format on unsigned char* p = packet; size_t p_size = QUIC_ARRAYSIZE(packet); - if (framer_.transport_version() > QUIC_VERSION_44) { + if (framer_.transport_version() > QUIC_VERSION_43) { p = packet46; p_size = QUIC_ARRAYSIZE(packet46); - } else if (framer_.transport_version() > QUIC_VERSION_43) { - p = packet44; - p_size = QUIC_ARRAYSIZE(packet44); } const size_t header_size = GetPacketHeaderSize( @@ -917,11 +1017,25 @@ TEST_P(QuicFramerTest, PacketHeader) { QuicConnectionId destination_connection_id, source_connection_id; QuicVersionLabel version_label; std::string detailed_error; - EXPECT_EQ(QUIC_NO_ERROR, - QuicFramer::ProcessPacketDispatcher( - *encrypted, kQuicDefaultConnectionIdLength, &format, - &version_flag, &version_label, &destination_connection_id, - &source_connection_id, &detailed_error)); + QuicErrorCode error_code; + if (!GetQuicReloadableFlag(quic_use_parse_public_header)) { + error_code = QuicFramer::ProcessPacketDispatcher( + *encrypted, kQuicDefaultConnectionIdLength, &format, &version_flag, + &version_label, &destination_connection_id, &source_connection_id, + &detailed_error); + } else { + bool retry_token_present, use_length_prefix; + QuicStringPiece retry_token; + ParsedQuicVersion parsed_version = UnsupportedQuicVersion(); + error_code = QuicFramer::ParsePublicHeaderDispatcher( + *encrypted, kQuicDefaultConnectionIdLength, &format, &version_flag, + &use_length_prefix, &version_label, &parsed_version, + &destination_connection_id, &source_connection_id, &retry_token_present, + &retry_token, &detailed_error); + EXPECT_FALSE(retry_token_present); + EXPECT_FALSE(use_length_prefix); + } + EXPECT_EQ(QUIC_NO_ERROR, error_code); EXPECT_EQ(GOOGLE_QUIC_PACKET, format); EXPECT_FALSE(version_flag); EXPECT_EQ(kQuicDefaultConnectionIdLength, destination_connection_id.length()); @@ -931,26 +1045,9 @@ TEST_P(QuicFramerTest, PacketHeader) { TEST_P(QuicFramerTest, LongPacketHeader) { // clang-format off - PacketFragments packet44 = { - // type (long header with packet type INITIAL) - {"Unable to read type.", - {0xFF}}, - // version tag - {"Unable to read protocol version.", - {QUIC_VERSION_BYTES}}, - // connection_id length - {"Unable to read ConnectionId length.", - {0x50}}, - // connection_id - {"Unable to read Destination ConnectionId.", - {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, - // packet number - {"Unable to read packet number.", - {0x12, 0x34, 0x56, 0x78}}, - }; PacketFragments packet46 = { // type (long header with packet type INITIAL) - {"Unable to read type.", + {"Unable to read first byte.", {0xC3}}, // version tag {"Unable to read protocol version.", @@ -959,7 +1056,7 @@ TEST_P(QuicFramerTest, LongPacketHeader) { {"Unable to read ConnectionId length.", {0x50}}, // connection_id - {"Unable to read Destination ConnectionId.", + {"Unable to read destination connection ID.", {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, // packet number {"Unable to read packet number.", @@ -972,10 +1069,8 @@ TEST_P(QuicFramerTest, LongPacketHeader) { return; } - PacketFragments& fragments = - framer_.transport_version() > QUIC_VERSION_44 ? packet46 : packet44; std::unique_ptr<QuicEncryptedPacket> encrypted( - AssemblePacketFromFragments(fragments)); + AssemblePacketFromFragments(packet46)); EXPECT_FALSE(framer_.ProcessPacket(*encrypted)); EXPECT_EQ(QUIC_MISSING_PAYLOAD, framer_.error()); @@ -986,20 +1081,31 @@ TEST_P(QuicFramerTest, LongPacketHeader) { EXPECT_TRUE(visitor_.header_->version_flag); EXPECT_EQ(kPacketNumber, visitor_.header_->packet_number); - CheckFramingBoundaries( - framer_.transport_version() > QUIC_VERSION_44 ? packet46 : packet44, - QUIC_INVALID_PACKET_HEADER); + CheckFramingBoundaries(packet46, QUIC_INVALID_PACKET_HEADER); PacketHeaderFormat format; bool version_flag; QuicConnectionId destination_connection_id, source_connection_id; QuicVersionLabel version_label; std::string detailed_error; - EXPECT_EQ(QUIC_NO_ERROR, - QuicFramer::ProcessPacketDispatcher( - *encrypted, kQuicDefaultConnectionIdLength, &format, - &version_flag, &version_label, &destination_connection_id, - &source_connection_id, &detailed_error)); + QuicErrorCode error_code; + if (!GetQuicReloadableFlag(quic_use_parse_public_header)) { + error_code = QuicFramer::ProcessPacketDispatcher( + *encrypted, kQuicDefaultConnectionIdLength, &format, &version_flag, + &version_label, &destination_connection_id, &source_connection_id, + &detailed_error); + } else { + bool retry_token_present, use_length_prefix; + QuicStringPiece retry_token; + ParsedQuicVersion parsed_version = UnsupportedQuicVersion(); + error_code = QuicFramer::ParsePublicHeaderDispatcher( + *encrypted, kQuicDefaultConnectionIdLength, &format, &version_flag, + &use_length_prefix, &version_label, &parsed_version, + &destination_connection_id, &source_connection_id, &retry_token_present, + &retry_token, &detailed_error); + EXPECT_EQ(retry_token_present, framer_.version().SupportsRetry()); + EXPECT_FALSE(use_length_prefix); + } EXPECT_EQ(IETF_QUIC_LONG_HEADER_PACKET, format); EXPECT_TRUE(version_flag); EXPECT_EQ(kQuicDefaultConnectionIdLength, destination_connection_id.length()); @@ -1012,15 +1118,13 @@ TEST_P(QuicFramerTest, LongPacketHeaderWithBothConnectionIds) { // This test requires an IETF long header. return; } - SetQuicRestartFlag(quic_do_not_override_connection_id, true); + SetQuicReloadableFlag(quic_use_parse_public_header, false); SetDecrypterLevel(ENCRYPTION_ZERO_RTT); - const unsigned char type_byte = - framer_.transport_version() == QUIC_VERSION_44 ? 0xFC : 0xD3; // clang-format off unsigned char packet[] = { // public flags (long header with packet type ZERO_RTT_PROTECTED and // 4-byte packet number) - type_byte, + 0xD3, // version QUIC_VERSION_BYTES, // connection ID lengths @@ -1044,11 +1148,24 @@ TEST_P(QuicFramerTest, LongPacketHeaderWithBothConnectionIds) { QuicConnectionId destination_connection_id, source_connection_id; QuicVersionLabel version_label = 0; std::string detailed_error = ""; - EXPECT_EQ(QUIC_NO_ERROR, - QuicFramer::ProcessPacketDispatcher( - encrypted, kQuicDefaultConnectionIdLength, &format, - &version_flag, &version_label, &destination_connection_id, - &source_connection_id, &detailed_error)); + QuicErrorCode error_code; + if (!GetQuicReloadableFlag(quic_use_parse_public_header)) { + error_code = QuicFramer::ProcessPacketDispatcher( + encrypted, kQuicDefaultConnectionIdLength, &format, &version_flag, + &version_label, &destination_connection_id, &source_connection_id, + &detailed_error); + } else { + bool retry_token_present, use_length_prefix; + QuicStringPiece retry_token; + ParsedQuicVersion parsed_version = UnsupportedQuicVersion(); + error_code = QuicFramer::ParsePublicHeaderDispatcher( + encrypted, kQuicDefaultConnectionIdLength, &format, &version_flag, + &use_length_prefix, &version_label, &parsed_version, + &destination_connection_id, &source_connection_id, &retry_token_present, + &retry_token, &detailed_error); + EXPECT_FALSE(retry_token_present); + EXPECT_FALSE(use_length_prefix); + } EXPECT_EQ("", detailed_error); EXPECT_EQ(IETF_QUIC_LONG_HEADER_PACKET, format); EXPECT_TRUE(version_flag); @@ -1056,8 +1173,111 @@ TEST_P(QuicFramerTest, LongPacketHeaderWithBothConnectionIds) { EXPECT_EQ(FramerTestConnectionIdPlusOne(), source_connection_id); } +TEST_P(QuicFramerTest, ParsePublicHeader) { + // clang-format off + unsigned char packet[] = { + // public flags (version included, 8-byte connection ID, + // 4-byte packet number) + 0x29, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // version + QUIC_VERSION_BYTES, + // packet number + 0x12, 0x34, 0x56, 0x78, + // padding frame + 0x00, + }; + unsigned char packet46[] = { + // public flags (long header with packet type HANDSHAKE and + // 4-byte packet number) + 0xE3, + // version + QUIC_VERSION_BYTES, + // connection ID lengths + 0x50, + // destination connection ID + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // long header packet length + 0x05, + // packet number + 0x12, 0x34, 0x56, 0x78, + // padding frame + 0x00, + }; + unsigned char packet99[] = { + // public flags (long header with packet type HANDSHAKE and + // 4-byte packet number) + 0xE3, + // version + QUIC_VERSION_BYTES, + // destination connection ID length + 0x08, + // destination connection ID + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // source connection ID length + 0x00, + // long header packet length + 0x05, + // packet number + 0x12, 0x34, 0x56, 0x78, + // padding frame + 0x00, + }; + // clang-format on + unsigned char* p = packet; + size_t p_length = QUIC_ARRAYSIZE(packet); + if (framer_.transport_version() == QUIC_VERSION_99) { + p = packet99; + p_length = QUIC_ARRAYSIZE(packet99); + } else if (framer_.transport_version() >= QUIC_VERSION_46) { + p = packet46; + p_length = QUIC_ARRAYSIZE(packet46); + } + + uint8_t first_byte = 0x33; + PacketHeaderFormat format = GOOGLE_QUIC_PACKET; + bool version_present = false, has_length_prefix = false; + QuicVersionLabel version_label = 0; + ParsedQuicVersion parsed_version = UnsupportedQuicVersion(); + QuicConnectionId destination_connection_id = EmptyQuicConnectionId(), + source_connection_id = EmptyQuicConnectionId(); + QuicLongHeaderType long_packet_type = INVALID_PACKET_TYPE; + QuicVariableLengthIntegerLength retry_token_length_length = + VARIABLE_LENGTH_INTEGER_LENGTH_4; + QuicStringPiece retry_token; + std::string detailed_error = "foobar"; + + QuicDataReader reader(AsChars(p), p_length); + const QuicErrorCode parse_error = QuicFramer::ParsePublicHeader( + &reader, kQuicDefaultConnectionIdLength, + /*ietf_format=*/ + VersionHasIetfInvariantHeader(framer_.transport_version()), &first_byte, + &format, &version_present, &has_length_prefix, &version_label, + &parsed_version, &destination_connection_id, &source_connection_id, + &long_packet_type, &retry_token_length_length, &retry_token, + &detailed_error); + EXPECT_EQ(QUIC_NO_ERROR, parse_error); + EXPECT_EQ("", detailed_error); + EXPECT_EQ(p[0], first_byte); + EXPECT_TRUE(version_present); + EXPECT_EQ(framer_.version().HasLengthPrefixedConnectionIds(), + has_length_prefix); + EXPECT_EQ(CreateQuicVersionLabel(framer_.version()), version_label); + EXPECT_EQ(framer_.version(), parsed_version); + EXPECT_EQ(FramerTestConnectionId(), destination_connection_id); + EXPECT_EQ(EmptyQuicConnectionId(), source_connection_id); + EXPECT_EQ(VARIABLE_LENGTH_INTEGER_LENGTH_0, retry_token_length_length); + EXPECT_EQ(QuicStringPiece(), retry_token); + if (VersionHasIetfInvariantHeader(framer_.transport_version())) { + EXPECT_EQ(IETF_QUIC_LONG_HEADER_PACKET, format); + EXPECT_EQ(HANDSHAKE, long_packet_type); + } else { + EXPECT_EQ(GOOGLE_QUIC_PACKET, format); + } +} + TEST_P(QuicFramerTest, ClientConnectionIdFromShortHeaderToClient) { - SetQuicRestartFlag(quic_do_not_override_connection_id, true); if (!framer_.version().SupportsClientConnectionIds()) { return; } @@ -1093,7 +1313,6 @@ TEST_P(QuicFramerTest, ClientConnectionIdFromShortHeaderToClient) { // last serialized client connection ID. This test ensures that this // mechanism behaves as expected. TEST_P(QuicFramerTest, ClientConnectionIdFromShortHeaderToServer) { - SetQuicRestartFlag(quic_do_not_override_connection_id, true); if (!framer_.version().SupportsClientConnectionIds()) { return; } @@ -1140,19 +1359,9 @@ TEST_P(QuicFramerTest, PacketHeaderWith0ByteConnectionId) { {0x12, 0x34, 0x56, 0x78}}, }; - PacketFragments packet44 = { - // type (short header, 4 byte packet number) - {"Unable to read type.", - {0x32}}, - // connection_id - // packet number - {"Unable to read packet number.", - {0x12, 0x34, 0x56, 0x78}}, - }; - PacketFragments packet46 = { // type (short header, 4 byte packet number) - {"Unable to read type.", + {"Unable to read first byte.", {0x43}}, // connection_id // packet number @@ -1162,7 +1371,7 @@ TEST_P(QuicFramerTest, PacketHeaderWith0ByteConnectionId) { PacketFragments packet_hp = { // type (short header, 4 byte packet number) - {"Unable to read type.", + {"Unable to read first byte.", {0x43}}, // connection_id // packet number @@ -1174,21 +1383,14 @@ TEST_P(QuicFramerTest, PacketHeaderWith0ByteConnectionId) { PacketFragments& fragments = framer_.version().HasHeaderProtection() ? packet_hp - : framer_.transport_version() > QUIC_VERSION_44 - ? packet46 - : (framer_.transport_version() > QUIC_VERSION_43 ? packet44 - : packet); + : (framer_.transport_version() >= QUIC_VERSION_46 ? packet46 + : packet); std::unique_ptr<QuicEncryptedPacket> encrypted( AssemblePacketFromFragments(fragments)); EXPECT_FALSE(framer_.ProcessPacket(*encrypted)); EXPECT_EQ(QUIC_MISSING_PAYLOAD, framer_.error()); ASSERT_TRUE(visitor_.header_.get()); - if (!GetQuicRestartFlag(quic_do_not_override_connection_id)) { - EXPECT_EQ(FramerTestConnectionId(), - visitor_.header_->destination_connection_id); - } else { - EXPECT_EQ(FramerTestConnectionId(), visitor_.header_->source_connection_id); - } + EXPECT_EQ(FramerTestConnectionId(), visitor_.header_->source_connection_id); EXPECT_FALSE(visitor_.header_->reset_flag); EXPECT_FALSE(visitor_.header_->version_flag); EXPECT_EQ(kPacketNumber, visitor_.header_->packet_number); @@ -1214,28 +1416,10 @@ TEST_P(QuicFramerTest, PacketHeaderWithVersionFlag) { {0x12, 0x34, 0x56, 0x78}}, }; - PacketFragments packet44 = { - // type (long header with packet type ZERO_RTT_PROTECTED) - {"Unable to read type.", - {0xFC}}, - // version tag - {"Unable to read protocol version.", - {QUIC_VERSION_BYTES}}, - // connection_id length - {"Unable to read ConnectionId length.", - {0x50}}, - // connection_id - {"Unable to read Destination ConnectionId.", - {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, - // packet number - {"Unable to read packet number.", - {0x12, 0x34, 0x56, 0x78}}, - }; - PacketFragments packet46 = { // type (long header with packet type ZERO_RTT_PROTECTED and 4 bytes // packet number) - {"Unable to read type.", + {"Unable to read first byte.", {0xD3}}, // version tag {"Unable to read protocol version.", @@ -1244,7 +1428,7 @@ TEST_P(QuicFramerTest, PacketHeaderWithVersionFlag) { {"Unable to read ConnectionId length.", {0x50}}, // connection_id - {"Unable to read Destination ConnectionId.", + {"Unable to read destination connection ID.", {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, // packet number {"Unable to read packet number.", @@ -1254,17 +1438,20 @@ TEST_P(QuicFramerTest, PacketHeaderWithVersionFlag) { PacketFragments packet99 = { // type (long header with packet type ZERO_RTT_PROTECTED and 4 bytes // packet number) - {"Unable to read type.", + {"Unable to read first byte.", {0xD3}}, // version tag {"Unable to read protocol version.", {QUIC_VERSION_BYTES}}, - // connection_id length - {"Unable to read ConnectionId length.", - {0x50}}, - // connection_id - {"Unable to read Destination ConnectionId.", + // destination connection ID length + {"Unable to read destination connection ID.", + {0x08}}, + // destination connection ID + {"Unable to read destination connection ID.", {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // source connection ID length + {"Unable to read source connection ID.", + {0x00}}, // long header packet length {"Unable to read long header payload length.", {0x04}}, @@ -1277,10 +1464,8 @@ TEST_P(QuicFramerTest, PacketHeaderWithVersionFlag) { PacketFragments& fragments = framer_.transport_version() == QUIC_VERSION_99 ? packet99 - : framer_.transport_version() > QUIC_VERSION_44 - ? packet46 - : (framer_.transport_version() > QUIC_VERSION_43 ? packet44 - : packet); + : (framer_.transport_version() >= QUIC_VERSION_46 ? packet46 + : packet); std::unique_ptr<QuicEncryptedPacket> encrypted( AssemblePacketFromFragments(fragments)); EXPECT_FALSE(framer_.ProcessPacket(*encrypted)); @@ -1313,24 +1498,12 @@ TEST_P(QuicFramerTest, PacketHeaderWith4BytePacketNumber) { {0x12, 0x34, 0x56, 0x78}}, }; - PacketFragments packet44 = { - // type (short header, 4 byte packet number) - {"Unable to read type.", - {0x32}}, - // connection_id - {"Unable to read Destination ConnectionId.", - {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, - // packet number - {"Unable to read packet number.", - {0x12, 0x34, 0x56, 0x78}}, - }; - PacketFragments packet46 = { // type (short header, 4 byte packet number) - {"Unable to read type.", + {"Unable to read first byte.", {0x43}}, // connection_id - {"Unable to read Destination ConnectionId.", + {"Unable to read destination connection ID.", {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, // packet number {"Unable to read packet number.", @@ -1339,10 +1512,10 @@ TEST_P(QuicFramerTest, PacketHeaderWith4BytePacketNumber) { PacketFragments packet_hp = { // type (short header, 4 byte packet number) - {"Unable to read type.", + {"Unable to read first byte.", {0x43}}, // connection_id - {"Unable to read Destination ConnectionId.", + {"Unable to read destination connection ID.", {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, // packet number {"", @@ -1353,10 +1526,8 @@ TEST_P(QuicFramerTest, PacketHeaderWith4BytePacketNumber) { PacketFragments& fragments = framer_.version().HasHeaderProtection() ? packet_hp - : framer_.transport_version() > QUIC_VERSION_44 - ? packet46 - : (framer_.transport_version() > QUIC_VERSION_43 ? packet44 - : packet); + : (framer_.transport_version() >= QUIC_VERSION_46 ? packet46 + : packet); std::unique_ptr<QuicEncryptedPacket> encrypted( AssemblePacketFromFragments(fragments)); EXPECT_FALSE(framer_.ProcessPacket(*encrypted)); @@ -1388,24 +1559,12 @@ TEST_P(QuicFramerTest, PacketHeaderWith2BytePacketNumber) { {0x56, 0x78}}, }; - PacketFragments packet44 = { - // type (short header, 2 byte packet number) - {"Unable to read type.", - {0x31}}, - // connection_id - {"Unable to read Destination ConnectionId.", - {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, - // packet number - {"Unable to read packet number.", - {0x56, 0x78}}, - }; - PacketFragments packet46 = { // type (short header, 2 byte packet number) - {"Unable to read type.", + {"Unable to read first byte.", {0x41}}, // connection_id - {"Unable to read Destination ConnectionId.", + {"Unable to read destination connection ID.", {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, // packet number {"Unable to read packet number.", @@ -1414,10 +1573,10 @@ TEST_P(QuicFramerTest, PacketHeaderWith2BytePacketNumber) { PacketFragments packet_hp = { // type (short header, 2 byte packet number) - {"Unable to read type.", + {"Unable to read first byte.", {0x41}}, // connection_id - {"Unable to read Destination ConnectionId.", + {"Unable to read destination connection ID.", {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, // packet number {"", @@ -1430,10 +1589,8 @@ TEST_P(QuicFramerTest, PacketHeaderWith2BytePacketNumber) { PacketFragments& fragments = framer_.version().HasHeaderProtection() ? packet_hp - : (framer_.transport_version() > QUIC_VERSION_44 - ? packet46 - : (framer_.transport_version() > QUIC_VERSION_43 ? packet44 - : packet)); + : (framer_.transport_version() >= QUIC_VERSION_46 ? packet46 + : packet); std::unique_ptr<QuicEncryptedPacket> encrypted( AssemblePacketFromFragments(fragments)); if (framer_.version().HasHeaderProtection()) { @@ -1471,24 +1628,12 @@ TEST_P(QuicFramerTest, PacketHeaderWith1BytePacketNumber) { {0x78}}, }; - PacketFragments packet44 = { - // type (8 byte connection_id and 1 byte packet number) - {"Unable to read type.", - {0x30}}, - // connection_id - {"Unable to read Destination ConnectionId.", - {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, - // packet number - {"Unable to read packet number.", - {0x78}}, - }; - PacketFragments packet46 = { // type (8 byte connection_id and 1 byte packet number) - {"Unable to read type.", + {"Unable to read first byte.", {0x40}}, // connection_id - {"Unable to read Destination ConnectionId.", + {"Unable to read destination connection ID.", {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, // packet number {"Unable to read packet number.", @@ -1497,10 +1642,10 @@ TEST_P(QuicFramerTest, PacketHeaderWith1BytePacketNumber) { PacketFragments packet_hp = { // type (8 byte connection_id and 1 byte packet number) - {"Unable to read type.", + {"Unable to read first byte.", {0x40}}, // connection_id - {"Unable to read Destination ConnectionId.", + {"Unable to read destination connection ID.", {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, // packet number {"", @@ -1514,10 +1659,8 @@ TEST_P(QuicFramerTest, PacketHeaderWith1BytePacketNumber) { PacketFragments& fragments = framer_.version().HasHeaderProtection() ? packet_hp - : (framer_.transport_version() > QUIC_VERSION_44 - ? packet46 - : (framer_.transport_version() > QUIC_VERSION_43 ? packet44 - : packet)); + : (framer_.transport_version() >= QUIC_VERSION_46 ? packet46 + : packet); std::unique_ptr<QuicEncryptedPacket> encrypted( AssemblePacketFromFragments(fragments)); if (framer_.version().HasHeaderProtection()) { @@ -1626,28 +1769,6 @@ TEST_P(QuicFramerTest, PacketWithDiversificationNonce) { 0x00, 0x00, 0x00, 0x00 }; - unsigned char packet44[] = { - // type: Long header with packet type ZERO_RTT_PROTECTED - 0xFC, - // version tag - QUIC_VERSION_BYTES, - // connection_id length - 0x05, - // connection_id - 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, - // packet number - 0x12, 0x34, 0x56, 0x78, - // nonce - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, - 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, - - // frame type (padding) - 0x00, - 0x00, 0x00, 0x00, 0x00 - }; - unsigned char packet46[] = { // type: Long header with packet type ZERO_RTT_PROTECTED and 1 byte packet // number. @@ -1677,9 +1798,11 @@ TEST_P(QuicFramerTest, PacketWithDiversificationNonce) { 0xD0, // version tag QUIC_VERSION_BYTES, - // connection_id length - 0x05, - // connection_id + // destination connection ID length + 0x00, + // source connection ID length + 0x08, + // source connection ID 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, // long header packet length 0x26, @@ -1706,12 +1829,9 @@ TEST_P(QuicFramerTest, PacketWithDiversificationNonce) { if (framer_.transport_version() == QUIC_VERSION_99) { p = packet99; p_size = QUIC_ARRAYSIZE(packet99); - } else if (framer_.transport_version() > QUIC_VERSION_44) { + } else if (framer_.transport_version() >= QUIC_VERSION_46) { p = packet46; p_size = QUIC_ARRAYSIZE(packet46); - } else if (framer_.transport_version() > QUIC_VERSION_43) { - p = packet44; - p_size = QUIC_ARRAYSIZE(packet44); } QuicEncryptedPacket encrypted(AsChars(p), p_size, false); @@ -1742,9 +1862,9 @@ TEST_P(QuicFramerTest, LargePublicFlagWithMismatchedVersions) { 0x00, 0x00, 0x00, 0x00 }; - unsigned char packet44[] = { - // type (long header with packet type ZERO_RTT_PROTECTED) - 0xFC, + unsigned char packet46[] = { + // type (long header, ZERO_RTT_PROTECTED, 4-byte packet number) + 0xD3, // version tag 'Q', '0', '0', '0', // connection_id length @@ -1759,15 +1879,17 @@ TEST_P(QuicFramerTest, LargePublicFlagWithMismatchedVersions) { 0x00, 0x00, 0x00, 0x00 }; - unsigned char packet45[] = { + unsigned char packet99[] = { // type (long header, ZERO_RTT_PROTECTED, 4-byte packet number) 0xD3, // version tag 'Q', '0', '0', '0', - // connection_id length - 0x50, - // connection_id + // destination connection ID length + 0x08, + // destination connection ID 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // source connection ID length + 0x00, // packet number 0x12, 0x34, 0x56, 0x78, @@ -1779,12 +1901,12 @@ TEST_P(QuicFramerTest, LargePublicFlagWithMismatchedVersions) { unsigned char* p = packet; size_t p_size = QUIC_ARRAYSIZE(packet); - if (framer_.transport_version() > QUIC_VERSION_44) { - p = packet45; - p_size = QUIC_ARRAYSIZE(packet45); - } else if (framer_.transport_version() > QUIC_VERSION_43) { - p = packet44; - p_size = QUIC_ARRAYSIZE(packet44); + if (framer_.transport_version() >= QUIC_VERSION_99) { + p = packet99; + p_size = QUIC_ARRAYSIZE(packet99); + } else if (framer_.transport_version() >= QUIC_VERSION_46) { + p = packet46; + p_size = QUIC_ARRAYSIZE(packet46); } QuicEncryptedPacket encrypted(AsChars(p), p_size, false); EXPECT_TRUE(framer_.ProcessPacket(encrypted)); @@ -1792,8 +1914,6 @@ TEST_P(QuicFramerTest, LargePublicFlagWithMismatchedVersions) { ASSERT_TRUE(visitor_.header_.get()); EXPECT_EQ(0, visitor_.frame_count_); EXPECT_EQ(1, visitor_.version_mismatch_); - ASSERT_EQ(1u, visitor_.padding_frames_.size()); - EXPECT_EQ(5, visitor_.padding_frames_[0]->num_padding_bytes); } TEST_P(QuicFramerTest, PaddingFrame) { @@ -1826,33 +1946,6 @@ TEST_P(QuicFramerTest, PaddingFrame) { 0x00, 0x00, }; - unsigned char packet44[] = { - // type (short header, 4 byte packet number) - 0x32, - // connection_id - 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, - // packet number - 0x12, 0x34, 0x56, 0x78, - - // paddings - 0x00, 0x00, - // frame type (stream frame with fin) - 0xFF, - // stream id - 0x01, 0x02, 0x03, 0x04, - // offset - 0x3A, 0x98, 0xFE, 0xDC, - 0x32, 0x10, 0x76, 0x54, - // data length - 0x00, 0x0c, - // data - 'h', 'e', 'l', 'l', - 'o', ' ', 'w', 'o', - 'r', 'l', 'd', '!', - // paddings - 0x00, 0x00, - }; - unsigned char packet46[] = { // type (short header, 4 byte packet number) 0x43, @@ -1914,12 +2007,9 @@ TEST_P(QuicFramerTest, PaddingFrame) { if (VersionHasIetfQuicFrames(framer_.transport_version())) { p = packet99; p_size = QUIC_ARRAYSIZE(packet99); - } else if (framer_.transport_version() > QUIC_VERSION_44) { + } else if (framer_.transport_version() >= QUIC_VERSION_46) { p = packet46; p_size = QUIC_ARRAYSIZE(packet46); - } else if (framer_.transport_version() > QUIC_VERSION_43) { - p = packet44; - p_size = QUIC_ARRAYSIZE(packet44); } QuicEncryptedPacket encrypted(AsChars(p), p_size, false); @@ -1974,36 +2064,6 @@ TEST_P(QuicFramerTest, StreamFrame) { 'r', 'l', 'd', '!'}}, }; - PacketFragments packet44 = { - // type (short header, 4 byte packet number) - {"", - {0x32}}, - // connection_id - {"", - {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, - // packet number - {"", - {0x12, 0x34, 0x56, 0x78}}, - // frame type (stream frame with fin) - {"", - {0xFF}}, - // stream id - {"Unable to read stream_id.", - {0x01, 0x02, 0x03, 0x04}}, - // offset - {"Unable to read offset.", - {0x3A, 0x98, 0xFE, 0xDC, - 0x32, 0x10, 0x76, 0x54}}, - {"Unable to read frame data.", - { - // data length - 0x00, 0x0c, - // data - 'h', 'e', 'l', 'l', - 'o', ' ', 'w', 'o', - 'r', 'l', 'd', '!'}}, - }; - PacketFragments packet46 = { // type (short header, 4 byte packet number) {"", @@ -2068,10 +2128,8 @@ TEST_P(QuicFramerTest, StreamFrame) { PacketFragments& fragments = VersionHasIetfQuicFrames(framer_.transport_version()) ? packet99 - : (framer_.transport_version() > QUIC_VERSION_44 - ? packet46 - : (framer_.transport_version() > QUIC_VERSION_43 ? packet44 - : packet)); + : (framer_.transport_version() >= QUIC_VERSION_46 ? packet46 + : packet); std::unique_ptr<QuicEncryptedPacket> encrypted( AssemblePacketFromFragments(fragments)); EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); @@ -2178,21 +2236,6 @@ TEST_P(QuicFramerTest, MissingDiversificationNonce) { 0x00, }; - unsigned char packet44[] = { - // type (long header, ZERO_RTT_PROTECTED, 4-byte packet number) - 0xFC, - // version tag - QUIC_VERSION_BYTES, - // connection_id length - 0x05, - // connection_id - 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, - // packet number - 0x12, 0x34, 0x56, 0x78, - // padding frame - 0x00, - }; - unsigned char packet46[] = { // type (long header, ZERO_RTT_PROTECTED, 4-byte packet number) 0xD3, @@ -2213,9 +2256,11 @@ TEST_P(QuicFramerTest, MissingDiversificationNonce) { 0xD3, // version tag QUIC_VERSION_BYTES, - // connection_id length - 0x05, - // connection_id + // destination connection ID length + 0x00, + // source connection ID length + 0x08, + // source connection ID 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, // IETF long header payload length 0x05, @@ -2234,16 +2279,13 @@ TEST_P(QuicFramerTest, MissingDiversificationNonce) { } else if (framer_.transport_version() >= QUIC_VERSION_46) { p = packet46; p_length = QUIC_ARRAYSIZE(packet46); - } else if (framer_.transport_version() >= QUIC_VERSION_44) { - p = packet44; - p_length = QUIC_ARRAYSIZE(packet44); } QuicEncryptedPacket encrypted(AsChars(p), p_length, false); EXPECT_FALSE(framer_.ProcessPacket(encrypted)); if (framer_.version().HasHeaderProtection()) { EXPECT_EQ(QUIC_DECRYPTION_FAILURE, framer_.error()); EXPECT_EQ("Unable to decrypt header protection.", framer_.detailed_error()); - } else if (framer_.transport_version() >= QUIC_VERSION_44) { + } else if (framer_.transport_version() >= QUIC_VERSION_46) { // Cannot read diversification nonce. EXPECT_EQ(QUIC_INVALID_PACKET_HEADER, framer_.error()); EXPECT_EQ("Unable to read nonce.", framer_.detailed_error()); @@ -2344,36 +2386,6 @@ TEST_P(QuicFramerTest, StreamFrame2ByteStreamId) { 'r', 'l', 'd', '!'}}, }; - PacketFragments packet44 = { - // type (short header, 4 byte packet number) - {"", - {0x32}}, - // connection_id - {"", - {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, - // packet number - {"", - {0x12, 0x34, 0x56, 0x78}}, - // frame type (stream frame with fin) - {"", - {0xFD}}, - // stream id - {"Unable to read stream_id.", - {0x03, 0x04}}, - // offset - {"Unable to read offset.", - {0x3A, 0x98, 0xFE, 0xDC, - 0x32, 0x10, 0x76, 0x54}}, - {"Unable to read frame data.", - { - // data length - 0x00, 0x0c, - // data - 'h', 'e', 'l', 'l', - 'o', ' ', 'w', 'o', - 'r', 'l', 'd', '!'}}, - }; - PacketFragments packet46 = { // type (short header, 4 byte packet number) {"", @@ -2438,10 +2450,8 @@ TEST_P(QuicFramerTest, StreamFrame2ByteStreamId) { PacketFragments& fragments = VersionHasIetfQuicFrames(framer_.transport_version()) ? packet99 - : (framer_.transport_version() > QUIC_VERSION_44 - ? packet46 - : (framer_.transport_version() > QUIC_VERSION_43 ? packet44 - : packet)); + : (framer_.transport_version() >= QUIC_VERSION_46 ? packet46 + : packet); std::unique_ptr<QuicEncryptedPacket> encrypted( AssemblePacketFromFragments(fragments)); EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); @@ -2496,36 +2506,6 @@ TEST_P(QuicFramerTest, StreamFrame1ByteStreamId) { 'r', 'l', 'd', '!'}}, }; - PacketFragments packet44 = { - // type (short header, 4 byte packet number) - {"", - {0x32}}, - // connection_id - {"", - {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, - // packet number - {"", - {0x12, 0x34, 0x56, 0x78}}, - // frame type (stream frame with fin) - {"", - {0xFC}}, - // stream id - {"Unable to read stream_id.", - {0x04}}, - // offset - {"Unable to read offset.", - {0x3A, 0x98, 0xFE, 0xDC, - 0x32, 0x10, 0x76, 0x54}}, - {"Unable to read frame data.", - { - // data length - 0x00, 0x0c, - // data - 'h', 'e', 'l', 'l', - 'o', ' ', 'w', 'o', - 'r', 'l', 'd', '!'}}, - }; - PacketFragments packet46 = { // type (short header, 4 byte packet number) {"", @@ -2590,10 +2570,8 @@ TEST_P(QuicFramerTest, StreamFrame1ByteStreamId) { PacketFragments& fragments = VersionHasIetfQuicFrames(framer_.transport_version()) ? packet99 - : (framer_.transport_version() > QUIC_VERSION_44 - ? packet46 - : (framer_.transport_version() > QUIC_VERSION_43 ? packet44 - : packet)); + : (framer_.transport_version() >= QUIC_VERSION_46 ? packet46 + : packet); std::unique_ptr<QuicEncryptedPacket> encrypted( AssemblePacketFromFragments(fragments)); EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); @@ -2657,42 +2635,6 @@ TEST_P(QuicFramerTest, StreamFrameWithVersion) { 'r', 'l', 'd', '!'}}, }; - PacketFragments packet44 = { - // public flags (long header with packet type ZERO_RTT_PROTECTED) - {"", - {0xFC}}, - // version tag - {"", - {QUIC_VERSION_BYTES}}, - // connection_id length - {"", - {0x50}}, - // connection_id - {"", - {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, - // packet number - {"", - {0x12, 0x34, 0x56, 0x78}}, - // frame type (stream frame with fin) - {"", - {0xFE}}, - // stream id - {"Unable to read stream_id.", - {0x02, 0x03, 0x04}}, - // offset - {"Unable to read offset.", - {0x3A, 0x98, 0xFE, 0xDC, - 0x32, 0x10, 0x76, 0x54}}, - {"Unable to read frame data.", - { - // data length - 0x00, 0x0c, - // data - 'h', 'e', 'l', 'l', - 'o', ' ', 'w', 'o', - 'r', 'l', 'd', '!'}}, - }; - PacketFragments packet46 = { // public flags (long header with packet type ZERO_RTT_PROTECTED and // 4-byte packet number) @@ -2738,12 +2680,15 @@ TEST_P(QuicFramerTest, StreamFrameWithVersion) { // version tag {"", {QUIC_VERSION_BYTES}}, - // connection_id length + // destination connection ID length {"", - {0x50}}, - // connection_id + {0x08}}, + // destination connection ID {"", {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // source connection ID length + {"", + {0x00}}, // long header packet length {"", {0x1E}}, @@ -2782,10 +2727,8 @@ TEST_P(QuicFramerTest, StreamFrameWithVersion) { PacketFragments& fragments = VersionHasIetfQuicFrames(framer_.transport_version()) ? packet99 - : (framer_.transport_version() > QUIC_VERSION_44 - ? packet46 - : (framer_.transport_version() > QUIC_VERSION_43 ? packet44 - : packet)); + : (framer_.transport_version() >= QUIC_VERSION_46 ? packet46 + : packet); std::unique_ptr<QuicEncryptedPacket> encrypted( AssemblePacketFromFragments(fragments)); EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); @@ -2839,29 +2782,6 @@ TEST_P(QuicFramerTest, RejectPacket) { 'r', 'l', 'd', '!', }; - unsigned char packet44[] = { - // type (short header, 4 byte packet number) - 0x32, - // connection_id - 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, - // packet number - 0x12, 0x34, 0x56, 0x78, - - // frame type (STREAM Frame with FIN, LEN, and OFFSET bits set) - 0x10 | 0x01 | 0x02 | 0x04, - // stream id - kVarInt62FourBytes + 0x01, 0x02, 0x03, 0x04, - // offset - kVarInt62EightBytes + 0x3A, 0x98, 0xFE, 0xDC, - 0x32, 0x10, 0x76, 0x54, - // data length - kVarInt62OneByte + 0x0c, - // data - 'h', 'e', 'l', 'l', - 'o', ' ', 'w', 'o', - 'r', 'l', 'd', '!', - }; - unsigned char packet46[] = { // type (short header, 4 byte packet number) 0x43, @@ -2887,14 +2807,12 @@ TEST_P(QuicFramerTest, RejectPacket) { // clang-format on unsigned char* p = packet; - if (framer_.transport_version() > QUIC_VERSION_44) { + if (framer_.transport_version() >= QUIC_VERSION_46) { p = packet46; - } else if (framer_.transport_version() > QUIC_VERSION_43) { - p = packet44; } QuicEncryptedPacket encrypted(AsChars(p), framer_.transport_version() > QUIC_VERSION_43 - ? QUIC_ARRAYSIZE(packet44) + ? QUIC_ARRAYSIZE(packet46) : QUIC_ARRAYSIZE(packet), false); EXPECT_TRUE(framer_.ProcessPacket(encrypted)); @@ -2920,15 +2838,6 @@ TEST_P(QuicFramerTest, RejectPublicHeader) { 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, }; - unsigned char packet44[] = { - // type (short header, 1 byte packet number) - 0x30, - // connection_id - 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, - // packet number - 0x01, - }; - unsigned char packet46[] = { // type (short header, 1 byte packet number) 0x40, @@ -2940,15 +2849,10 @@ TEST_P(QuicFramerTest, RejectPublicHeader) { // clang-format on QuicEncryptedPacket encrypted( - framer_.transport_version() > QUIC_VERSION_44 - ? AsChars(packet46) - : (framer_.transport_version() > QUIC_VERSION_43 ? AsChars(packet44) - : AsChars(packet)), - framer_.transport_version() > QUIC_VERSION_44 - ? QUIC_ARRAYSIZE(packet46) - : (framer_.transport_version() > QUIC_VERSION_43 - ? QUIC_ARRAYSIZE(packet44) - : QUIC_ARRAYSIZE(packet)), + framer_.transport_version() >= QUIC_VERSION_46 ? AsChars(packet46) + : AsChars(packet), + framer_.transport_version() >= QUIC_VERSION_46 ? QUIC_ARRAYSIZE(packet46) + : QUIC_ARRAYSIZE(packet), false); EXPECT_TRUE(framer_.ProcessPacket(encrypted)); @@ -2988,34 +2892,6 @@ TEST_P(QuicFramerTest, AckFrameOneAckBlock) { {0x00}} }; - PacketFragments packet44 = { - // type (short packet, 4 byte packet number) - {"", - {0x32}}, - // connection_id - {"", - {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, - // packet number - {"", - {0x12, 0x34, 0x56, 0x78}}, - // frame type (ack frame) - // (one ack block, 2 byte largest observed, 2 byte block length) - {"", - {0x45}}, - // largest acked - {"Unable to read largest acked.", - {0x12, 0x34}}, - // Zero delta time. - {"Unable to read ack delay time.", - {0x00, 0x00}}, - // first ack block length. - {"Unable to read first ack block length.", - {0x12, 0x34}}, - // num timestamps. - {"Unable to read num received packets.", - {0x00}} - }; - PacketFragments packet46 = { // type (short packet, 4 byte packet number) {"", @@ -3083,10 +2959,8 @@ TEST_P(QuicFramerTest, AckFrameOneAckBlock) { PacketFragments& fragments = VersionHasIetfQuicFrames(framer_.transport_version()) ? packet99 - : (framer_.transport_version() > QUIC_VERSION_44 - ? packet46 - : (framer_.transport_version() > QUIC_VERSION_43 ? packet44 - : packet)); + : (framer_.transport_version() >= QUIC_VERSION_46 ? packet46 + : packet); std::unique_ptr<QuicEncryptedPacket> encrypted( AssemblePacketFromFragments(fragments)); EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); @@ -3140,34 +3014,6 @@ TEST_P(QuicFramerTest, FirstAckFrameUnderflow) { {0x00}} }; - PacketFragments packet44 = { - // type (short header, 4 byte packet number) - {"", - {0x32}}, - // connection_id - {"", - {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, - // packet number - {"", - {0x12, 0x34, 0x56, 0x78}}, - // frame type (ack frame) - // (one ack block, 2 byte largest observed, 2 byte block length) - {"", - {0x45}}, - // largest acked - {"Unable to read largest acked.", - {0x12, 0x34}}, - // Zero delta time. - {"Unable to read ack delay time.", - {0x00, 0x00}}, - // first ack block length. - {"Unable to read first ack block length.", - {0x88, 0x88}}, - // num timestamps. - {"Underflow with first ack block length 34952 largest acked is 4660.", - {0x00}} - }; - PacketFragments packet46 = { // type (short header, 4 byte packet number) {"", @@ -3227,10 +3073,8 @@ TEST_P(QuicFramerTest, FirstAckFrameUnderflow) { PacketFragments& fragments = VersionHasIetfQuicFrames(framer_.transport_version()) ? packet99 - : (framer_.transport_version() > QUIC_VERSION_44 - ? packet46 - : (framer_.transport_version() > QUIC_VERSION_43 ? packet44 - : packet)); + : (framer_.transport_version() >= QUIC_VERSION_46 ? packet46 + : packet); std::unique_ptr<QuicEncryptedPacket> encrypted( AssemblePacketFromFragments(fragments)); EXPECT_FALSE(framer_.ProcessPacket(*encrypted)); @@ -3558,44 +3402,6 @@ TEST_P(QuicFramerTest, AckFrameFirstAckBlockLengthZero) { { 0x00 }}, }; - PacketFragments packet44 = { - // type (short header, 4 byte packet number) - {"", - { 0x32 }}, - // connection_id - {"", - { 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10 }}, - // packet number - {"", - { 0x12, 0x34, 0x56, 0x78 }}, - - // frame type (ack frame) - // (more than one ack block, 2 byte largest observed, 2 byte block length) - {"", - { 0x65 }}, - // largest acked - {"Unable to read largest acked.", - { 0x12, 0x34 }}, - // Zero delta time. - {"Unable to read ack delay time.", - { 0x00, 0x00 }}, - // num ack blocks ranges. - {"Unable to read num of ack blocks.", - { 0x01 }}, - // first ack block length. - {"Unable to read first ack block length.", - { 0x00, 0x00 }}, - // gap to next block. - { "First block length is zero.", - { 0x01 }}, - // ack block length. - { "First block length is zero.", - { 0x0e, 0xaf }}, - // Number of timestamps. - { "First block length is zero.", - { 0x00 }}, - }; - PacketFragments packet46 = { // type (short header, 4 byte packet number) {"", @@ -3636,9 +3442,7 @@ TEST_P(QuicFramerTest, AckFrameFirstAckBlockLengthZero) { // clang-format on PacketFragments& fragments = - framer_.transport_version() > QUIC_VERSION_44 - ? packet46 - : (framer_.transport_version() > QUIC_VERSION_43 ? packet44 : packet); + framer_.transport_version() >= QUIC_VERSION_46 ? packet46 : packet; std::unique_ptr<QuicEncryptedPacket> encrypted( AssemblePacketFromFragments(fragments)); @@ -3688,34 +3492,6 @@ TEST_P(QuicFramerTest, AckFrameOneAckBlockMaxLength) { {0x00}} }; - PacketFragments packet44 = { - // type (short header, 4 byte packet number) - {"", - {0x32}}, - // connection_id - {"", - {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, - // packet number - {"", - {0x56, 0x78, 0x9A, 0xBC}}, - // frame type (ack frame) - // (one ack block, 4 byte largest observed, 2 byte block length) - {"", - {0x49}}, - // largest acked - {"Unable to read largest acked.", - {0x12, 0x34, 0x56, 0x78}}, - // Zero delta time. - {"Unable to read ack delay time.", - {0x00, 0x00}}, - // first ack block length. - {"Unable to read first ack block length.", - {0x12, 0x34}}, - // num timestamps. - {"Unable to read num received packets.", - {0x00}} - }; - PacketFragments packet46 = { // type (short header, 4 byte packet number) {"", @@ -3775,10 +3551,8 @@ TEST_P(QuicFramerTest, AckFrameOneAckBlockMaxLength) { PacketFragments& fragments = VersionHasIetfQuicFrames(framer_.transport_version()) ? packet99 - : (framer_.transport_version() > QUIC_VERSION_44 - ? packet46 - : (framer_.transport_version() > QUIC_VERSION_43 ? packet44 - : packet)); + : (framer_.transport_version() >= QUIC_VERSION_46 ? packet46 + : packet); std::unique_ptr<QuicEncryptedPacket> encrypted( AssemblePacketFromFragments(fragments)); EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); @@ -3871,74 +3645,6 @@ TEST_P(QuicFramerTest, AckFrameTwoTimeStampsMultipleAckBlocks) { { 0x32, 0x10 }}, }; - PacketFragments packet44 = { - // type (short header, 4 byte packet number) - {"", - { 0x32 }}, - // connection_id - {"", - { 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10 }}, - // packet number - {"", - { 0x12, 0x34, 0x56, 0x78 }}, - - // frame type (ack frame) - // (more than one ack block, 2 byte largest observed, 2 byte block length) - {"", - { 0x65 }}, - // largest acked - {"Unable to read largest acked.", - { 0x12, 0x34 }}, - // Zero delta time. - {"Unable to read ack delay time.", - { 0x00, 0x00 }}, - // num ack blocks ranges. - {"Unable to read num of ack blocks.", - { 0x04 }}, - // first ack block length. - {"Unable to read first ack block length.", - { 0x00, 0x01 }}, - // gap to next block. - { "Unable to read gap to next ack block.", - { 0x01 }}, - // ack block length. - { "Unable to ack block length.", - { 0x0e, 0xaf }}, - // gap to next block. - { "Unable to read gap to next ack block.", - { 0xff }}, - // ack block length. - { "Unable to ack block length.", - { 0x00, 0x00 }}, - // gap to next block. - { "Unable to read gap to next ack block.", - { 0x91 }}, - // ack block length. - { "Unable to ack block length.", - { 0x01, 0xea }}, - // gap to next block. - { "Unable to read gap to next ack block.", - { 0x05 }}, - // ack block length. - { "Unable to ack block length.", - { 0x00, 0x04 }}, - // Number of timestamps. - { "Unable to read num received packets.", - { 0x02 }}, - // Delta from largest observed. - { "Unable to read sequence delta in received packets.", - { 0x01 }}, - // Delta time. - { "Unable to read time delta in received packets.", - { 0x76, 0x54, 0x32, 0x10 }}, - // Delta from largest observed. - { "Unable to read sequence delta in received packets.", - { 0x02 }}, - // Delta time. - { "Unable to read incremental time delta in received packets.", - { 0x32, 0x10 }}, - }; - PacketFragments packet46 = { // type (short header, 4 byte packet number) {"", @@ -4067,10 +3773,8 @@ TEST_P(QuicFramerTest, AckFrameTwoTimeStampsMultipleAckBlocks) { PacketFragments& fragments = VersionHasIetfQuicFrames(framer_.transport_version()) ? packet99 - : (framer_.transport_version() > QUIC_VERSION_44 - ? packet46 - : (framer_.transport_version() > QUIC_VERSION_43 ? packet44 - : packet)); + : (framer_.transport_version() >= QUIC_VERSION_46 ? packet46 + : packet); std::unique_ptr<QuicEncryptedPacket> encrypted( AssemblePacketFromFragments(fragments)); @@ -4126,31 +3830,6 @@ TEST_P(QuicFramerTest, AckFrameTimeStampDeltaTooHigh) { 0x10, 0x32, 0x54, 0x76, }; - unsigned char packet44[] = { - // type (short header, 4 byte packet number) - 0x32, - // connection_id - 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, - // packet number - 0x12, 0x34, 0x56, 0x78, - - // frame type (ack frame) - // (no ack blocks, 1 byte largest observed, 1 byte block length) - 0x40, - // largest acked - 0x01, - // Zero delta time. - 0x00, 0x00, - // first ack block length. - 0x01, - // num timestamps. - 0x01, - // Delta from largest observed. - 0x01, - // Delta time. - 0x10, 0x32, 0x54, 0x76, - }; - unsigned char packet46[] = { // type (short header, 4 byte packet number) 0x43, @@ -4180,10 +3859,8 @@ TEST_P(QuicFramerTest, AckFrameTimeStampDeltaTooHigh) { return; } QuicEncryptedPacket encrypted( - AsChars(framer_.transport_version() > QUIC_VERSION_44 - ? packet46 - : (framer_.transport_version() > QUIC_VERSION_43 ? packet44 - : packet)), + AsChars(framer_.transport_version() >= QUIC_VERSION_46 ? packet46 + : packet), QUIC_ARRAYSIZE(packet), false); EXPECT_FALSE(framer_.ProcessPacket(encrypted)); EXPECT_TRUE(QuicTextUtils::StartsWith( @@ -4222,35 +3899,6 @@ TEST_P(QuicFramerTest, AckFrameTimeStampSecondDeltaTooHigh) { 0x10, 0x32, }; - unsigned char packet44[] = { - // type (short header, 4 byte packet number) - 0x32, - // connection_id - 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, - // packet number - 0x12, 0x34, 0x56, 0x78, - - // frame type (ack frame) - // (no ack blocks, 1 byte largest observed, 1 byte block length) - 0x40, - // largest acked - 0x03, - // Zero delta time. - 0x00, 0x00, - // first ack block length. - 0x03, - // num timestamps. - 0x02, - // Delta from largest observed. - 0x01, - // Delta time. - 0x10, 0x32, 0x54, 0x76, - // Delta from largest observed. - 0x03, - // Delta time. - 0x10, 0x32, - }; - unsigned char packet46[] = { // type (short header, 4 byte packet number) 0x43, @@ -4284,10 +3932,8 @@ TEST_P(QuicFramerTest, AckFrameTimeStampSecondDeltaTooHigh) { return; } QuicEncryptedPacket encrypted( - AsChars(framer_.transport_version() > QUIC_VERSION_44 - ? packet46 - : (framer_.transport_version() > QUIC_VERSION_43 ? packet44 - : packet)), + AsChars(framer_.transport_version() >= QUIC_VERSION_46 ? packet46 + : packet), QUIC_ARRAYSIZE(packet), false); EXPECT_FALSE(framer_.ProcessPacket(encrypted)); EXPECT_TRUE(QuicTextUtils::StartsWith( @@ -4318,24 +3964,6 @@ TEST_P(QuicFramerTest, NewStopWaitingFrame) { {0x00, 0x00, 0x00, 0x08}} }; - PacketFragments packet44 = { - // type (short header, 4 byte packet number) - {"", - {0x32}}, - // connection_id - {"", - {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, - // packet number - {"", - {0x12, 0x34, 0x56, 0x78}}, - // frame type (stop waiting frame) - {"", - {0x06}}, - // least packet number awaiting an ack, delta from packet number. - {"Unable to read least unacked delta.", - {0x00, 0x00, 0x00, 0x08}} - }; - PacketFragments packet46 = { // type (short header, 4 byte packet number) {"", @@ -4356,14 +3984,12 @@ TEST_P(QuicFramerTest, NewStopWaitingFrame) { // clang-format on PacketFragments& fragments = - framer_.transport_version() > QUIC_VERSION_44 - ? packet46 - : (framer_.transport_version() > QUIC_VERSION_43 ? packet44 : packet); + framer_.transport_version() >= QUIC_VERSION_46 ? packet46 : packet; std::unique_ptr<QuicEncryptedPacket> encrypted( AssemblePacketFromFragments(fragments)); if (GetQuicReloadableFlag(quic_do_not_accept_stop_waiting) && - version_.transport_version >= QUIC_VERSION_44) { + version_.transport_version >= QUIC_VERSION_46) { EXPECT_FALSE(framer_.ProcessPacket(*encrypted)); EXPECT_EQ(QUIC_INVALID_STOP_WAITING_DATA, framer_.error()); EXPECT_EQ("STOP WAITING not supported in version 44+.", @@ -4390,7 +4016,7 @@ TEST_P(QuicFramerTest, NewStopWaitingFrame) { TEST_P(QuicFramerTest, InvalidNewStopWaitingFrame) { if (VersionHasIetfQuicFrames(version_.transport_version) || (GetQuicReloadableFlag(quic_do_not_accept_stop_waiting) && - version_.transport_version >= QUIC_VERSION_44)) { + version_.transport_version >= QUIC_VERSION_46)) { return; } SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE); @@ -4409,19 +4035,6 @@ TEST_P(QuicFramerTest, InvalidNewStopWaitingFrame) { 0x9A, 0xA8, }; - unsigned char packet44[] = { - // type (short header, 4 byte packet number) - 0x32, - // connection_id - 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, - // packet number - 0x12, 0x34, 0x56, 0x78, - // frame type (stop waiting frame) - 0x06, - // least packet number awaiting an ack, delta from packet number. - 0x57, 0x78, 0x9A, 0xA8, - }; - unsigned char packet46[] = { // type (short header, 4 byte packet number) 0x43, @@ -4437,11 +4050,9 @@ TEST_P(QuicFramerTest, InvalidNewStopWaitingFrame) { // clang-format on QuicEncryptedPacket encrypted( - AsChars(framer_.transport_version() > QUIC_VERSION_44 - ? packet46 - : (framer_.transport_version() > QUIC_VERSION_43 ? packet44 - : packet)), - framer_.transport_version() > QUIC_VERSION_43 ? QUIC_ARRAYSIZE(packet44) + AsChars(framer_.transport_version() >= QUIC_VERSION_46 ? packet46 + : packet), + framer_.transport_version() > QUIC_VERSION_43 ? QUIC_ARRAYSIZE(packet46) : QUIC_ARRAYSIZE(packet), false); EXPECT_FALSE(framer_.ProcessPacket(encrypted)); @@ -4477,31 +4088,6 @@ TEST_P(QuicFramerTest, RstStreamFrame) { {0x00, 0x00, 0x00, 0x01}} }; - PacketFragments packet44 = { - // type (short header, 4 byte packet number) - {"", - {0x32}}, - // connection_id - {"", - {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, - // packet number - {"", - {0x12, 0x34, 0x56, 0x78}}, - // frame type (rst stream frame) - {"", - {0x01}}, - // stream id - {"Unable to read stream_id.", - {0x01, 0x02, 0x03, 0x04}}, - // sent byte offset - {"Unable to read rst stream sent byte offset.", - {0x3A, 0x98, 0xFE, 0xDC, - 0x32, 0x10, 0x76, 0x54}}, - // error code - {"Unable to read rst stream error code.", - {0x00, 0x00, 0x00, 0x01}} - }; - PacketFragments packet46 = { // type (short header, 4 byte packet number) {"", @@ -4555,10 +4141,8 @@ TEST_P(QuicFramerTest, RstStreamFrame) { PacketFragments& fragments = VersionHasIetfQuicFrames(framer_.transport_version()) ? packet99 - : (framer_.transport_version() > QUIC_VERSION_44 - ? packet46 - : (framer_.transport_version() > QUIC_VERSION_43 ? packet44 - : packet)); + : (framer_.transport_version() >= QUIC_VERSION_46 ? packet46 + : packet); std::unique_ptr<QuicEncryptedPacket> encrypted( AssemblePacketFromFragments(fragments)); EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); @@ -4606,34 +4190,6 @@ TEST_P(QuicFramerTest, ConnectionCloseFrame) { } }; - PacketFragments packet44 = { - // type (short header, 4 byte packet number) - {"", - {0x32}}, - // connection_id - {"", - {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, - // packet number - {"", - {0x12, 0x34, 0x56, 0x78}}, - // frame type (connection close frame) - {"", - {0x02}}, - // error code - {"Unable to read connection close error code.", - {0x00, 0x00, 0x00, 0x11}}, - {"Unable to read connection close error details.", - { - // error details length - 0x0, 0x0d, - // error details - 'b', 'e', 'c', 'a', - 'u', 's', 'e', ' ', - 'I', ' ', 'c', 'a', - 'n'} - } - }; - PacketFragments packet46 = { // type (short header, 4 byte packet number) {"", @@ -4672,7 +4228,7 @@ TEST_P(QuicFramerTest, ConnectionCloseFrame) { // packet number {"", {0x12, 0x34, 0x56, 0x78}}, - // frame type (IETF_CONNECTION_CLOSE frame) + // frame type (IETF Transport CONNECTION_CLOSE frame) {"", {0x1c}}, // error code @@ -4696,10 +4252,8 @@ TEST_P(QuicFramerTest, ConnectionCloseFrame) { PacketFragments& fragments = VersionHasIetfQuicFrames(framer_.transport_version()) ? packet99 - : (framer_.transport_version() > QUIC_VERSION_44 - ? packet46 - : (framer_.transport_version() > QUIC_VERSION_43 ? packet44 - : packet)); + : (framer_.transport_version() >= QUIC_VERSION_46 ? packet46 + : packet); std::unique_ptr<QuicEncryptedPacket> encrypted( AssemblePacketFromFragments(fragments)); EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); @@ -4717,6 +4271,148 @@ TEST_P(QuicFramerTest, ConnectionCloseFrame) { if (VersionHasIetfQuicFrames(framer_.transport_version())) { EXPECT_EQ(0x1234u, visitor_.connection_close_frame_.transport_close_frame_type); + EXPECT_EQ(QUIC_IETF_GQUIC_ERROR_MISSING, + visitor_.connection_close_frame_.extracted_error_code); + } else { + // For Google QUIC closes, the error code is copied into + // extracted_error_code. + EXPECT_EQ(0x11u, + static_cast<unsigned>( + visitor_.connection_close_frame_.extracted_error_code)); + } + + ASSERT_EQ(0u, visitor_.ack_frames_.size()); + + CheckFramingBoundaries(fragments, QUIC_INVALID_CONNECTION_CLOSE_DATA); +} + +// As above, but checks that for Google-QUIC, if there happens +// to be an ErrorCode string at the start of the details, it is +// NOT extracted/parsed/folded/spindled/and/mutilated. +TEST_P(QuicFramerTest, ConnectionCloseFrameWithExtractedInfoIgnoreGCuic) { + SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE); + + // clang-format off + PacketFragments packet = { + // public flags (8 byte connection_id) + {"", + {0x28}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // frame type (connection close frame) + {"", + {0x02}}, + // error code + {"Unable to read connection close error code.", + {0x00, 0x00, 0x00, 0x11}}, + {"Unable to read connection close error details.", + { + // error details length + 0x0, 0x13, + // error details + '1', '7', '7', '6', + '7', ':', 'b', 'e', + 'c', 'a', 'u', 's', + 'e', ' ', 'I', ' ', + 'c', 'a', 'n'} + } + }; + + PacketFragments packet46 = { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // frame type (connection close frame) + {"", + {0x02}}, + // error code + {"Unable to read connection close error code.", + {0x00, 0x00, 0x00, 0x11}}, + {"Unable to read connection close error details.", + { + // error details length + 0x0, 0x13, + // error details + '1', '7', '7', '6', + '7', ':', 'b', 'e', + 'c', 'a', 'u', 's', + 'e', ' ', 'I', ' ', + 'c', 'a', 'n'} + } + }; + + PacketFragments packet99 = { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // frame type (IETF Transport CONNECTION_CLOSE frame) + {"", + {0x1c}}, + // error code + {"Unable to read connection close error code.", + {kVarInt62OneByte + 0x11}}, + {"Unable to read connection close frame type.", + {kVarInt62TwoBytes + 0x12, 0x34 }}, + {"Unable to read connection close error details.", + { + // error details length + kVarInt62OneByte + 0x13, + // error details + '1', '7', '7', '6', + '7', ':', 'b', 'e', + 'c', 'a', 'u', 's', + 'e', ' ', 'I', ' ', + 'c', 'a', 'n'} + } + }; + // clang-format on + + PacketFragments& fragments = + VersionHasIetfQuicFrames(framer_.transport_version()) + ? packet99 + : (framer_.transport_version() >= QUIC_VERSION_46 ? packet46 + : packet); + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(fragments)); + EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption( + *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce, + PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID)); + + EXPECT_EQ(0u, visitor_.stream_frames_.size()); + EXPECT_EQ(0x11u, static_cast<unsigned>( + visitor_.connection_close_frame_.quic_error_code)); + + if (VersionHasIetfQuicFrames(framer_.transport_version())) { + EXPECT_EQ(0x1234u, + visitor_.connection_close_frame_.transport_close_frame_type); + EXPECT_EQ(17767u, visitor_.connection_close_frame_.extracted_error_code); + EXPECT_EQ("because I can", visitor_.connection_close_frame_.error_details); + } else { + EXPECT_EQ(0x11u, visitor_.connection_close_frame_.extracted_error_code); + // Error code is not prepended in GQUIC, so it is not removed and should + // remain in the reason phrase. + EXPECT_EQ("17767:because I can", + visitor_.connection_close_frame_.error_details); } ASSERT_EQ(0u, visitor_.ack_frames_.size()); @@ -4785,49 +4481,79 @@ TEST_P(QuicFramerTest, ApplicationCloseFrame) { CheckFramingBoundaries(packet99, QUIC_INVALID_CONNECTION_CLOSE_DATA); } -TEST_P(QuicFramerTest, GoAwayFrame) { - if (VersionHasIetfQuicFrames(framer_.transport_version())) { - // This frame is not supported in version 99. +// Check that we can extract an error code from an application close. +TEST_P(QuicFramerTest, ApplicationCloseFrameExtract) { + if (!VersionHasIetfQuicFrames(framer_.transport_version())) { + // This frame does not exist in versions other than 99. return; } SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE); + // clang-format off - PacketFragments packet = { - // public flags (8 byte connection_id) + PacketFragments packet99 = { + // type (short header, 4 byte packet number) {"", - {0x28}}, + {0x43}}, // connection_id {"", {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, // packet number {"", {0x12, 0x34, 0x56, 0x78}}, - // frame type (go away frame) + // frame type (IETF_CONNECTION_CLOSE/Application frame) {"", - {0x03}}, + {0x1d}}, // error code - {"Unable to read go away error code.", - {0x00, 0x00, 0x00, 0x09}}, - // stream id - {"Unable to read last good stream id.", - {0x01, 0x02, 0x03, 0x04}}, - // stream id - {"Unable to read goaway reason.", + {"Unable to read connection close error code.", + {kVarInt62OneByte + 0x11}}, + {"Unable to read connection close error details.", { - // error details length - 0x0, 0x0d, - // error details - 'b', 'e', 'c', 'a', - 'u', 's', 'e', ' ', - 'I', ' ', 'c', 'a', - 'n'} + // error details length + kVarInt62OneByte + 0x13, + // error details + '1', '7', '7', '6', + '7', ':', 'b', 'e', + 'c', 'a', 'u', 's', + 'e', ' ', 'I', ' ', + 'c', 'a', 'n'} } }; + // clang-format on - PacketFragments packet44 = { - // type (short header, 4 byte packet number) + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(packet99)); + EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption( + *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce, + PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID)); + + EXPECT_EQ(0u, visitor_.stream_frames_.size()); + + EXPECT_EQ(IETF_QUIC_APPLICATION_CONNECTION_CLOSE, + visitor_.connection_close_frame_.close_type); + EXPECT_EQ(17767u, visitor_.connection_close_frame_.extracted_error_code); + EXPECT_EQ(0x11u, visitor_.connection_close_frame_.quic_error_code); + EXPECT_EQ("because I can", visitor_.connection_close_frame_.error_details); + + ASSERT_EQ(0u, visitor_.ack_frames_.size()); + + CheckFramingBoundaries(packet99, QUIC_INVALID_CONNECTION_CLOSE_DATA); +} + +TEST_P(QuicFramerTest, GoAwayFrame) { + if (VersionHasIetfQuicFrames(framer_.transport_version())) { + // This frame is not supported in version 99. + return; + } + SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE); + // clang-format off + PacketFragments packet = { + // public flags (8 byte connection_id) {"", - {0x32}}, + {0x28}}, // connection_id {"", {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, @@ -4890,9 +4616,7 @@ TEST_P(QuicFramerTest, GoAwayFrame) { // clang-format on PacketFragments& fragments = - framer_.transport_version() > QUIC_VERSION_44 - ? packet46 - : (framer_.transport_version() > QUIC_VERSION_43 ? packet44 : packet); + framer_.transport_version() >= QUIC_VERSION_46 ? packet46 : packet; std::unique_ptr<QuicEncryptedPacket> encrypted( AssemblePacketFromFragments(fragments)); EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); @@ -4940,28 +4664,6 @@ TEST_P(QuicFramerTest, WindowUpdateFrame) { 0x32, 0x10, 0x76, 0x54}}, }; - PacketFragments packet44 = { - // type (short header, 4 byte packet number) - {"", - {0x32}}, - // connection_id - {"", - {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, - // packet number - {"", - {0x12, 0x34, 0x56, 0x78}}, - // frame type (window update frame) - {"", - {0x04}}, - // stream id - {"Unable to read stream_id.", - {0x01, 0x02, 0x03, 0x04}}, - // byte offset - {"Unable to read window byte_offset.", - {0x3A, 0x98, 0xFE, 0xDC, - 0x32, 0x10, 0x76, 0x54}}, - }; - PacketFragments packet46 = { // type (short header, 4 byte packet number) {"", @@ -4987,9 +4689,7 @@ TEST_P(QuicFramerTest, WindowUpdateFrame) { // clang-format on PacketFragments& fragments = - framer_.transport_version() > QUIC_VERSION_44 - ? packet46 - : (framer_.transport_version() > QUIC_VERSION_43 ? packet44 : packet); + framer_.transport_version() >= QUIC_VERSION_46 ? packet46 : packet; std::unique_ptr<QuicEncryptedPacket> encrypted( AssemblePacketFromFragments(fragments)); EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); @@ -5117,24 +4817,6 @@ TEST_P(QuicFramerTest, BlockedFrame) { {0x01, 0x02, 0x03, 0x04}}, }; - PacketFragments packet44 = { - // type (short header, 4 byte packet number) - {"", - {0x32}}, - // connection_id - {"", - {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, - // packet number - {"", - {0x12, 0x34, 0x56, 0x78}}, - // frame type (blocked frame) - {"", - {0x05}}, - // stream id - {"Unable to read stream_id.", - {0x01, 0x02, 0x03, 0x04}}, - }; - PacketFragments packet46 = { // type (short header, 4 byte packet number) {"", @@ -5178,10 +4860,8 @@ TEST_P(QuicFramerTest, BlockedFrame) { PacketFragments& fragments = VersionHasIetfQuicFrames(framer_.transport_version()) ? packet99 - : (framer_.transport_version() > QUIC_VERSION_44 - ? packet46 - : (framer_.transport_version() > QUIC_VERSION_43 ? packet44 - : packet)); + : (framer_.transport_version() >= QUIC_VERSION_46 ? packet46 + : packet); std::unique_ptr<QuicEncryptedPacket> encrypted( AssemblePacketFromFragments(fragments)); EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); @@ -5221,18 +4901,6 @@ TEST_P(QuicFramerTest, PingFrame) { 0x07, }; - unsigned char packet44[] = { - // type (short header, 4 byte packet number) - 0x32, - // connection_id - 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, - // packet number - 0x12, 0x34, 0x56, 0x78, - - // frame type - 0x07, - }; - unsigned char packet46[] = { // type (short header, 4 byte packet number) 0x43, @@ -5261,18 +4929,13 @@ TEST_P(QuicFramerTest, PingFrame) { QuicEncryptedPacket encrypted( AsChars(VersionHasIetfQuicFrames(framer_.transport_version()) ? packet99 - : (framer_.transport_version() > QUIC_VERSION_44 - ? packet46 - : framer_.transport_version() > QUIC_VERSION_43 - ? packet44 - : packet)), + : (framer_.transport_version() >= QUIC_VERSION_46 ? packet46 + : packet)), VersionHasIetfQuicFrames(framer_.transport_version()) ? QUIC_ARRAYSIZE(packet99) - : (framer_.transport_version() > QUIC_VERSION_44 + : (framer_.transport_version() >= QUIC_VERSION_46 ? QUIC_ARRAYSIZE(packet46) - : framer_.transport_version() > QUIC_VERSION_43 - ? QUIC_ARRAYSIZE(packet44) - : QUIC_ARRAYSIZE(packet)), + : QUIC_ARRAYSIZE(packet)), false); EXPECT_TRUE(framer_.ProcessPacket(encrypted)); @@ -5288,38 +4951,11 @@ TEST_P(QuicFramerTest, PingFrame) { } TEST_P(QuicFramerTest, MessageFrame) { - if (framer_.transport_version() <= QUIC_VERSION_44) { + if (framer_.transport_version() <= QUIC_VERSION_43) { return; } SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE); // clang-format off - PacketFragments packet45 = { - // type (short header, 4 byte packet number) - {"", - {0x32}}, - // connection_id - {"", - {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, - // packet number - {"", - {0x12, 0x34, 0x56, 0x78}}, - // message frame type. - {"", - { 0x21 }}, - // message length - {"Unable to read message length", - {0x07}}, - // message data - {"Unable to read message data", - {'m', 'e', 's', 's', 'a', 'g', 'e'}}, - // message frame no length. - {"", - { 0x20 }}, - // message data - {{}, - {'m', 'e', 's', 's', 'a', 'g', 'e', '2'}}, - }; - PacketFragments packet46 = { // type (short header, 4 byte packet number) {"", @@ -5348,8 +4984,8 @@ TEST_P(QuicFramerTest, MessageFrame) { }; // clang-format on - std::unique_ptr<QuicEncryptedPacket> encrypted(AssemblePacketFromFragments( - framer_.transport_version() > QUIC_VERSION_44 ? packet46 : packet45)); + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(packet46)); EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); @@ -5362,9 +4998,7 @@ TEST_P(QuicFramerTest, MessageFrame) { EXPECT_EQ(7u, visitor_.message_frames_[0]->message_length); EXPECT_EQ(8u, visitor_.message_frames_[1]->message_length); - CheckFramingBoundaries( - framer_.transport_version() > QUIC_VERSION_44 ? packet46 : packet45, - QUIC_INVALID_MESSAGE_DATA); + CheckFramingBoundaries(packet46, QUIC_INVALID_MESSAGE_DATA); } TEST_P(QuicFramerTest, PublicResetPacketV33) { @@ -5578,8 +5212,6 @@ TEST_P(QuicFramerTest, IetfStatelessResetPacket) { unsigned char packet[] = { // type (short packet, 1 byte packet number) 0x50, - // connection_id - 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, // Random bytes 0x01, 0x11, 0x02, 0x22, 0x03, 0x33, 0x04, 0x44, 0x01, 0x11, 0x02, 0x22, 0x03, 0x33, 0x04, 0x44, @@ -5594,6 +5226,8 @@ TEST_P(QuicFramerTest, IetfStatelessResetPacket) { return; } QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT); + QuicFramerPeer::SetLastSerializedServerConnectionId(&framer_, + TestConnectionId(0x33)); decrypter_ = new test::TestDecrypter(); if (framer_.version().KnowsWhichDecrypterToUse()) { framer_.InstallDecrypter(ENCRYPTION_INITIAL, QuicMakeUnique<NullDecrypter>( @@ -5620,8 +5254,11 @@ TEST_P(QuicFramerTest, IetfStatelessResetPacketInvalidStatelessResetToken) { unsigned char packet[] = { // type (short packet, 1 byte packet number) 0x50, - // connection_id - 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // Random bytes + 0x01, 0x11, 0x02, 0x22, 0x03, 0x33, 0x04, 0x44, + 0x01, 0x11, 0x02, 0x22, 0x03, 0x33, 0x04, 0x44, + 0x01, 0x11, 0x02, 0x22, 0x03, 0x33, 0x04, 0x44, + 0x01, 0x11, 0x02, 0x22, 0x03, 0x33, 0x04, 0x44, // stateless reset token 0xB6, 0x69, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -5630,6 +5267,8 @@ TEST_P(QuicFramerTest, IetfStatelessResetPacketInvalidStatelessResetToken) { if (framer_.transport_version() <= QUIC_VERSION_43) { return; } + QuicFramerPeer::SetLastSerializedServerConnectionId(&framer_, + TestConnectionId(0x33)); QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT); decrypter_ = new test::TestDecrypter(); if (framer_.version().KnowsWhichDecrypterToUse()) { @@ -5665,7 +5304,7 @@ TEST_P(QuicFramerTest, VersionNegotiationPacketClient) { 'Q', '2', '.', '0'}}, }; - PacketFragments packet44 = { + PacketFragments packet46 = { // type (long header) {"", {0x8F}}, @@ -5682,12 +5321,34 @@ TEST_P(QuicFramerTest, VersionNegotiationPacketClient) { {QUIC_VERSION_BYTES, 'Q', '2', '.', '0'}}, }; + + PacketFragments packet99 = { + // type (long header) + {"", + {0x8F}}, + // version tag + {"", + {0x00, 0x00, 0x00, 0x00}}, + {"", + {0x08}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + {"", + {0x00}}, + // Supported versions + {"Unable to read supported version in negotiation.", + {QUIC_VERSION_BYTES, + 'Q', '2', '.', '0'}}, + }; // clang-format on QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT); PacketFragments& fragments = - framer_.transport_version() > QUIC_VERSION_43 ? packet44 : packet; + framer_.transport_version() >= QUIC_VERSION_99 + ? packet99 + : framer_.transport_version() > QUIC_VERSION_43 ? packet46 : packet; std::unique_ptr<QuicEncryptedPacket> encrypted( AssemblePacketFromFragments(fragments)); EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); @@ -5706,7 +5367,7 @@ TEST_P(QuicFramerTest, VersionNegotiationPacketClient) { } TEST_P(QuicFramerTest, VersionNegotiationPacketServer) { - if (framer_.transport_version() < QUIC_VERSION_44) { + if (framer_.transport_version() <= QUIC_VERSION_43) { return; } @@ -5725,9 +5386,30 @@ TEST_P(QuicFramerTest, VersionNegotiationPacketServer) { QUIC_VERSION_BYTES, 'Q', '2', '.', '0', }; + unsigned char packet2[] = { + // public flags (long header with all ignored bits set) + 0xFF, + // version + 0x00, 0x00, 0x00, 0x00, + // destination connection ID length + 0x08, + // destination connection ID + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x11, + // source connection ID length + 0x00, + // supported versions + QUIC_VERSION_BYTES, + 'Q', '2', '.', '0', + }; // clang-format on + unsigned char* p = packet; + size_t p_length = QUIC_ARRAYSIZE(packet); + if (framer_.version().HasLengthPrefixedConnectionIds()) { + p = packet2; + p_length = QUIC_ARRAYSIZE(packet2); + } - QuicEncryptedPacket encrypted(AsChars(packet), QUIC_ARRAYSIZE(packet), false); + QuicEncryptedPacket encrypted(AsChars(p), p_length, false); EXPECT_FALSE(framer_.ProcessPacket(encrypted)); EXPECT_EQ(QUIC_INVALID_VERSION_NEGOTIATION_PACKET, framer_.error()); EXPECT_EQ("Server received version negotiation packet.", @@ -5796,9 +5478,34 @@ TEST_P(QuicFramerTest, ParseIetfRetryPacket) { 'H', 'e', 'l', 'l', 'o', ' ', 't', 'h', 'i', 's', ' ', 'i', 's', ' ', 'R', 'E', 'T', 'R', 'Y', '!', }; + unsigned char packet99[] = { + // public flags (long header with packet type RETRY) + 0xF0, + // version + QUIC_VERSION_BYTES, + // destination connection ID length + 0x00, + // source connection ID length + 0x08, + // source connection ID + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x11, + // original destination connection ID length + 0x08, + // original destination connection ID + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // retry token + 'H', 'e', 'l', 'l', 'o', ' ', 't', 'h', 'i', 's', + ' ', 'i', 's', ' ', 'R', 'E', 'T', 'R', 'Y', '!', + }; // clang-format on - QuicEncryptedPacket encrypted(AsChars(packet), QUIC_ARRAYSIZE(packet), false); + unsigned char* p = packet; + size_t p_length = QUIC_ARRAYSIZE(packet); + if (framer_.transport_version() == QUIC_VERSION_99) { + p = packet99; + p_length = QUIC_ARRAYSIZE(packet99); + } + QuicEncryptedPacket encrypted(AsChars(p), p_length, false); EXPECT_TRUE(framer_.ProcessPacket(encrypted)); EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); @@ -5828,7 +5535,7 @@ TEST_P(QuicFramerTest, RejectIetfRetryPacketAsServer) { // version QUIC_VERSION_BYTES, // connection ID lengths - 0x05, + 0x00, 0x08, // source connection ID 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x11, // original destination connection ID @@ -5870,19 +5577,6 @@ TEST_P(QuicFramerTest, BuildPaddingFramePacket) { 0x00, 0x00, 0x00, 0x00 }; - unsigned char packet44[kMaxOutgoingPacketSize] = { - // type (short header, 4 byte packet number) - 0x32, - // connection_id - 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, - // packet number - 0x12, 0x34, 0x56, 0x78, - - // frame type (padding frame) - 0x00, - 0x00, 0x00, 0x00, 0x00 - }; - unsigned char packet46[kMaxOutgoingPacketSize] = { // type (short header, 4 byte packet number) 0x43, @@ -5913,10 +5607,8 @@ TEST_P(QuicFramerTest, BuildPaddingFramePacket) { unsigned char* p = packet; if (VersionHasIetfQuicFrames(framer_.transport_version())) { p = packet99; - } else if (framer_.transport_version() > QUIC_VERSION_44) { + } else if (framer_.transport_version() >= QUIC_VERSION_46) { p = packet46; - } else if (framer_.transport_version() > QUIC_VERSION_43) { - p = packet44; } uint64_t header_size = GetPacketHeaderSize( @@ -5931,7 +5623,7 @@ TEST_P(QuicFramerTest, BuildPaddingFramePacket) { test::CompareCharArraysWithHexError( "constructed packet", data->data(), data->length(), AsChars(p), - framer_.transport_version() > QUIC_VERSION_43 ? QUIC_ARRAYSIZE(packet44) + framer_.transport_version() > QUIC_VERSION_43 ? QUIC_ARRAYSIZE(packet46) : QUIC_ARRAYSIZE(packet)); } @@ -5976,33 +5668,6 @@ TEST_P(QuicFramerTest, BuildStreamFramePacketWithNewPaddingFrame) { 0x00, 0x00, }; - unsigned char packet44[] = { - // type (short header, 4 byte packet number) - 0x32, - // connection_id - 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, - // packet number - 0x12, 0x34, 0x56, 0x78, - - // paddings - 0x00, 0x00, - // frame type (stream frame with fin) - 0xFF, - // stream id - 0x01, 0x02, 0x03, 0x04, - // offset - 0x3A, 0x98, 0xFE, 0xDC, - 0x32, 0x10, 0x76, 0x54, - // data length - 0x00, 0x0c, - // data - 'h', 'e', 'l', 'l', - 'o', ' ', 'w', 'o', - 'r', 'l', 'd', '!', - // paddings - 0x00, 0x00, - }; - unsigned char packet46[] = { // type (short header, 4 byte packet number) 0x43, @@ -6066,12 +5731,9 @@ TEST_P(QuicFramerTest, BuildStreamFramePacketWithNewPaddingFrame) { if (VersionHasIetfQuicFrames(framer_.transport_version())) { p = packet99; p_size = QUIC_ARRAYSIZE(packet99); - } else if (framer_.transport_version() > QUIC_VERSION_44) { + } else if (framer_.transport_version() >= QUIC_VERSION_46) { p = packet46; p_size = QUIC_ARRAYSIZE(packet46); - } else if (framer_.transport_version() > QUIC_VERSION_43) { - p = packet44; - p_size = QUIC_ARRAYSIZE(packet44); } QuicEncryptedPacket encrypted(AsChars(p), p_size, false); @@ -6104,19 +5766,6 @@ TEST_P(QuicFramerTest, Build4ByteSequenceNumberPaddingFramePacket) { 0x00, 0x00, 0x00, 0x00 }; - unsigned char packet44[kMaxOutgoingPacketSize] = { - // type (short header, 4 byte packet number) - 0x32, - // connection_id - 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, - // packet number - 0x12, 0x34, 0x56, 0x78, - - // frame type (padding frame) - 0x00, - 0x00, 0x00, 0x00, 0x00 - }; - unsigned char packet46[kMaxOutgoingPacketSize] = { // type (short header, 4 byte packet number) 0x43, @@ -6147,10 +5796,8 @@ TEST_P(QuicFramerTest, Build4ByteSequenceNumberPaddingFramePacket) { unsigned char* p = packet; if (VersionHasIetfQuicFrames(framer_.transport_version())) { p = packet99; - } else if (framer_.transport_version() > QUIC_VERSION_44) { + } else if (framer_.transport_version() >= QUIC_VERSION_46) { p = packet46; - } else if (framer_.transport_version() > QUIC_VERSION_43) { - p = packet44; } uint64_t header_size = GetPacketHeaderSize( @@ -6165,7 +5812,7 @@ TEST_P(QuicFramerTest, Build4ByteSequenceNumberPaddingFramePacket) { test::CompareCharArraysWithHexError( "constructed packet", data->data(), data->length(), AsChars(p), - framer_.transport_version() > QUIC_VERSION_43 ? QUIC_ARRAYSIZE(packet44) + framer_.transport_version() > QUIC_VERSION_43 ? QUIC_ARRAYSIZE(packet46) : QUIC_ARRAYSIZE(packet)); } @@ -6194,19 +5841,6 @@ TEST_P(QuicFramerTest, Build2ByteSequenceNumberPaddingFramePacket) { 0x00, 0x00, 0x00, 0x00 }; - unsigned char packet44[kMaxOutgoingPacketSize] = { - // type (short header, 2 byte packet number) - 0x31, - // connection_id - 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, - // packet number - 0x56, 0x78, - - // frame type (padding frame) - 0x00, - 0x00, 0x00, 0x00, 0x00 - }; - unsigned char packet46[kMaxOutgoingPacketSize] = { // type (short header, 2 byte packet number) 0x41, @@ -6237,10 +5871,8 @@ TEST_P(QuicFramerTest, Build2ByteSequenceNumberPaddingFramePacket) { unsigned char* p = packet; if (VersionHasIetfQuicFrames(framer_.transport_version())) { p = packet99; - } else if (framer_.transport_version() > QUIC_VERSION_44) { + } else if (framer_.transport_version() >= QUIC_VERSION_46) { p = packet46; - } else if (framer_.transport_version() > QUIC_VERSION_43) { - p = packet44; } uint64_t header_size = GetPacketHeaderSize( @@ -6255,7 +5887,7 @@ TEST_P(QuicFramerTest, Build2ByteSequenceNumberPaddingFramePacket) { test::CompareCharArraysWithHexError( "constructed packet", data->data(), data->length(), AsChars(p), - framer_.transport_version() > QUIC_VERSION_43 ? QUIC_ARRAYSIZE(packet44) + framer_.transport_version() > QUIC_VERSION_43 ? QUIC_ARRAYSIZE(packet46) : QUIC_ARRAYSIZE(packet)); } @@ -6284,19 +5916,6 @@ TEST_P(QuicFramerTest, Build1ByteSequenceNumberPaddingFramePacket) { 0x00, 0x00, 0x00, 0x00 }; - unsigned char packet44[kMaxOutgoingPacketSize] = { - // type (short header, 1 byte packet number) - 0x30, - // connection_id - 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, - // packet number - 0x78, - - // frame type (padding frame) - 0x00, - 0x00, 0x00, 0x00, 0x00 - }; - unsigned char packet46[kMaxOutgoingPacketSize] = { // type (short header, 1 byte packet number) 0x40, @@ -6327,10 +5946,8 @@ TEST_P(QuicFramerTest, Build1ByteSequenceNumberPaddingFramePacket) { unsigned char* p = packet; if (VersionHasIetfQuicFrames(framer_.transport_version())) { p = packet99; - } else if (framer_.transport_version() > QUIC_VERSION_44) { + } else if (framer_.transport_version() >= QUIC_VERSION_46) { p = packet46; - } else if (framer_.transport_version() > QUIC_VERSION_43) { - p = packet44; } uint64_t header_size = GetPacketHeaderSize( @@ -6345,7 +5962,7 @@ TEST_P(QuicFramerTest, Build1ByteSequenceNumberPaddingFramePacket) { test::CompareCharArraysWithHexError( "constructed packet", data->data(), data->length(), AsChars(p), - framer_.transport_version() > QUIC_VERSION_43 ? QUIC_ARRAYSIZE(packet44) + framer_.transport_version() > QUIC_VERSION_43 ? QUIC_ARRAYSIZE(packet46) : QUIC_ARRAYSIZE(packet)); } @@ -6387,27 +6004,6 @@ TEST_P(QuicFramerTest, BuildStreamFramePacket) { 'r', 'l', 'd', '!', }; - unsigned char packet44[] = { - // type (short header, 4 byte packet number) - 0x32, - // connection_id - 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, - // packet number - 0x12, 0x34, 0x56, 0x78, - - // frame type (stream frame with fin and no length) - 0xDF, - // stream id - 0x01, 0x02, 0x03, 0x04, - // offset - 0x3A, 0x98, 0xFE, 0xDC, - 0x32, 0x10, 0x76, 0x54, - // data - 'h', 'e', 'l', 'l', - 'o', ' ', 'w', 'o', - 'r', 'l', 'd', '!', - }; - unsigned char packet46[] = { // type (short header, 4 byte packet number) 0x43, @@ -6459,12 +6055,9 @@ TEST_P(QuicFramerTest, BuildStreamFramePacket) { if (VersionHasIetfQuicFrames(framer_.transport_version())) { p = packet99; p_size = QUIC_ARRAYSIZE(packet99); - } else if (framer_.transport_version() > QUIC_VERSION_44) { + } else if (framer_.transport_version() >= QUIC_VERSION_46) { p = packet46; p_size = QUIC_ARRAYSIZE(packet46); - } else if (framer_.transport_version() > QUIC_VERSION_43) { - p = packet44; - p_size = QUIC_ARRAYSIZE(packet44); } test::CompareCharArraysWithHexError("constructed packet", data->data(), data->length(), AsChars(p), p_size); @@ -6508,28 +6101,6 @@ TEST_P(QuicFramerTest, BuildStreamFramePacketWithVersionFlag) { 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '!', }; - unsigned char packet44[] = { - // type (long header with packet type ZERO_RTT_PROTECTED) - 0xFC, - // version tag - QUIC_VERSION_BYTES, - // connection_id length - 0x50, - // connection_id - 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, - // packet number - 0x12, 0x34, 0x56, 0x78, - - // frame type (stream frame with fin and no length) - 0xDF, - // stream id - 0x01, 0x02, 0x03, 0x04, - // offset - 0x3A, 0x98, 0xFE, 0xDC, 0x32, 0x10, 0x76, 0x54, - // data - 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '!', - }; - unsigned char packet46[] = { // type (long header with packet type ZERO_RTT_PROTECTED) 0xD3, @@ -6557,10 +6128,12 @@ TEST_P(QuicFramerTest, BuildStreamFramePacketWithVersionFlag) { 0xD3, // version tag QUIC_VERSION_BYTES, - // connection_id length - 0x50, - // connection_id + // destination connection ID length + 0x08, + // destination connection ID 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // source connection ID length + 0x00, // length 0x40, 0x1D, // packet number @@ -6586,12 +6159,9 @@ TEST_P(QuicFramerTest, BuildStreamFramePacketWithVersionFlag) { if (VersionHasIetfQuicFrames(framer_.transport_version())) { p = packet99; p_size = QUIC_ARRAYSIZE(packet99); - } else if (framer_.transport_version() > QUIC_VERSION_44) { + } else if (framer_.transport_version() >= QUIC_VERSION_46) { p = packet46; p_size = QUIC_ARRAYSIZE(packet46); - } else if (framer_.transport_version() > QUIC_VERSION_43) { - p = packet44; - p_size = QUIC_ARRAYSIZE(packet44); } test::CompareCharArraysWithHexError("constructed packet", data->data(), data->length(), AsChars(p), p_size); @@ -6773,7 +6343,7 @@ TEST_P(QuicFramerTest, BuildVersionNegotiationPacket) { 0xDA, 0x5A, 0x3A, 0x3A, QUIC_VERSION_BYTES, }; - unsigned char packet44[] = { + unsigned char packet46[] = { // type (long header) 0xC0, // version tag @@ -6786,12 +6356,30 @@ TEST_P(QuicFramerTest, BuildVersionNegotiationPacket) { 0xDA, 0x5A, 0x3A, 0x3A, QUIC_VERSION_BYTES, }; + unsigned char packet99[] = { + // type (long header) + 0xC0, + // version tag + 0x00, 0x00, 0x00, 0x00, + // destination connection ID length + 0x00, + // source connection ID length + 0x08, + // source connection ID + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // supported versions + 0xDA, 0x5A, 0x3A, 0x3A, + QUIC_VERSION_BYTES, + }; // clang-format on unsigned char* p = packet; size_t p_size = QUIC_ARRAYSIZE(packet); - if (framer_.transport_version() > QUIC_VERSION_43) { - p = packet44; - p_size = QUIC_ARRAYSIZE(packet44); + if (framer_.transport_version() >= QUIC_VERSION_99) { + p = packet99; + p_size = QUIC_ARRAYSIZE(packet99); + } else if (framer_.transport_version() > QUIC_VERSION_43) { + p = packet46; + p_size = QUIC_ARRAYSIZE(packet46); } QuicConnectionId connection_id = FramerTestConnectionId(); @@ -6799,19 +6387,17 @@ TEST_P(QuicFramerTest, BuildVersionNegotiationPacket) { QuicFramer::BuildVersionNegotiationPacket( connection_id, EmptyQuicConnectionId(), framer_.transport_version() > QUIC_VERSION_43, + framer_.version().HasLengthPrefixedConnectionIds(), SupportedVersions(GetParam()))); test::CompareCharArraysWithHexError("constructed packet", data->data(), data->length(), AsChars(p), p_size); } TEST_P(QuicFramerTest, BuildVersionNegotiationPacketWithClientConnectionId) { - if (framer_.transport_version() <= QUIC_VERSION_43) { - // The GQUIC encoding does not support encoding client connection IDs. + if (!framer_.version().SupportsClientConnectionIds()) { return; } - // Client connection IDs cannot be used unless this flag is true. - SetQuicRestartFlag(quic_do_not_override_connection_id, true); SetQuicReloadableFlag(quic_version_negotiation_grease, true); SetQuicFlag(FLAGS_quic_disable_version_negotiation_grease_randomness, true); @@ -6821,11 +6407,11 @@ TEST_P(QuicFramerTest, BuildVersionNegotiationPacketWithClientConnectionId) { 0xC0, // version tag 0x00, 0x00, 0x00, 0x00, - // connection ID lengths - 0x55, // client/destination connection ID + 0x08, 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x11, // server/source connection ID + 0x08, 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, // supported versions 0xDA, 0x5A, 0x3A, 0x3A, @@ -6836,9 +6422,9 @@ TEST_P(QuicFramerTest, BuildVersionNegotiationPacketWithClientConnectionId) { QuicConnectionId server_connection_id = FramerTestConnectionId(); QuicConnectionId client_connection_id = FramerTestConnectionIdPlusOne(); std::unique_ptr<QuicEncryptedPacket> data( - QuicFramer::BuildVersionNegotiationPacket(server_connection_id, - client_connection_id, true, - SupportedVersions(GetParam()))); + QuicFramer::BuildVersionNegotiationPacket( + server_connection_id, client_connection_id, true, true, + SupportedVersions(GetParam()))); test::CompareCharArraysWithHexError("constructed packet", data->data(), data->length(), AsChars(packet), QUIC_ARRAYSIZE(packet)); @@ -6880,27 +6466,6 @@ TEST_P(QuicFramerTest, BuildAckFramePacketOneAckBlock) { 0x00, }; - unsigned char packet44[] = { - // type (short header, 4 byte packet number) - 0x32, - // connection_id - 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, - // packet number - 0x12, 0x34, 0x56, 0x78, - - // frame type (ack frame) - // (no ack blocks, 2 byte largest observed, 2 byte block length) - 0x45, - // largest acked - 0x12, 0x34, - // Zero delta time. - 0x00, 0x00, - // first ack block length. - 0x12, 0x34, - // num timestamps. - 0x00, - }; - unsigned char packet46[] = { // type (short header, 4 byte packet number) 0x43, @@ -6947,12 +6512,9 @@ TEST_P(QuicFramerTest, BuildAckFramePacketOneAckBlock) { if (VersionHasIetfQuicFrames(framer_.transport_version())) { p = packet99; p_size = QUIC_ARRAYSIZE(packet99); - } else if (framer_.transport_version() > QUIC_VERSION_44) { + } else if (framer_.transport_version() >= QUIC_VERSION_46) { p = packet46; p_size = QUIC_ARRAYSIZE(packet46); - } else if (framer_.transport_version() > QUIC_VERSION_43) { - p = packet44; - p_size = QUIC_ARRAYSIZE(packet44); } std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); @@ -6996,27 +6558,6 @@ TEST_P(QuicFramerTest, BuildAckFramePacketOneAckBlockMaxLength) { 0x00, }; - unsigned char packet44[] = { - // type (short header, 4 byte packet number) - 0x32, - // connection_id - 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, - // packet number - 0x12, 0x34, 0x56, 0x78, - - // frame type (ack frame) - // (no ack blocks, 4 byte largest observed, 4 byte block length) - 0x4A, - // largest acked - 0x12, 0x34, 0x56, 0x78, - // Zero delta time. - 0x00, 0x00, - // first ack block length. - 0x12, 0x34, 0x56, 0x78, - // num timestamps. - 0x00, - }; - unsigned char packet46[] = { // type (short header, 4 byte packet number) 0x43, @@ -7064,12 +6605,9 @@ TEST_P(QuicFramerTest, BuildAckFramePacketOneAckBlockMaxLength) { if (VersionHasIetfQuicFrames(framer_.transport_version())) { p = packet99; p_size = QUIC_ARRAYSIZE(packet99); - } else if (framer_.transport_version() > QUIC_VERSION_44) { + } else if (framer_.transport_version() >= QUIC_VERSION_46) { p = packet46; p_size = QUIC_ARRAYSIZE(packet46); - } else if (framer_.transport_version() > QUIC_VERSION_43) { - p = packet44; - p_size = QUIC_ARRAYSIZE(packet44); } std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); @@ -7136,45 +6674,6 @@ TEST_P(QuicFramerTest, BuildAckFramePacketMultipleAckBlocks) { 0x00, }; - unsigned char packet44[] = { - // type (short header, 4 byte packet number) - 0x32, - // connection_id - 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, - // packet number - 0x12, 0x34, 0x56, 0x78, - - // frame type (ack frame) - // (has ack blocks, 2 byte largest observed, 2 byte block length) - 0x65, - // largest acked - 0x12, 0x34, - // Zero delta time. - 0x00, 0x00, - // num ack blocks ranges. - 0x04, - // first ack block length. - 0x00, 0x01, - // gap to next block. - 0x01, - // ack block length. - 0x0e, 0xaf, - // gap to next block. - 0xff, - // ack block length. - 0x00, 0x00, - // gap to next block. - 0x91, - // ack block length. - 0x01, 0xea, - // gap to next block. - 0x05, - // ack block length. - 0x00, 0x04, - // num timestamps. - 0x00, - }; - unsigned char packet46[] = { // type (short header, 4 byte packet number) 0x43, @@ -7254,12 +6753,9 @@ TEST_P(QuicFramerTest, BuildAckFramePacketMultipleAckBlocks) { if (VersionHasIetfQuicFrames(framer_.transport_version())) { p = packet99; p_size = QUIC_ARRAYSIZE(packet99); - } else if (framer_.transport_version() > QUIC_VERSION_44) { + } else if (framer_.transport_version() >= QUIC_VERSION_46) { p = packet46; p_size = QUIC_ARRAYSIZE(packet46); - } else if (framer_.transport_version() > QUIC_VERSION_43) { - p = packet44; - p_size = QUIC_ARRAYSIZE(packet44); } std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); @@ -7383,99 +6879,6 @@ TEST_P(QuicFramerTest, BuildAckFramePacketMaxAckBlocks) { 0x00, }; - unsigned char packet44[] = { - // type (short header, 4 byte packet number) - 0x32, - // connection_id - 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, - // packet number - 0x12, 0x34, 0x56, 0x78, - // frame type (ack frame) - // (has ack blocks, 2 byte largest observed, 2 byte block length) - 0x65, - // largest acked - 0x12, 0x34, - // Zero delta time. - 0x00, 0x00, - // num ack blocks ranges. - 0xff, - // first ack block length. - 0x0f, 0xdd, - // 255 = 4 * 63 + 3 - 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, - - 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, - - 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, - - 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, - - 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, - - 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, - - 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, - // num timestamps. - 0x00, - }; - unsigned char packet46[] = { // type (short header, 4 byte packet number) 0x43, @@ -7659,12 +7062,9 @@ TEST_P(QuicFramerTest, BuildAckFramePacketMaxAckBlocks) { if (VersionHasIetfQuicFrames(framer_.transport_version())) { p = packet99; p_size = QUIC_ARRAYSIZE(packet99); - } else if (framer_.transport_version() > QUIC_VERSION_44) { + } else if (framer_.transport_version() >= QUIC_VERSION_46) { p = packet46; p_size = QUIC_ARRAYSIZE(packet46); - } else if (framer_.transport_version() > QUIC_VERSION_43) { - p = packet44; - p_size = QUIC_ARRAYSIZE(packet44); } std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); @@ -7752,25 +7152,6 @@ TEST_P(QuicFramerTest, BuildRstFramePacketQuic) { 0x05, 0x06, 0x07, 0x08, }; - unsigned char packet44[] = { - // type (short packet, 4 byte packet number) - 0x32, - // connection_id - 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, - // packet number - 0x12, 0x34, 0x56, 0x78, - - // frame type (rst stream frame) - 0x01, - // stream id - 0x01, 0x02, 0x03, 0x04, - // sent byte offset - 0x08, 0x07, 0x06, 0x05, - 0x04, 0x03, 0x02, 0x01, - // error code - 0x05, 0x06, 0x07, 0x08, - }; - unsigned char packet46[] = { // type (short packet, 4 byte packet number) 0x43, @@ -7819,12 +7200,9 @@ TEST_P(QuicFramerTest, BuildRstFramePacketQuic) { if (VersionHasIetfQuicFrames(framer_.transport_version())) { p = packet99; p_size = QUIC_ARRAYSIZE(packet99); - } else if (framer_.transport_version() > QUIC_VERSION_44) { + } else if (framer_.transport_version() >= QUIC_VERSION_46) { p = packet46; p_size = QUIC_ARRAYSIZE(packet46); - } else if (framer_.transport_version() > QUIC_VERSION_43) { - p = packet44; - p_size = QUIC_ARRAYSIZE(packet44); } QuicEncryptedPacket encrypted(AsChars(p), p_size, false); @@ -7875,9 +7253,99 @@ TEST_P(QuicFramerTest, BuildCloseFramePacket) { 'n', }; - unsigned char packet44[] = { + unsigned char packet46[] = { // type (short header, 4 byte packet number) - 0x32, + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (connection close frame) + 0x02, + // error code + 0x05, 0x06, 0x07, 0x08, + // error details length + 0x00, 0x0d, + // error details + 'b', 'e', 'c', 'a', + 'u', 's', 'e', ' ', + 'I', ' ', 'c', 'a', + 'n', + }; + + unsigned char packet99[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (IETF_CONNECTION_CLOSE frame) + 0x1c, + // error code + kVarInt62OneByte + 0x11, + // Frame type within the CONNECTION_CLOSE frame + kVarInt62OneByte + 0x05, + // error details length + kVarInt62OneByte + 0x0d, + // error details + 'b', 'e', 'c', 'a', + 'u', 's', 'e', ' ', + 'I', ' ', 'c', 'a', + 'n', + }; + // clang-format on + + unsigned char* p = packet; + size_t p_size = QUIC_ARRAYSIZE(packet); + if (VersionHasIetfQuicFrames(framer_.transport_version())) { + p = packet99; + p_size = QUIC_ARRAYSIZE(packet99); + } else if (framer_.transport_version() >= QUIC_VERSION_46) { + p = packet46; + p_size = QUIC_ARRAYSIZE(packet46); + } + + std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != nullptr); + + test::CompareCharArraysWithHexError("constructed packet", data->data(), + data->length(), AsChars(p), p_size); +} + +TEST_P(QuicFramerTest, BuildCloseFramePacketExtendedInfo) { + QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT); + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + + QuicConnectionCloseFrame close_frame; + if (VersionHasIetfQuicFrames(framer_.transport_version())) { + close_frame.transport_error_code = + static_cast<QuicIetfTransportErrorCodes>(0x11); + close_frame.transport_close_frame_type = 0x05; + close_frame.close_type = IETF_QUIC_TRANSPORT_CONNECTION_CLOSE; + } else { + close_frame.quic_error_code = static_cast<QuicErrorCode>(0x05060708); + } + // Set this so that it is "there" for both Google QUIC and IETF QUIC + // framing. It better not show up for Google QUIC! + close_frame.extracted_error_code = static_cast<QuicErrorCode>(0x4567); + + // For IETF QUIC this will be prefaced with "17767:" + // (17767 == 0x4567). + close_frame.error_details = "because I can"; + + QuicFrames frames = {QuicFrame(&close_frame)}; + + // clang-format off + unsigned char packet[] = { + // public flags (8 byte connection_id) + 0x2C, // connection_id 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, // packet number @@ -7932,12 +7400,13 @@ TEST_P(QuicFramerTest, BuildCloseFramePacket) { // Frame type within the CONNECTION_CLOSE frame kVarInt62OneByte + 0x05, // error details length - kVarInt62OneByte + 0x0d, + kVarInt62OneByte + 0x13, // error details - 'b', 'e', 'c', 'a', - 'u', 's', 'e', ' ', - 'I', ' ', 'c', 'a', - 'n', + '1', '7', '7', '6', + '7', ':', 'b', 'e', + 'c', 'a', 'u', 's', + 'e', ' ', 'I', ' ', + 'c', 'a', 'n' }; // clang-format on @@ -7946,12 +7415,9 @@ TEST_P(QuicFramerTest, BuildCloseFramePacket) { if (VersionHasIetfQuicFrames(framer_.transport_version())) { p = packet99; p_size = QUIC_ARRAYSIZE(packet99); - } else if (framer_.transport_version() > QUIC_VERSION_44) { + } else if (framer_.transport_version() >= QUIC_VERSION_46) { p = packet46; p_size = QUIC_ARRAYSIZE(packet46); - } else if (framer_.transport_version() > QUIC_VERSION_43) { - p = packet44; - p_size = QUIC_ARRAYSIZE(packet44); } std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); @@ -8030,55 +7496,6 @@ TEST_P(QuicFramerTest, BuildTruncatedCloseFramePacket) { 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', }; - unsigned char packet44[] = { - // type (short header, 4 byte packet number) - 0x32, - // connection_id - 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, - // packet number - 0x12, 0x34, 0x56, 0x78, - - // frame type (connection close frame) - 0x02, - // error code - 0x05, 0x06, 0x07, 0x08, - // error details length - 0x01, 0x00, - // error details (truncated to 256 bytes) - 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', - 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', - 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', - 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', - 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', - 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', - 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', - 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', - 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', - 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', - 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', - 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', - 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', - 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', - 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', - 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', - 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', - 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', - 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', - 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', - 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', - 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', - 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', - 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', - 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', - 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', - 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', - 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', - 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', - 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', - 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', - 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', - }; - unsigned char packet46[] = { // type (short header, 4 byte packet number) 0x43, @@ -8185,12 +7602,9 @@ TEST_P(QuicFramerTest, BuildTruncatedCloseFramePacket) { if (VersionHasIetfQuicFrames(framer_.transport_version())) { p = packet99; p_size = QUIC_ARRAYSIZE(packet99); - } else if (framer_.transport_version() > QUIC_VERSION_44) { + } else if (framer_.transport_version() >= QUIC_VERSION_46) { p = packet46; p_size = QUIC_ARRAYSIZE(packet46); - } else if (framer_.transport_version() > QUIC_VERSION_43) { - p = packet44; - p_size = QUIC_ARRAYSIZE(packet44); } std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); @@ -8374,29 +7788,6 @@ TEST_P(QuicFramerTest, BuildGoAwayPacket) { 'n', }; - unsigned char packet44[] = { - // type (short header, 4 byte packet number) - 0x32, - // connection_id - 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, - // packet number - 0x12, 0x34, 0x56, 0x78, - - // frame type (go away frame) - 0x03, - // error code - 0x05, 0x06, 0x07, 0x08, - // stream id - 0x01, 0x02, 0x03, 0x04, - // error details length - 0x00, 0x0d, - // error details - 'b', 'e', 'c', 'a', - 'u', 's', 'e', ' ', - 'I', ' ', 'c', 'a', - 'n', - }; - unsigned char packet46[] = { // type (short header, 4 byte packet number) 0x43, @@ -8424,12 +7815,9 @@ TEST_P(QuicFramerTest, BuildGoAwayPacket) { unsigned char* p = packet; size_t p_size = QUIC_ARRAYSIZE(packet); - if (framer_.transport_version() > QUIC_VERSION_44) { + if (framer_.transport_version() > QUIC_VERSION_43) { p = packet46; p_size = QUIC_ARRAYSIZE(packet46); - } else if (framer_.transport_version() > QUIC_VERSION_43) { - p = packet44; - p_size = QUIC_ARRAYSIZE(packet44); } std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); @@ -8510,57 +7898,6 @@ TEST_P(QuicFramerTest, BuildTruncatedGoAwayPacket) { 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', }; - unsigned char packet44[] = { - // type (short header, 4 byte packet number) - 0x32, - // connection_id - 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, - // packet number - 0x12, 0x34, 0x56, 0x78, - - // frame type (go away frame) - 0x03, - // error code - 0x05, 0x06, 0x07, 0x08, - // stream id - 0x01, 0x02, 0x03, 0x04, - // error details length - 0x01, 0x00, - // error details (truncated to 256 bytes) - 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', - 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', - 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', - 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', - 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', - 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', - 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', - 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', - 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', - 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', - 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', - 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', - 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', - 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', - 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', - 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', - 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', - 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', - 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', - 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', - 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', - 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', - 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', - 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', - 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', - 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', - 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', - 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', - 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', - 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', - 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', - 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', - }; - unsigned char packet46[] = { // type (short header, 4 byte packet number) 0x43, @@ -8615,12 +7952,9 @@ TEST_P(QuicFramerTest, BuildTruncatedGoAwayPacket) { unsigned char* p = packet; size_t p_size = QUIC_ARRAYSIZE(packet); - if (framer_.transport_version() > QUIC_VERSION_44) { + if (framer_.transport_version() > QUIC_VERSION_43) { p = packet46; p_size = QUIC_ARRAYSIZE(packet46); - } else if (framer_.transport_version() > QUIC_VERSION_43) { - p = packet44; - p_size = QUIC_ARRAYSIZE(packet44); } std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); @@ -8662,23 +7996,6 @@ TEST_P(QuicFramerTest, BuildWindowUpdatePacket) { 0x55, 0x66, 0x77, 0x88, }; - unsigned char packet44[] = { - // type (short header, 4 byte packet number) - 0x32, - // connection_id - 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, - // packet number - 0x12, 0x34, 0x56, 0x78, - - // frame type (window update frame) - 0x04, - // stream id - 0x01, 0x02, 0x03, 0x04, - // byte offset - 0x11, 0x22, 0x33, 0x44, - 0x55, 0x66, 0x77, 0x88, - }; - unsigned char packet46[] = { // type (short header, 4 byte packet number) 0x43, @@ -8722,12 +8039,9 @@ TEST_P(QuicFramerTest, BuildWindowUpdatePacket) { if (VersionHasIetfQuicFrames(framer_.transport_version())) { p = packet99; p_size = QUIC_ARRAYSIZE(packet99); - } else if (framer_.transport_version() > QUIC_VERSION_44) { + } else if (framer_.transport_version() >= QUIC_VERSION_46) { p = packet46; p_size = QUIC_ARRAYSIZE(packet46); - } else if (framer_.transport_version() > QUIC_VERSION_43) { - p = packet44; - p_size = QUIC_ARRAYSIZE(packet44); } test::CompareCharArraysWithHexError("constructed packet", data->data(), @@ -8860,20 +8174,6 @@ TEST_P(QuicFramerTest, BuildBlockedPacket) { 0x01, 0x02, 0x03, 0x04, }; - unsigned char packet44[] = { - // type (short packet, 4 byte packet number) - 0x32, - // connection_id - 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, - // packet number - 0x12, 0x34, 0x56, 0x78, - - // frame type (blocked frame) - 0x05, - // stream id - 0x01, 0x02, 0x03, 0x04, - }; - unsigned char packet46[] = { // type (short packet, 4 byte packet number) 0x43, @@ -8911,12 +8211,9 @@ TEST_P(QuicFramerTest, BuildBlockedPacket) { if (VersionHasIetfQuicFrames(framer_.transport_version())) { p = packet99; p_size = QUIC_ARRAYSIZE(packet99); - } else if (framer_.transport_version() > QUIC_VERSION_44) { + } else if (framer_.transport_version() >= QUIC_VERSION_46) { p = packet46; p_size = QUIC_ARRAYSIZE(packet46); - } else if (framer_.transport_version() > QUIC_VERSION_43) { - p = packet44; - p_size = QUIC_ARRAYSIZE(packet44); } test::CompareCharArraysWithHexError("constructed packet", data->data(), @@ -8946,18 +8243,6 @@ TEST_P(QuicFramerTest, BuildPingPacket) { 0x07, }; - unsigned char packet44[] = { - // type (short header, 4 byte packet number) - 0x32, - // connection_id - 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, - // packet number - 0x12, 0x34, 0x56, 0x78, - - // frame type - 0x07, - }; - unsigned char packet46[] = { // type (short header, 4 byte packet number) 0x43, @@ -8986,10 +8271,8 @@ TEST_P(QuicFramerTest, BuildPingPacket) { unsigned char* p = packet; if (VersionHasIetfQuicFrames(framer_.transport_version())) { p = packet99; - } else if (framer_.transport_version() > QUIC_VERSION_44) { + } else if (framer_.transport_version() >= QUIC_VERSION_46) { p = packet46; - } else if (framer_.transport_version() > QUIC_VERSION_43) { - p = packet44; } std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); @@ -8997,12 +8280,12 @@ TEST_P(QuicFramerTest, BuildPingPacket) { test::CompareCharArraysWithHexError( "constructed packet", data->data(), data->length(), AsChars(p), - framer_.transport_version() > QUIC_VERSION_43 ? QUIC_ARRAYSIZE(packet44) + framer_.transport_version() > QUIC_VERSION_43 ? QUIC_ARRAYSIZE(packet46) : QUIC_ARRAYSIZE(packet)); } TEST_P(QuicFramerTest, BuildMessagePacket) { - if (framer_.transport_version() <= QUIC_VERSION_44) { + if (framer_.transport_version() <= QUIC_VERSION_43) { return; } QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT); @@ -9018,26 +8301,6 @@ TEST_P(QuicFramerTest, BuildMessagePacket) { QuicFrames frames = {QuicFrame(&frame), QuicFrame(&frame2)}; // clang-format off - unsigned char packet45[] = { - // type (short header, 4 byte packet number) - 0x32, - // connection_id - 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, - // packet number - 0x12, 0x34, 0x56, 0x78, - - // frame type (message frame) - 0x21, - // Length - 0x07, - // Message Data - 'm', 'e', 's', 's', 'a', 'g', 'e', - // frame type (message frame no length) - 0x20, - // Message Data - 'm', 'e', 's', 's', 'a', 'g', 'e', '2' - }; - unsigned char packet46[] = { // type (short header, 4 byte packet number) 0x43, @@ -9079,11 +8342,9 @@ TEST_P(QuicFramerTest, BuildMessagePacket) { }; // clang-format on - unsigned char* p = packet45; + unsigned char* p = packet46; if (VersionHasIetfQuicFrames(framer_.transport_version())) { p = packet99; - } else if (framer_.transport_version() > QUIC_VERSION_44) { - p = packet46; } std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); @@ -9091,7 +8352,7 @@ TEST_P(QuicFramerTest, BuildMessagePacket) { test::CompareCharArraysWithHexError("constructed packet", data->data(), data->length(), AsChars(p), - QUIC_ARRAYSIZE(packet45)); + QUIC_ARRAYSIZE(packet46)); } // Test that the connectivity probing packet is serialized correctly as a @@ -9120,21 +8381,6 @@ TEST_P(QuicFramerTest, BuildConnectivityProbingPacket) { 0x00, 0x00, 0x00, 0x00 }; - unsigned char packet44[] = { - // type (short header, 4 byte packet number) - 0x32, - // connection_id - 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, - // packet number - 0x12, 0x34, 0x56, 0x78, - - // frame type - 0x07, - // frame type (padding frame) - 0x00, - 0x00, 0x00, 0x00, 0x00 - }; - unsigned char packet46[] = { // type (short header, 4 byte packet number) 0x43, @@ -9171,12 +8417,9 @@ TEST_P(QuicFramerTest, BuildConnectivityProbingPacket) { if (VersionHasIetfQuicFrames(framer_.transport_version())) { p = packet99; packet_size = QUIC_ARRAYSIZE(packet99); - } else if (framer_.transport_version() > QUIC_VERSION_44) { + } else if (framer_.transport_version() >= QUIC_VERSION_46) { p = packet46; packet_size = QUIC_ARRAYSIZE(packet46); - } else if (framer_.transport_version() > QUIC_VERSION_43) { - p = packet44; - packet_size = QUIC_ARRAYSIZE(packet44); } std::unique_ptr<char[]> buffer(new char[kMaxOutgoingPacketSize]); @@ -9467,18 +8710,6 @@ TEST_P(QuicFramerTest, BuildMtuDiscoveryPacket) { 0x07, }; - unsigned char packet44[] = { - // type (short header, 4 byte packet number) - 0x32, - // connection_id - 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, - // packet number - 0x12, 0x34, 0x56, 0x78, - - // frame type - 0x07, - }; - unsigned char packet46[] = { // type (short header, 4 byte packet number) 0x43, @@ -9510,15 +8741,13 @@ TEST_P(QuicFramerTest, BuildMtuDiscoveryPacket) { unsigned char* p = packet; if (VersionHasIetfQuicFrames(framer_.transport_version())) { p = packet99; - } else if (framer_.transport_version() > QUIC_VERSION_44) { + } else if (framer_.transport_version() >= QUIC_VERSION_46) { p = packet46; - } else if (framer_.transport_version() > QUIC_VERSION_43) { - p = packet44; } test::CompareCharArraysWithHexError( "constructed packet", data->data(), data->length(), AsChars(p), - framer_.transport_version() > QUIC_VERSION_43 ? QUIC_ARRAYSIZE(packet44) + framer_.transport_version() > QUIC_VERSION_43 ? QUIC_ARRAYSIZE(packet46) : QUIC_ARRAYSIZE(packet)); } @@ -9689,7 +8918,7 @@ TEST_P(QuicFramerTest, BuildPublicResetPacketWithEndpointId) { TEST_P(QuicFramerTest, BuildIetfStatelessResetPacket) { // clang-format off - unsigned char packet44[] = { + unsigned char packet[] = { // type (short header, 1 byte packet number) 0x70, // random packet number @@ -9706,7 +8935,7 @@ TEST_P(QuicFramerTest, BuildIetfStatelessResetPacket) { ASSERT_TRUE(data != nullptr); // Skip packet number byte which is random in stateless reset packet. test::CompareCharArraysWithHexError("constructed packet", data->data(), 1, - AsChars(packet44), 1); + AsChars(packet), 1); const size_t random_bytes_length = data->length() - kPacketHeaderTypeSize - sizeof(kTestStatelessResetToken); EXPECT_EQ(kMinRandomBytesLengthInStatelessReset, random_bytes_length); @@ -9715,7 +8944,7 @@ TEST_P(QuicFramerTest, BuildIetfStatelessResetPacket) { "constructed packet", data->data() + data->length() - sizeof(kTestStatelessResetToken), sizeof(kTestStatelessResetToken), - AsChars(packet44) + QUIC_ARRAYSIZE(packet44) - + AsChars(packet) + QUIC_ARRAYSIZE(packet) - sizeof(kTestStatelessResetToken), sizeof(kTestStatelessResetToken)); } @@ -9738,21 +8967,6 @@ TEST_P(QuicFramerTest, EncryptPacket) { 'm', 'n', 'o', 'p', }; - unsigned char packet44[] = { - // type (short header, 4 byte packet number) - 0x32, - // connection_id - 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, - // packet number - 0x12, 0x34, 0x56, 0x78, - - // redundancy - 'a', 'b', 'c', 'd', - 'e', 'f', 'g', 'h', - 'i', 'j', 'k', 'l', - 'm', 'n', 'o', 'p', - }; - unsigned char packet46[] = { // type (short header, 4 byte packet number) 0x43, @@ -9790,10 +9004,8 @@ TEST_P(QuicFramerTest, EncryptPacket) { if (framer_.transport_version() == QUIC_VERSION_99) { p = packet99; p_size = QUIC_ARRAYSIZE(packet99); - } else if (framer_.transport_version() > QUIC_VERSION_44) { + } else if (framer_.transport_version() >= QUIC_VERSION_46) { p = packet46; - } else if (framer_.transport_version() > QUIC_VERSION_43) { - p = packet44; } std::unique_ptr<QuicPacket> raw(new QuicPacket( @@ -9830,25 +9042,6 @@ TEST_P(QuicFramerTest, EncryptPacketWithVersionFlag) { 'm', 'n', 'o', 'p', }; - unsigned char packet44[] = { - // type (long header with packet type ZERO_RTT_PROTECTED) - 0xFC, - // version tag - 'Q', '.', '1', '0', - // connection_id length - 0x50, - // connection_id - 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, - // packet number - 0x12, 0x34, 0x56, 0x78, - - // redundancy - 'a', 'b', 'c', 'd', - 'e', 'f', 'g', 'h', - 'i', 'j', 'k', 'l', - 'm', 'n', 'o', 'p', - }; - unsigned char packet46[] = { // type (long header with packet type ZERO_RTT_PROTECTED) 0xD3, @@ -9873,10 +9066,12 @@ TEST_P(QuicFramerTest, EncryptPacketWithVersionFlag) { 0xD3, // version tag 'Q', '.', '1', '0', - // connection_id length - 0x50, - // connection_id + // destination connection ID length + 0x08, + // destination connection ID 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // source connection ID length + 0x00, // packet number 0x12, 0x34, 0x56, 0x78, @@ -9895,12 +9090,9 @@ TEST_P(QuicFramerTest, EncryptPacketWithVersionFlag) { if (framer_.transport_version() == QUIC_VERSION_99) { p = packet99; p_size = QUIC_ARRAYSIZE(packet99); - } else if (framer_.transport_version() > QUIC_VERSION_44) { + } else if (framer_.transport_version() >= QUIC_VERSION_46) { p = packet46; p_size = QUIC_ARRAYSIZE(packet46); - } else if (framer_.transport_version() > QUIC_VERSION_43) { - p = packet44; - p_size = QUIC_ARRAYSIZE(packet44); } std::unique_ptr<QuicPacket> raw(new QuicPacket( @@ -10083,43 +9275,6 @@ TEST_P(QuicFramerTest, StopPacketProcessing) { 0x9A, 0xBE, }; - unsigned char packet44[] = { - // type (short header, 4 byte packet number) - 0x32, - // connection_id - 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, - // packet number - 0x12, 0x34, 0x56, 0x78, - - // frame type (stream frame with fin) - 0xFF, - // stream id - 0x01, 0x02, 0x03, 0x04, - // offset - 0x3A, 0x98, 0xFE, 0xDC, - 0x32, 0x10, 0x76, 0x54, - // data length - 0x00, 0x0c, - // data - 'h', 'e', 'l', 'l', - 'o', ' ', 'w', 'o', - 'r', 'l', 'd', '!', - - // frame type (ack frame) - 0x40, - // least packet number awaiting an ack - 0x12, 0x34, 0x56, 0x78, - 0x9A, 0xA0, - // largest observed packet number - 0x12, 0x34, 0x56, 0x78, - 0x9A, 0xBF, - // num missing packets - 0x01, - // missing packet - 0x12, 0x34, 0x56, 0x78, - 0x9A, 0xBE, - }; - unsigned char packet46[] = { // type (short header, 4 byte packet number) 0x43, @@ -10211,12 +9366,9 @@ TEST_P(QuicFramerTest, StopPacketProcessing) { if (VersionHasIetfQuicFrames(framer_.transport_version())) { p = packet99; p_size = QUIC_ARRAYSIZE(packet99); - } else if (framer_.transport_version() > QUIC_VERSION_44) { + } else if (framer_.transport_version() >= QUIC_VERSION_46) { p = packet46; p_size = QUIC_ARRAYSIZE(packet46); - } else if (framer_.transport_version() > QUIC_VERSION_43) { - p = packet44; - p_size = QUIC_ARRAYSIZE(packet44); } QuicEncryptedPacket encrypted(AsChars(p), p_size, false); EXPECT_TRUE(framer_.ProcessPacket(encrypted)); @@ -11415,11 +10567,16 @@ TEST_P(QuicFramerTest, InvalidLongNewConnectionIdFrame) { {"Unable to read new connection ID frame retire_prior_to.", {kVarInt62OneByte + 0x0b}}, {"Unable to read new connection ID frame connection id.", - {0x13}}, // connection ID length + {0x40}}, // connection ID length {"Unable to read new connection ID frame connection id.", {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, 0xF0, 0xD2, 0xB4, 0x96, 0x78, 0x5A, 0x3C, 0x1E, - 0x42, 0x33, 0x42}}, + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + 0xF0, 0xD2, 0xB4, 0x96, 0x78, 0x5A, 0x3C, 0x1E, + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + 0xF0, 0xD2, 0xB4, 0x96, 0x78, 0x5A, 0x3C, 0x1E, + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + 0xF0, 0xD2, 0xB4, 0x96, 0x78, 0x5A, 0x3C, 0x1E}}, {"Can not read new connection ID frame reset token.", {0xb5, 0x69, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}} @@ -12710,26 +11867,6 @@ TEST_P(QuicFramerTest, AckFrameWithInvalidLargestObserved) { 0x00 }; - unsigned char packet44[] = { - // type (short header, 4 byte packet number) - 0x32, - // connection_id - 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, - // packet number - 0x12, 0x34, 0x56, 0x78, - - // frame type (ack frame) - 0x45, - // largest observed - 0x00, 0x00, - // Zero delta time. - 0x00, 0x00, - // first ack block length. - 0x00, 0x00, - // num timestamps. - 0x00 - }; - unsigned char packet46[] = { // type (short header, 4 byte packet number) 0x43, @@ -12776,11 +11913,8 @@ TEST_P(QuicFramerTest, AckFrameWithInvalidLargestObserved) { if (VersionHasIetfQuicFrames(framer_.transport_version())) { p = packet99; p_size = QUIC_ARRAYSIZE(packet99); - } else if (framer_.transport_version() > QUIC_VERSION_44) { + } else if (framer_.transport_version() >= QUIC_VERSION_46) { p = packet46; - } else if (framer_.transport_version() > QUIC_VERSION_43) { - p = packet44; - p_size = QUIC_ARRAYSIZE(packet44); } QuicEncryptedPacket encrypted(AsChars(p), p_size, false); @@ -12811,26 +11945,6 @@ TEST_P(QuicFramerTest, FirstAckBlockJustUnderFlow) { 0x00 }; - unsigned char packet44[] = { - // type (short header, 4 byte packet number) - 0x32, - // connection_id - 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, - // packet number - 0x12, 0x34, 0x56, 0x78, - - // frame type (ack frame) - 0x45, - // largest observed - 0x00, 0x02, - // Zero delta time. - 0x00, 0x00, - // first ack block length. - 0x00, 0x03, - // num timestamps. - 0x00 - }; - unsigned char packet46[] = { // type (short header, 4 byte packet number) 0x43, @@ -12877,12 +11991,9 @@ TEST_P(QuicFramerTest, FirstAckBlockJustUnderFlow) { if (VersionHasIetfQuicFrames(framer_.transport_version())) { p = packet99; p_size = QUIC_ARRAYSIZE(packet99); - } else if (framer_.transport_version() > QUIC_VERSION_44) { + } else if (framer_.transport_version() >= QUIC_VERSION_46) { p = packet46; p_size = QUIC_ARRAYSIZE(packet46); - } else if (framer_.transport_version() > QUIC_VERSION_43) { - p = packet44; - p_size = QUIC_ARRAYSIZE(packet44); } QuicEncryptedPacket encrypted(AsChars(p), p_size, false); @@ -12924,36 +12035,6 @@ TEST_P(QuicFramerTest, ThirdAckBlockJustUnderflow) { 0x00 }; - unsigned char packet44[] = { - // type (short header, 4 byte packet number) - 0x32, - // connection_id - 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, - // packet number - 0x12, 0x34, 0x56, 0x78, - - // frame type (ack frame) - 0x60, - // largest observed - 0x0A, - // Zero delta time. - 0x00, 0x00, - // Num of ack blocks - 0x02, - // first ack block length. - 0x02, - // gap to next block - 0x01, - // ack block length - 0x01, - // gap to next block - 0x01, - // ack block length - 0x06, - // num timestamps. - 0x00 - }; - unsigned char packet46[] = { // type (short header, 4 byte packet number) 0x43, @@ -13018,12 +12099,9 @@ TEST_P(QuicFramerTest, ThirdAckBlockJustUnderflow) { if (VersionHasIetfQuicFrames(framer_.transport_version())) { p = packet99; p_size = QUIC_ARRAYSIZE(packet99); - } else if (framer_.transport_version() > QUIC_VERSION_44) { + } else if (framer_.transport_version() >= QUIC_VERSION_46) { p = packet46; p_size = QUIC_ARRAYSIZE(packet46); - } else if (framer_.transport_version() > QUIC_VERSION_43) { - p = packet44; - p_size = QUIC_ARRAYSIZE(packet44); } QuicEncryptedPacket encrypted(AsChars(p), p_size, false); @@ -13051,9 +12129,11 @@ TEST_P(QuicFramerTest, CoalescedPacket) { // version QUIC_VERSION_BYTES, // destination connection ID length - 0x50, + 0x08, // destination connection ID 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // source connection ID length + 0x00, // long header packet length 0x1E, // packet number @@ -13078,9 +12158,11 @@ TEST_P(QuicFramerTest, CoalescedPacket) { // version QUIC_VERSION_BYTES, // destination connection ID length - 0x50, + 0x08, // destination connection ID 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // source connection ID length + 0x00, // long header packet length 0x1E, // packet number @@ -13132,6 +12214,340 @@ TEST_P(QuicFramerTest, CoalescedPacket) { CheckStreamFrameData("HELLO_WORLD?", visitor_.stream_frames_[1].get()); } +TEST_P(QuicFramerTest, UndecryptablePacketWithoutDecrypter) { + QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT); + + if (!framer_.version().KnowsWhichDecrypterToUse()) { + // We create a bad client decrypter by using initial encryption with a + // bogus connection ID; it should fail to decrypt everything. + QuicConnectionId bogus_connection_id = TestConnectionId(0xbad); + CrypterPair bogus_crypters; + CryptoUtils::CreateTlsInitialCrypters(Perspective::IS_CLIENT, + framer_.transport_version(), + bogus_connection_id, &bogus_crypters); + // This removes all other decrypters. + framer_.SetDecrypter(ENCRYPTION_FORWARD_SECURE, + std::move(bogus_crypters.decrypter)); + } + + // clang-format off + unsigned char packet[] = { + // public flags (version included, 8-byte connection ID, + // 4-byte packet number) + 0x28, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x00, + // padding frames + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + unsigned char packet46[] = { + // public flags (long header with packet type HANDSHAKE and + // 4-byte packet number) + 0xE3, + // version + QUIC_VERSION_BYTES, + // connection ID lengths + 0x05, + // source connection ID + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // long header packet length + 0x05, + // packet number + 0x12, 0x34, 0x56, 0x00, + // padding frames + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + unsigned char packet99[] = { + // public flags (long header with packet type HANDSHAKE and + // 4-byte packet number) + 0xE3, + // version + QUIC_VERSION_BYTES, + // destination connection ID length + 0x00, + // source connection ID length + 0x08, + // source connection ID + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // long header packet length + 0x24, + // packet number + 0x12, 0x34, 0x56, 0x00, + // padding frames + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + // clang-format on + unsigned char* p = packet; + size_t p_length = QUIC_ARRAYSIZE(packet); + if (framer_.transport_version() == QUIC_VERSION_99) { + p = packet99; + p_length = QUIC_ARRAYSIZE(packet99); + } else if (framer_.transport_version() >= QUIC_VERSION_46) { + p = packet46; + p_length = QUIC_ARRAYSIZE(packet46); + } + // First attempt decryption without the handshake crypter. + EXPECT_FALSE( + framer_.ProcessPacket(QuicEncryptedPacket(AsChars(p), p_length, false))); + EXPECT_EQ(QUIC_DECRYPTION_FAILURE, framer_.error()); + if (GetQuicRestartFlag(quic_framer_uses_undecryptable_upcall)) { + ASSERT_EQ(1u, visitor_.undecryptable_packets_.size()); + ASSERT_EQ(1u, visitor_.undecryptable_decryption_levels_.size()); + ASSERT_EQ(1u, visitor_.undecryptable_has_decryption_keys_.size()); + CompareCharArraysWithHexError( + "undecryptable packet", visitor_.undecryptable_packets_[0]->data(), + visitor_.undecryptable_packets_[0]->length(), AsChars(p), p_length); + if (framer_.version().KnowsWhichDecrypterToUse()) { + EXPECT_EQ(ENCRYPTION_HANDSHAKE, + visitor_.undecryptable_decryption_levels_[0]); + } + EXPECT_FALSE(visitor_.undecryptable_has_decryption_keys_[0]); + } else { + EXPECT_EQ(0u, visitor_.undecryptable_packets_.size()); + EXPECT_EQ(0u, visitor_.undecryptable_decryption_levels_.size()); + EXPECT_EQ(0u, visitor_.undecryptable_has_decryption_keys_.size()); + } +} + +TEST_P(QuicFramerTest, UndecryptablePacketWithDecrypter) { + QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT); + + // We create a bad client decrypter by using initial encryption with a + // bogus connection ID; it should fail to decrypt everything. + QuicConnectionId bogus_connection_id = TestConnectionId(0xbad); + CrypterPair bad_handshake_crypters; + CryptoUtils::CreateTlsInitialCrypters( + Perspective::IS_CLIENT, framer_.transport_version(), bogus_connection_id, + &bad_handshake_crypters); + if (framer_.version().KnowsWhichDecrypterToUse()) { + framer_.InstallDecrypter(ENCRYPTION_HANDSHAKE, + std::move(bad_handshake_crypters.decrypter)); + } else { + framer_.SetDecrypter(ENCRYPTION_HANDSHAKE, + std::move(bad_handshake_crypters.decrypter)); + } + + // clang-format off + unsigned char packet[] = { + // public flags (version included, 8-byte connection ID, + // 4-byte packet number) + 0x28, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x00, + // padding frames + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + unsigned char packet46[] = { + // public flags (long header with packet type HANDSHAKE and + // 4-byte packet number) + 0xE3, + // version + QUIC_VERSION_BYTES, + // connection ID lengths + 0x05, + // source connection ID + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // long header packet length + 0x05, + // packet number + 0x12, 0x34, 0x56, 0x00, + // padding frames + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + unsigned char packet99[] = { + // public flags (long header with packet type HANDSHAKE and + // 4-byte packet number) + 0xE3, + // version + QUIC_VERSION_BYTES, + // destination connection ID length + 0x00, + // source connection ID length + 0x08, + // source connection ID + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // long header packet length + 0x24, + // packet number + 0x12, 0x34, 0x56, 0x00, + // padding frames + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + // clang-format on + unsigned char* p = packet; + size_t p_length = QUIC_ARRAYSIZE(packet); + if (framer_.transport_version() == QUIC_VERSION_99) { + p = packet99; + p_length = QUIC_ARRAYSIZE(packet99); + } else if (framer_.transport_version() >= QUIC_VERSION_46) { + p = packet46; + p_length = QUIC_ARRAYSIZE(packet46); + } + + EXPECT_FALSE( + framer_.ProcessPacket(QuicEncryptedPacket(AsChars(p), p_length, false))); + EXPECT_EQ(QUIC_DECRYPTION_FAILURE, framer_.error()); + if (GetQuicRestartFlag(quic_framer_uses_undecryptable_upcall)) { + ASSERT_EQ(1u, visitor_.undecryptable_packets_.size()); + ASSERT_EQ(1u, visitor_.undecryptable_decryption_levels_.size()); + ASSERT_EQ(1u, visitor_.undecryptable_has_decryption_keys_.size()); + CompareCharArraysWithHexError( + "undecryptable packet", visitor_.undecryptable_packets_[0]->data(), + visitor_.undecryptable_packets_[0]->length(), AsChars(p), p_length); + if (framer_.version().KnowsWhichDecrypterToUse()) { + EXPECT_EQ(ENCRYPTION_HANDSHAKE, + visitor_.undecryptable_decryption_levels_[0]); + } + EXPECT_EQ(framer_.version().KnowsWhichDecrypterToUse(), + visitor_.undecryptable_has_decryption_keys_[0]); + } else { + EXPECT_EQ(0u, visitor_.undecryptable_packets_.size()); + EXPECT_EQ(0u, visitor_.undecryptable_decryption_levels_.size()); + EXPECT_EQ(0u, visitor_.undecryptable_has_decryption_keys_.size()); + } +} + +TEST_P(QuicFramerTest, UndecryptableCoalescedPacket) { + if (!QuicVersionHasLongHeaderLengths(framer_.transport_version())) { + return; + } + ASSERT_TRUE(framer_.version().KnowsWhichDecrypterToUse()); + SetDecrypterLevel(ENCRYPTION_ZERO_RTT); + // We create a bad client decrypter by using initial encryption with a + // bogus connection ID; it should fail to decrypt everything. + QuicConnectionId bogus_connection_id = TestConnectionId(0xbad); + CrypterPair bad_handshake_crypters; + CryptoUtils::CreateTlsInitialCrypters( + Perspective::IS_CLIENT, framer_.transport_version(), bogus_connection_id, + &bad_handshake_crypters); + framer_.InstallDecrypter(ENCRYPTION_HANDSHAKE, + std::move(bad_handshake_crypters.decrypter)); + // clang-format off + unsigned char packet[] = { + // first coalesced packet + // public flags (long header with packet type HANDSHAKE and + // 4-byte packet number) + 0xE3, + // version + QUIC_VERSION_BYTES, + // destination connection ID length + 0x08, + // destination connection ID + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // source connection ID length + 0x00, + // long header packet length + 0x1E, + // packet number + 0x12, 0x34, 0x56, 0x78, + // frame type (IETF_STREAM frame with FIN, LEN, and OFFSET bits set) + 0x08 | 0x01 | 0x02 | 0x04, + // stream id + kVarInt62FourBytes + 0x00, 0x02, 0x03, 0x04, + // offset + kVarInt62EightBytes + 0x3A, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54, + // data length + kVarInt62OneByte + 0x0c, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!', + // second coalesced packet + // public flags (long header with packet type ZERO_RTT_PROTECTED and + // 4-byte packet number) + 0xD3, + // version + QUIC_VERSION_BYTES, + // destination connection ID length + 0x08, + // destination connection ID + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // source connection ID length + 0x00, + // long header packet length + 0x1E, + // packet number + 0x12, 0x34, 0x56, 0x79, + // frame type (IETF_STREAM frame with FIN, LEN, and OFFSET bits set) + 0x08 | 0x01 | 0x02 | 0x04, + // stream id + kVarInt62FourBytes + 0x00, 0x02, 0x03, 0x04, + // offset + kVarInt62EightBytes + 0x3A, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54, + // data length + kVarInt62OneByte + 0x0c, + // data + 'H', 'E', 'L', 'L', + 'O', '_', 'W', 'O', + 'R', 'L', 'D', '?', + }; + // clang-format on + const size_t length_of_first_coalesced_packet = 46; + + QuicEncryptedPacket encrypted(AsChars(packet), QUIC_ARRAYSIZE(packet), false); + EXPECT_FALSE(framer_.ProcessPacket(encrypted)); + + EXPECT_EQ(QUIC_DECRYPTION_FAILURE, framer_.error()); + + if (GetQuicRestartFlag(quic_framer_uses_undecryptable_upcall)) { + ASSERT_EQ(1u, visitor_.undecryptable_packets_.size()); + ASSERT_EQ(1u, visitor_.undecryptable_decryption_levels_.size()); + ASSERT_EQ(1u, visitor_.undecryptable_has_decryption_keys_.size()); + // Make sure we only receive the first undecryptable packet and not the + // full packet including the second coalesced packet. + CompareCharArraysWithHexError( + "undecryptable packet", visitor_.undecryptable_packets_[0]->data(), + visitor_.undecryptable_packets_[0]->length(), AsChars(packet), + length_of_first_coalesced_packet); + EXPECT_EQ(ENCRYPTION_HANDSHAKE, + visitor_.undecryptable_decryption_levels_[0]); + EXPECT_TRUE(visitor_.undecryptable_has_decryption_keys_[0]); + } else { + EXPECT_EQ(0u, visitor_.undecryptable_packets_.size()); + EXPECT_EQ(0u, visitor_.undecryptable_decryption_levels_.size()); + EXPECT_EQ(0u, visitor_.undecryptable_has_decryption_keys_.size()); + } + + // Make sure the second coalesced packet is parsed correctly. + ASSERT_EQ(visitor_.coalesced_packets_.size(), 1u); + EXPECT_TRUE(framer_.ProcessPacket(*visitor_.coalesced_packets_[0].get())); + + ASSERT_TRUE(visitor_.header_.get()); + + ASSERT_EQ(1u, visitor_.stream_frames_.size()); + EXPECT_EQ(0u, visitor_.ack_frames_.size()); + + // Stream ID should be the last 3 bytes of kStreamId. + EXPECT_EQ(0x00FFFFFF & kStreamId, visitor_.stream_frames_[0]->stream_id); + EXPECT_TRUE(visitor_.stream_frames_[0]->fin); + EXPECT_EQ(kStreamOffset, visitor_.stream_frames_[0]->offset); + CheckStreamFrameData("HELLO_WORLD?", visitor_.stream_frames_[0].get()); +} + TEST_P(QuicFramerTest, MismatchedCoalescedPacket) { if (!QuicVersionHasLongHeaderLengths(framer_.transport_version())) { return; @@ -13146,9 +12562,11 @@ TEST_P(QuicFramerTest, MismatchedCoalescedPacket) { // version QUIC_VERSION_BYTES, // destination connection ID length - 0x50, + 0x08, // destination connection ID 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // source connection ID length + 0x00, // long header packet length 0x1E, // packet number @@ -13173,9 +12591,11 @@ TEST_P(QuicFramerTest, MismatchedCoalescedPacket) { // version QUIC_VERSION_BYTES, // destination connection ID length - 0x50, + 0x08, // destination connection ID 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x11, + // source connection ID length + 0x00, // long header packet length 0x1E, // packet number @@ -13229,9 +12649,11 @@ TEST_P(QuicFramerTest, InvalidCoalescedPacket) { // version QUIC_VERSION_BYTES, // destination connection ID length - 0x50, + 0x08, // destination connection ID 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // source connection ID length + 0x00, // long header packet length 0x1E, // packet number @@ -13327,11 +12749,12 @@ TEST_P(QuicFramerTest, CoalescedPacketWithZeroesRoundTrip) { // Make sure we discard the subsequent zeroes. EXPECT_QUIC_PEER_BUG(EXPECT_TRUE(framer_.ProcessPacket(encrypted)), - "Server: Received mismatched coalesced header.*"); + "Server: (Failed to parse received|Received mismatched) " + "coalesced header.*"); } TEST_P(QuicFramerTest, ClientReceivesInvalidVersion) { - if (framer_.transport_version() < QUIC_VERSION_44) { + if (framer_.transport_version() <= QUIC_VERSION_43) { return; } QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT); @@ -13339,7 +12762,7 @@ TEST_P(QuicFramerTest, ClientReceivesInvalidVersion) { // clang-format off unsigned char packet[] = { // public flags (long header with packet type INITIAL) - 0xFF, + 0xC3, // version that is different from the framer's version 'Q', '0', '4', '3', // connection ID lengths @@ -13361,7 +12784,8 @@ TEST_P(QuicFramerTest, ClientReceivesInvalidVersion) { } TEST_P(QuicFramerTest, PacketHeaderWithVariableLengthConnectionId) { - if (framer_.transport_version() < QUIC_VERSION_46) { + if (!QuicUtils::VariableLengthConnectionIdAllowedForVersion( + framer_.transport_version())) { return; } SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE); @@ -13376,10 +12800,10 @@ TEST_P(QuicFramerTest, PacketHeaderWithVariableLengthConnectionId) { // clang-format off PacketFragments packet = { // type (8 byte connection_id and 1 byte packet number) - {"Unable to read type.", + {"Unable to read first byte.", {0x40}}, // connection_id - {"Unable to read Destination ConnectionId.", + {"Unable to read destination connection ID.", {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, 0x42}}, // packet number {"Unable to read packet number.", @@ -13388,10 +12812,10 @@ TEST_P(QuicFramerTest, PacketHeaderWithVariableLengthConnectionId) { PacketFragments packet_with_padding = { // type (8 byte connection_id and 1 byte packet number) - {"Unable to read type.", + {"Unable to read first byte.", {0x40}}, // connection_id - {"Unable to read Destination ConnectionId.", + {"Unable to read destination connection ID.", {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, 0x42}}, // packet number {"", @@ -13451,9 +12875,11 @@ TEST_P(QuicFramerTest, MultiplePacketNumberSpaces) { // version QUIC_VERSION_BYTES, // destination connection ID length - 0x50, + 0x08, // destination connection ID 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // source connection ID length + 0x00, // long header packet length 0x05, // packet number @@ -13533,23 +12959,9 @@ TEST_P(QuicFramerTest, IetfRetryPacketRejected) { } // clang-format off - PacketFragments packet = { - // public flags (IETF Retry packet, 0-length original destination CID) - {"Unable to read type.", - {0xf0}}, - // version tag - {"Unable to read protocol version.", - {QUIC_VERSION_BYTES}}, - // connection_id length - {"Illegal long header type value.", - {0x00}}, - }; - // clang-format on - - // clang-format off - PacketFragments packet45 = { + PacketFragments packet46 = { // public flags (IETF Retry packet, 0-length original destination CID) - {"Unable to read type.", + {"Unable to read first byte.", {0xf0}}, // version tag {"Unable to read protocol version.", @@ -13560,14 +12972,12 @@ TEST_P(QuicFramerTest, IetfRetryPacketRejected) { }; // clang-format on - PacketFragments& fragments = - framer_.transport_version() > QUIC_VERSION_44 ? packet45 : packet; std::unique_ptr<QuicEncryptedPacket> encrypted( - AssemblePacketFromFragments(fragments)); + AssemblePacketFromFragments(packet46)); EXPECT_FALSE(framer_.ProcessPacket(*encrypted)); EXPECT_EQ(QUIC_INVALID_PACKET_HEADER, framer_.error()); - CheckFramingBoundaries(fragments, QUIC_INVALID_PACKET_HEADER); + CheckFramingBoundaries(packet46, QUIC_INVALID_PACKET_HEADER); } TEST_P(QuicFramerTest, RetryPacketRejectedWithMultiplePacketNumberSpaces) { @@ -13580,7 +12990,7 @@ TEST_P(QuicFramerTest, RetryPacketRejectedWithMultiplePacketNumberSpaces) { // clang-format off PacketFragments packet = { // public flags (IETF Retry packet, 0-length original destination CID) - {"Unable to read type.", + {"Unable to read first byte.", {0xf0}}, // version tag {"Unable to read protocol version.", @@ -13677,7 +13087,8 @@ TEST_P(QuicFramerTest, ProcessMismatchedHeaderVersion) { CheckFramingBoundaries(packet, QUIC_INVALID_PACKET_HEADER); } -TEST_P(QuicFramerTest, WriteClientVersionNegotiationProbePacket) { +TEST_P(QuicFramerTest, WriteClientVersionNegotiationProbePacketOld) { + SetQuicFlag(FLAGS_quic_prober_uses_length_prefixed_connection_ids, false); // clang-format off static const char expected_packet[1200] = { // IETF long header with fixed bit set, type initial, all-0 encrypted bits. @@ -13726,26 +13137,279 @@ TEST_P(QuicFramerTest, WriteClientVersionNegotiationProbePacket) { EXPECT_TRUE(QuicFramer::WriteClientVersionNegotiationProbePacket( packet, sizeof(packet), destination_connection_id_bytes, sizeof(destination_connection_id_bytes))); - test::CompareCharArraysWithHexError("constructed packet", expected_packet, - sizeof(expected_packet), packet, - sizeof(packet)); + test::CompareCharArraysWithHexError("constructed packet", packet, + sizeof(packet), expected_packet, + sizeof(expected_packet)); QuicEncryptedPacket encrypted(reinterpret_cast<const char*>(packet), sizeof(packet), false); // Make sure we fail to parse this packet for the version under test. - EXPECT_FALSE(framer_.ProcessPacket(encrypted)); if (framer_.transport_version() <= QUIC_VERSION_43) { // We can only parse the connection ID with an IETF parser. + EXPECT_FALSE(framer_.ProcessPacket(encrypted)); return; } + EXPECT_TRUE(framer_.ProcessPacket(encrypted)); ASSERT_TRUE(visitor_.header_.get()); QuicConnectionId probe_payload_connection_id( reinterpret_cast<const char*>(destination_connection_id_bytes), sizeof(destination_connection_id_bytes)); EXPECT_EQ(probe_payload_connection_id, visitor_.header_.get()->destination_connection_id); + + PacketHeaderFormat format = GOOGLE_QUIC_PACKET; + bool version_present = false, has_length_prefix = false; + QuicVersionLabel version_label = 0; + ParsedQuicVersion parsed_version = QuicVersionReservedForNegotiation(); + QuicConnectionId destination_connection_id = TestConnectionId(0x33); + QuicConnectionId source_connection_id = TestConnectionId(0x34); + bool retry_token_present = true; + QuicStringPiece retry_token; + std::string detailed_error = "foobar"; + + QuicErrorCode parse_result = QuicFramer::ParsePublicHeaderDispatcher( + encrypted, kQuicDefaultConnectionIdLength, &format, &version_present, + &has_length_prefix, &version_label, &parsed_version, + &destination_connection_id, &source_connection_id, &retry_token_present, + &retry_token, &detailed_error); + EXPECT_EQ(QUIC_NO_ERROR, parse_result); + EXPECT_EQ(IETF_QUIC_LONG_HEADER_PACKET, format); + EXPECT_TRUE(version_present); + EXPECT_FALSE(has_length_prefix); + EXPECT_EQ(0xcabadaba, version_label); + EXPECT_EQ(QUIC_VERSION_UNSUPPORTED, parsed_version.transport_version); + EXPECT_EQ(probe_payload_connection_id, destination_connection_id); + EXPECT_EQ(EmptyQuicConnectionId(), source_connection_id); + EXPECT_FALSE(retry_token_present); + EXPECT_EQ(QuicStringPiece(), retry_token); + EXPECT_EQ("", detailed_error); } -TEST_P(QuicFramerTest, ParseServerVersionNegotiationProbeResponse) { +TEST_P(QuicFramerTest, WriteClientVersionNegotiationProbePacket) { + SetQuicFlag(FLAGS_quic_prober_uses_length_prefixed_connection_ids, true); + // clang-format off + static const char expected_packet[1200] = { + // IETF long header with fixed bit set, type initial, all-0 encrypted bits. + 0xc0, + // Version, part of the IETF space reserved for negotiation. + 0xca, 0xba, 0xda, 0xda, + // Destination connection ID length 8. + 0x08, + // 8-byte destination connection ID. + 0x56, 0x4e, 0x20, 0x70, 0x6c, 0x7a, 0x20, 0x21, + // Source connection ID length 0. + 0x00, + // 8 bytes of zeroes followed by 8 bytes of ones to ensure that this does + // not parse with any known version. + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + // zeroes to pad to 16 byte boundary. + 0x00, + // A polite greeting in case a human sees this in tcpdump. + 0x54, 0x68, 0x69, 0x73, 0x20, 0x70, 0x61, 0x63, + 0x6b, 0x65, 0x74, 0x20, 0x6f, 0x6e, 0x6c, 0x79, + 0x20, 0x65, 0x78, 0x69, 0x73, 0x74, 0x73, 0x20, + 0x74, 0x6f, 0x20, 0x74, 0x72, 0x69, 0x67, 0x67, + 0x65, 0x72, 0x20, 0x49, 0x45, 0x54, 0x46, 0x20, + 0x51, 0x55, 0x49, 0x43, 0x20, 0x76, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x6e, 0x65, 0x67, + 0x6f, 0x74, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x2e, 0x20, 0x50, 0x6c, 0x65, 0x61, 0x73, 0x65, + 0x20, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x64, + 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x61, 0x20, + 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, + 0x4e, 0x65, 0x67, 0x6f, 0x74, 0x69, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x20, 0x70, 0x61, 0x63, 0x6b, + 0x65, 0x74, 0x20, 0x69, 0x6e, 0x64, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x77, 0x68, + 0x61, 0x74, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x73, 0x20, 0x79, 0x6f, 0x75, 0x20, + 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x2e, + 0x20, 0x54, 0x68, 0x61, 0x6e, 0x6b, 0x20, 0x79, + 0x6f, 0x75, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x68, + 0x61, 0x76, 0x65, 0x20, 0x61, 0x20, 0x6e, 0x69, + 0x63, 0x65, 0x20, 0x64, 0x61, 0x79, 0x2e, 0x00, + }; + // clang-format on + char packet[1200]; + char destination_connection_id_bytes[] = {0x56, 0x4e, 0x20, 0x70, + 0x6c, 0x7a, 0x20, 0x21}; + EXPECT_TRUE(QuicFramer::WriteClientVersionNegotiationProbePacket( + packet, sizeof(packet), destination_connection_id_bytes, + sizeof(destination_connection_id_bytes))); + test::CompareCharArraysWithHexError("constructed packet", packet, + sizeof(packet), expected_packet, + sizeof(expected_packet)); + QuicEncryptedPacket encrypted(reinterpret_cast<const char*>(packet), + sizeof(packet), false); + if (framer_.transport_version() < QUIC_VERSION_99) { + // We can only parse the connection ID with a v99 parser. + EXPECT_FALSE(framer_.ProcessPacket(encrypted)); + return; + } + EXPECT_TRUE(framer_.ProcessPacket(encrypted)); + ASSERT_TRUE(visitor_.header_.get()); + QuicConnectionId probe_payload_connection_id( + reinterpret_cast<const char*>(destination_connection_id_bytes), + sizeof(destination_connection_id_bytes)); + EXPECT_EQ(probe_payload_connection_id, + visitor_.header_.get()->destination_connection_id); +} + +TEST_P(QuicFramerTest, DispatcherParseOldClientVersionNegotiationProbePacket) { + // clang-format off + static const char packet[1200] = { + // IETF long header with fixed bit set, type initial, all-0 encrypted bits. + 0xc0, + // Version, part of the IETF space reserved for negotiation. + 0xca, 0xba, 0xda, 0xba, + // Destination connection ID length 8, source connection ID length 0. + 0x50, + // 8-byte destination connection ID. + 0x56, 0x4e, 0x20, 0x70, 0x6c, 0x7a, 0x20, 0x21, + // 8 bytes of zeroes followed by 8 bytes of ones to ensure that this does + // not parse with any known version. + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + // 2 bytes of zeroes to pad to 16 byte boundary. + 0x00, 0x00, + // A polite greeting in case a human sees this in tcpdump. + 0x54, 0x68, 0x69, 0x73, 0x20, 0x70, 0x61, 0x63, + 0x6b, 0x65, 0x74, 0x20, 0x6f, 0x6e, 0x6c, 0x79, + 0x20, 0x65, 0x78, 0x69, 0x73, 0x74, 0x73, 0x20, + 0x74, 0x6f, 0x20, 0x74, 0x72, 0x69, 0x67, 0x67, + 0x65, 0x72, 0x20, 0x49, 0x45, 0x54, 0x46, 0x20, + 0x51, 0x55, 0x49, 0x43, 0x20, 0x76, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x6e, 0x65, 0x67, + 0x6f, 0x74, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x2e, 0x20, 0x50, 0x6c, 0x65, 0x61, 0x73, 0x65, + 0x20, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x64, + 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x61, 0x20, + 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, + 0x4e, 0x65, 0x67, 0x6f, 0x74, 0x69, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x20, 0x70, 0x61, 0x63, 0x6b, + 0x65, 0x74, 0x20, 0x69, 0x6e, 0x64, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x77, 0x68, + 0x61, 0x74, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x73, 0x20, 0x79, 0x6f, 0x75, 0x20, + 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x2e, + 0x20, 0x54, 0x68, 0x61, 0x6e, 0x6b, 0x20, 0x79, + 0x6f, 0x75, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x68, + 0x61, 0x76, 0x65, 0x20, 0x61, 0x20, 0x6e, 0x69, + 0x63, 0x65, 0x20, 0x64, 0x61, 0x79, 0x2e, 0x00, + }; + // clang-format on + char expected_destination_connection_id_bytes[] = {0x56, 0x4e, 0x20, 0x70, + 0x6c, 0x7a, 0x20, 0x21}; + QuicConnectionId expected_destination_connection_id( + reinterpret_cast<const char*>(expected_destination_connection_id_bytes), + sizeof(expected_destination_connection_id_bytes)); + + QuicEncryptedPacket encrypted(reinterpret_cast<const char*>(packet), + sizeof(packet)); + PacketHeaderFormat format = GOOGLE_QUIC_PACKET; + bool version_present = false, has_length_prefix = true; + QuicVersionLabel version_label = 33; + ParsedQuicVersion parsed_version = UnsupportedQuicVersion(); + QuicConnectionId destination_connection_id = TestConnectionId(1); + QuicConnectionId source_connection_id = TestConnectionId(2); + bool retry_token_present = true; + QuicStringPiece retry_token; + std::string detailed_error = "foobar"; + QuicErrorCode header_parse_result = QuicFramer::ParsePublicHeaderDispatcher( + encrypted, kQuicDefaultConnectionIdLength, &format, &version_present, + &has_length_prefix, &version_label, &parsed_version, + &destination_connection_id, &source_connection_id, &retry_token_present, + &retry_token, &detailed_error); + EXPECT_EQ(QUIC_NO_ERROR, header_parse_result); + EXPECT_EQ(IETF_QUIC_LONG_HEADER_PACKET, format); + EXPECT_TRUE(version_present); + EXPECT_FALSE(has_length_prefix); + EXPECT_EQ(0xcabadaba, version_label); + EXPECT_EQ(expected_destination_connection_id, destination_connection_id); + EXPECT_EQ(EmptyQuicConnectionId(), source_connection_id); + EXPECT_FALSE(retry_token_present); + EXPECT_EQ("", detailed_error); +} + +TEST_P(QuicFramerTest, DispatcherParseClientVersionNegotiationProbePacket) { + // clang-format off + static const char packet[1200] = { + // IETF long header with fixed bit set, type initial, all-0 encrypted bits. + 0xc0, + // Version, part of the IETF space reserved for negotiation. + 0xca, 0xba, 0xda, 0xba, + // Destination connection ID length 8. + 0x08, + // 8-byte destination connection ID. + 0x56, 0x4e, 0x20, 0x70, 0x6c, 0x7a, 0x20, 0x21, + // Source connection ID length 0. + 0x00, + // 8 bytes of zeroes followed by 8 bytes of ones to ensure that this does + // not parse with any known version. + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + // 1 byte of zeroes to pad to 16 byte boundary. + 0x00, + // A polite greeting in case a human sees this in tcpdump. + 0x54, 0x68, 0x69, 0x73, 0x20, 0x70, 0x61, 0x63, + 0x6b, 0x65, 0x74, 0x20, 0x6f, 0x6e, 0x6c, 0x79, + 0x20, 0x65, 0x78, 0x69, 0x73, 0x74, 0x73, 0x20, + 0x74, 0x6f, 0x20, 0x74, 0x72, 0x69, 0x67, 0x67, + 0x65, 0x72, 0x20, 0x49, 0x45, 0x54, 0x46, 0x20, + 0x51, 0x55, 0x49, 0x43, 0x20, 0x76, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x6e, 0x65, 0x67, + 0x6f, 0x74, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x2e, 0x20, 0x50, 0x6c, 0x65, 0x61, 0x73, 0x65, + 0x20, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x64, + 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x61, 0x20, + 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, + 0x4e, 0x65, 0x67, 0x6f, 0x74, 0x69, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x20, 0x70, 0x61, 0x63, 0x6b, + 0x65, 0x74, 0x20, 0x69, 0x6e, 0x64, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x77, 0x68, + 0x61, 0x74, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x73, 0x20, 0x79, 0x6f, 0x75, 0x20, + 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x2e, + 0x20, 0x54, 0x68, 0x61, 0x6e, 0x6b, 0x20, 0x79, + 0x6f, 0x75, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x68, + 0x61, 0x76, 0x65, 0x20, 0x61, 0x20, 0x6e, 0x69, + 0x63, 0x65, 0x20, 0x64, 0x61, 0x79, 0x2e, 0x00, + }; + // clang-format on + char expected_destination_connection_id_bytes[] = {0x56, 0x4e, 0x20, 0x70, + 0x6c, 0x7a, 0x20, 0x21}; + QuicConnectionId expected_destination_connection_id( + reinterpret_cast<const char*>(expected_destination_connection_id_bytes), + sizeof(expected_destination_connection_id_bytes)); + + QuicEncryptedPacket encrypted(reinterpret_cast<const char*>(packet), + sizeof(packet)); + PacketHeaderFormat format = GOOGLE_QUIC_PACKET; + bool version_present = false, has_length_prefix = false; + QuicVersionLabel version_label = 33; + ParsedQuicVersion parsed_version = UnsupportedQuicVersion(); + QuicConnectionId destination_connection_id = TestConnectionId(1); + QuicConnectionId source_connection_id = TestConnectionId(2); + bool retry_token_present = true; + QuicStringPiece retry_token; + std::string detailed_error = "foobar"; + QuicErrorCode header_parse_result = QuicFramer::ParsePublicHeaderDispatcher( + encrypted, kQuicDefaultConnectionIdLength, &format, &version_present, + &has_length_prefix, &version_label, &parsed_version, + &destination_connection_id, &source_connection_id, &retry_token_present, + &retry_token, &detailed_error); + EXPECT_EQ(QUIC_NO_ERROR, header_parse_result); + EXPECT_EQ(IETF_QUIC_LONG_HEADER_PACKET, format); + EXPECT_TRUE(version_present); + EXPECT_TRUE(has_length_prefix); + EXPECT_EQ(0xcabadaba, version_label); + EXPECT_EQ(expected_destination_connection_id, destination_connection_id); + EXPECT_EQ(EmptyQuicConnectionId(), source_connection_id); + EXPECT_EQ("", detailed_error); +} + +TEST_P(QuicFramerTest, ParseServerVersionNegotiationProbeResponseOld) { + SetQuicFlag(FLAGS_quic_prober_uses_length_prefixed_connection_ids, false); // clang-format off const char packet[] = { // IETF long header with fixed bit set, type initial, all-0 encrypted bits. @@ -13762,7 +13426,71 @@ TEST_P(QuicFramerTest, ParseServerVersionNegotiationProbeResponse) { }; // clang-format on char probe_payload_bytes[] = {0x56, 0x4e, 0x20, 0x70, 0x6c, 0x7a, 0x20, 0x21}; - char parsed_probe_payload_bytes[kQuicMaxConnectionIdLength] = {}; + char parsed_probe_payload_bytes[255] = {}; + uint8_t parsed_probe_payload_length = 0; + std::string parse_detailed_error = ""; + EXPECT_TRUE(QuicFramer::ParseServerVersionNegotiationProbeResponse( + reinterpret_cast<const char*>(packet), sizeof(packet), + reinterpret_cast<char*>(parsed_probe_payload_bytes), + &parsed_probe_payload_length, &parse_detailed_error)); + EXPECT_EQ("", parse_detailed_error); + test::CompareCharArraysWithHexError( + "parsed probe", parsed_probe_payload_bytes, parsed_probe_payload_length, + probe_payload_bytes, sizeof(probe_payload_bytes)); +} + +TEST_P(QuicFramerTest, ParseServerVersionNegotiationProbeResponse) { + SetQuicFlag(FLAGS_quic_prober_uses_length_prefixed_connection_ids, true); + // clang-format off + const char packet[] = { + // IETF long header with fixed bit set, type initial, all-0 encrypted bits. + 0xc0, + // Version of 0, indicating version negotiation. + 0x00, 0x00, 0x00, 0x00, + // Destination connection ID length 0, source connection ID length 8. + 0x00, 0x08, + // 8-byte source connection ID. + 0x56, 0x4e, 0x20, 0x70, 0x6c, 0x7a, 0x20, 0x21, + // A few supported versions. + 0xaa, 0xaa, 0xaa, 0xaa, + QUIC_VERSION_BYTES, + }; + // clang-format on + char probe_payload_bytes[] = {0x56, 0x4e, 0x20, 0x70, 0x6c, 0x7a, 0x20, 0x21}; + char parsed_probe_payload_bytes[255] = {}; + uint8_t parsed_probe_payload_length = 0; + std::string parse_detailed_error = ""; + EXPECT_TRUE(QuicFramer::ParseServerVersionNegotiationProbeResponse( + reinterpret_cast<const char*>(packet), sizeof(packet), + reinterpret_cast<char*>(parsed_probe_payload_bytes), + &parsed_probe_payload_length, &parse_detailed_error)); + EXPECT_EQ("", parse_detailed_error); + test::CompareCharArraysWithHexError( + "parsed probe", parsed_probe_payload_bytes, parsed_probe_payload_length, + probe_payload_bytes, sizeof(probe_payload_bytes)); +} + +// Test the client-side workaround for b/139330014 where an old client expects +// no length prefix but receives a length-prefixed response. +TEST_P(QuicFramerTest, ParseBadServerVersionNegotiationProbeResponse) { + SetQuicFlag(FLAGS_quic_prober_uses_length_prefixed_connection_ids, false); + // clang-format off + const char packet[] = { + // IETF long header with fixed bit set, type initial, all-0 encrypted bits. + 0xc0, + // Version of 0, indicating version negotiation. + 0x00, 0x00, 0x00, 0x00, + // Destination connection ID length 0, source connection ID length 8. + 0x00, 0x08, + // 8-byte source connection ID. + 0x56, 0x4e, 0x20, 0x70, 0x6c, 0x7a, 0x20, 0x21, + // A few supported versions. + 0xaa, 0xaa, 0xaa, 0xaa, + QUIC_VERSION_BYTES, + }; + // clang-format on + char probe_payload_bytes[] = {0x56, 0x4e, 0x20, 0x70, 0x6c, 0x7a, 0x20, 0x21}; + char parsed_probe_payload_bytes[255] = {}; uint8_t parsed_probe_payload_length = 0; std::string parse_detailed_error = ""; EXPECT_TRUE(QuicFramer::ParseServerVersionNegotiationProbeResponse( @@ -13771,8 +13499,8 @@ TEST_P(QuicFramerTest, ParseServerVersionNegotiationProbeResponse) { &parsed_probe_payload_length, &parse_detailed_error)); EXPECT_EQ("", parse_detailed_error); test::CompareCharArraysWithHexError( - "parsed probe", probe_payload_bytes, sizeof(probe_payload_bytes), - parsed_probe_payload_bytes, parsed_probe_payload_length); + "parsed probe", parsed_probe_payload_bytes, parsed_probe_payload_length, + probe_payload_bytes, sizeof(probe_payload_bytes)); } TEST_P(QuicFramerTest, ClientConnectionIdFromLongHeaderToClient) { @@ -13782,13 +13510,11 @@ TEST_P(QuicFramerTest, ClientConnectionIdFromLongHeaderToClient) { } SetDecrypterLevel(ENCRYPTION_HANDSHAKE); QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT); - const unsigned char type_byte = - framer_.transport_version() == QUIC_VERSION_44 ? 0xFC : 0xE3; // clang-format off unsigned char packet[] = { // public flags (long header with packet type HANDSHAKE and // 4-byte packet number) - type_byte, + 0xE3, // version QUIC_VERSION_BYTES, // connection ID lengths @@ -13802,9 +13528,34 @@ TEST_P(QuicFramerTest, ClientConnectionIdFromLongHeaderToClient) { // padding frame 0x00, }; + unsigned char packet99[] = { + // public flags (long header with packet type HANDSHAKE and + // 4-byte packet number) + 0xE3, + // version + QUIC_VERSION_BYTES, + // destination connection ID length + 0x08, + // destination connection ID + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // source connection ID length + 0x00, + // long header packet length + 0x05, + // packet number + 0x12, 0x34, 0x56, 0x00, + // padding frame + 0x00, + }; // clang-format on - const bool parse_success = framer_.ProcessPacket( - QuicEncryptedPacket(AsChars(packet), QUIC_ARRAYSIZE(packet), false)); + unsigned char* p = packet; + size_t p_length = QUIC_ARRAYSIZE(packet); + if (framer_.transport_version() == QUIC_VERSION_99) { + p = packet99; + p_length = QUIC_ARRAYSIZE(packet99); + } + const bool parse_success = + framer_.ProcessPacket(QuicEncryptedPacket(AsChars(p), p_length, false)); if (!QuicUtils::VariableLengthConnectionIdAllowedForVersion( framer_.transport_version())) { EXPECT_FALSE(parse_success); @@ -13812,14 +13563,6 @@ TEST_P(QuicFramerTest, ClientConnectionIdFromLongHeaderToClient) { EXPECT_EQ("Invalid ConnectionId length.", framer_.detailed_error()); return; } - if (!GetQuicRestartFlag(quic_do_not_override_connection_id)) { - // When the flag is disabled we expect processing to fail. - EXPECT_FALSE(parse_success); - EXPECT_EQ(QUIC_INVALID_PACKET_HEADER, framer_.error()); - EXPECT_EQ("Client connection ID not supported yet.", - framer_.detailed_error()); - return; - } EXPECT_TRUE(parse_success); EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); EXPECT_EQ("", framer_.detailed_error()); @@ -13835,13 +13578,11 @@ TEST_P(QuicFramerTest, ClientConnectionIdFromLongHeaderToServer) { } SetDecrypterLevel(ENCRYPTION_HANDSHAKE); QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_SERVER); - const unsigned char type_byte = - framer_.transport_version() == QUIC_VERSION_44 ? 0xFC : 0xE3; // clang-format off unsigned char packet[] = { // public flags (long header with packet type HANDSHAKE and // 4-byte packet number) - type_byte, + 0xE3, // version QUIC_VERSION_BYTES, // connection ID lengths @@ -13855,9 +13596,32 @@ TEST_P(QuicFramerTest, ClientConnectionIdFromLongHeaderToServer) { // padding frame 0x00, }; + unsigned char packet99[] = { + // public flags (long header with packet type HANDSHAKE and + // 4-byte packet number) + 0xE3, + // version + QUIC_VERSION_BYTES, + // connection ID lengths + 0x00, 0x08, + // source connection ID + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // long header packet length + 0x05, + // packet number + 0x12, 0x34, 0x56, 0x00, + // padding frame + 0x00, + }; // clang-format on - const bool parse_success = framer_.ProcessPacket( - QuicEncryptedPacket(AsChars(packet), QUIC_ARRAYSIZE(packet), false)); + unsigned char* p = packet; + size_t p_length = QUIC_ARRAYSIZE(packet); + if (framer_.transport_version() == QUIC_VERSION_99) { + p = packet99; + p_length = QUIC_ARRAYSIZE(packet99); + } + const bool parse_success = + framer_.ProcessPacket(QuicEncryptedPacket(AsChars(p), p_length, false)); if (!QuicUtils::VariableLengthConnectionIdAllowedForVersion( framer_.transport_version())) { EXPECT_FALSE(parse_success); @@ -13865,8 +13629,7 @@ TEST_P(QuicFramerTest, ClientConnectionIdFromLongHeaderToServer) { EXPECT_EQ("Invalid ConnectionId length.", framer_.detailed_error()); return; } - if (!framer_.version().SupportsClientConnectionIds() && - GetQuicRestartFlag(quic_do_not_override_connection_id)) { + if (!framer_.version().SupportsClientConnectionIds()) { EXPECT_FALSE(parse_success); EXPECT_EQ(QUIC_INVALID_PACKET_HEADER, framer_.error()); EXPECT_EQ("Client connection ID not supported in this version.", @@ -13957,6 +13720,87 @@ TEST_P(QuicFramerTest, ProcessAndValidateIetfConnectionIdLengthServer) { EXPECT_EQ("", detailed_error); } +TEST_P(QuicFramerTest, TestExtendedErrorCodeParser) { + if (VersionHasIetfQuicFrames(framer_.transport_version())) { + return; + } + QuicConnectionCloseFrame frame; + + frame.error_details = "this has no error code info in it"; + MaybeExtractQuicErrorCode(&frame); + EXPECT_EQ(QUIC_IETF_GQUIC_ERROR_MISSING, frame.extracted_error_code); + EXPECT_EQ("this has no error code info in it", frame.error_details); + + frame.error_details = "1234this does not have the colon in it"; + MaybeExtractQuicErrorCode(&frame); + EXPECT_EQ(QUIC_IETF_GQUIC_ERROR_MISSING, frame.extracted_error_code); + EXPECT_EQ("1234this does not have the colon in it", frame.error_details); + + frame.error_details = "1a234:this has a colon, but a malformed error number"; + MaybeExtractQuicErrorCode(&frame); + EXPECT_EQ(QUIC_IETF_GQUIC_ERROR_MISSING, frame.extracted_error_code); + EXPECT_EQ("1a234:this has a colon, but a malformed error number", + frame.error_details); + + frame.error_details = "1234:this is good"; + MaybeExtractQuicErrorCode(&frame); + EXPECT_EQ(1234u, frame.extracted_error_code); + EXPECT_EQ("this is good", frame.error_details); + + frame.error_details = + "1234 :this is not good, space between last digit and colon"; + MaybeExtractQuicErrorCode(&frame); + EXPECT_EQ(QUIC_IETF_GQUIC_ERROR_MISSING, frame.extracted_error_code); + EXPECT_EQ("1234 :this is not good, space between last digit and colon", + frame.error_details); + + frame.error_details = "123456789"; + MaybeExtractQuicErrorCode(&frame); + EXPECT_EQ(QUIC_IETF_GQUIC_ERROR_MISSING, + frame.extracted_error_code); // Not good, all numbers, no : + EXPECT_EQ("123456789", frame.error_details); + + frame.error_details = "1234:"; + MaybeExtractQuicErrorCode(&frame); + EXPECT_EQ(1234u, + frame.extracted_error_code); // corner case. + EXPECT_EQ("", frame.error_details); + + frame.error_details = "1234:5678"; + MaybeExtractQuicErrorCode(&frame); + EXPECT_EQ(1234u, + frame.extracted_error_code); // another corner case. + EXPECT_EQ("5678", frame.error_details); + + frame.error_details = "12345 6789:"; + MaybeExtractQuicErrorCode(&frame); + EXPECT_EQ(QUIC_IETF_GQUIC_ERROR_MISSING, + frame.extracted_error_code); // Not good + EXPECT_EQ("12345 6789:", frame.error_details); + + frame.error_details = ":no numbers, is not good"; + MaybeExtractQuicErrorCode(&frame); + EXPECT_EQ(QUIC_IETF_GQUIC_ERROR_MISSING, frame.extracted_error_code); + EXPECT_EQ(":no numbers, is not good", frame.error_details); + + frame.error_details = "qwer:also no numbers, is not good"; + MaybeExtractQuicErrorCode(&frame); + EXPECT_EQ(QUIC_IETF_GQUIC_ERROR_MISSING, frame.extracted_error_code); + EXPECT_EQ("qwer:also no numbers, is not good", frame.error_details); + + frame.error_details = " 1234:this is not good, space before first digit"; + MaybeExtractQuicErrorCode(&frame); + EXPECT_EQ(QUIC_IETF_GQUIC_ERROR_MISSING, frame.extracted_error_code); + EXPECT_EQ(" 1234:this is not good, space before first digit", + frame.error_details); + + frame.error_details = "1234:"; + MaybeExtractQuicErrorCode(&frame); + EXPECT_EQ(1234u, + frame.extracted_error_code); // this is good + EXPECT_EQ("", frame.error_details); +} + } // namespace } // namespace test } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_ietf_framer_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_ietf_framer_test.cc index dbbacdd8ddc..9a2b562a821 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_ietf_framer_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_ietf_framer_test.cc @@ -102,7 +102,7 @@ class TestQuicVisitor : public QuicFramerVisitorInterface { bool OnProtocolVersionMismatch( ParsedQuicVersion /*received_version*/) override { - return true; + return false; } bool OnUnauthenticatedPublicHeader( @@ -122,6 +122,10 @@ class TestQuicVisitor : public QuicFramerVisitorInterface { void OnCoalescedPacket(const QuicEncryptedPacket& /*packet*/) override {} + void OnUndecryptablePacket(const QuicEncryptedPacket& /*packet*/, + EncryptionLevel /*decryption_level*/, + bool /*has_decryption_key*/) override {} + bool OnStreamFrame(const QuicStreamFrame& /*frame*/) override { return true; } bool OnCryptoFrame(const QuicCryptoFrame& /*frame*/) override { return true; } diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_packet_creator.cc b/chromium/net/third_party/quiche/src/quic/core/quic_packet_creator.cc index a8d32918b5a..70d5ccb0459 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_packet_creator.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_packet_creator.cc @@ -640,12 +640,13 @@ void QuicPacketCreator::SerializePacket(char* encrypted_buffer, std::unique_ptr<QuicEncryptedPacket> QuicPacketCreator::SerializeVersionNegotiationPacket( bool ietf_quic, + bool use_length_prefix, const ParsedQuicVersionVector& supported_versions) { DCHECK_EQ(Perspective::IS_SERVER, framer_->perspective()); std::unique_ptr<QuicEncryptedPacket> encrypted = - QuicFramer::BuildVersionNegotiationPacket(server_connection_id_, - client_connection_id_, - ietf_quic, supported_versions); + QuicFramer::BuildVersionNegotiationPacket( + server_connection_id_, client_connection_id_, ietf_quic, + use_length_prefix, supported_versions); DCHECK(encrypted); DCHECK_GE(max_packet_length_, encrypted->length()); return encrypted; @@ -761,10 +762,6 @@ SerializedPacket QuicPacketCreator::NoPacket() { } QuicConnectionId QuicPacketCreator::GetDestinationConnectionId() const { - if (!GetQuicRestartFlag(quic_do_not_override_connection_id)) { - return server_connection_id_; - } - QUIC_RESTART_FLAG_COUNT_N(quic_do_not_override_connection_id, 1, 7); if (framer_->perspective() == Perspective::IS_SERVER) { return client_connection_id_; } @@ -772,10 +769,6 @@ QuicConnectionId QuicPacketCreator::GetDestinationConnectionId() const { } QuicConnectionId QuicPacketCreator::GetSourceConnectionId() const { - if (!GetQuicRestartFlag(quic_do_not_override_connection_id)) { - return server_connection_id_; - } - QUIC_RESTART_FLAG_COUNT_N(quic_do_not_override_connection_id, 6, 7); if (framer_->perspective() == Perspective::IS_CLIENT) { return client_connection_id_; } @@ -784,16 +777,12 @@ QuicConnectionId QuicPacketCreator::GetSourceConnectionId() const { QuicConnectionIdIncluded QuicPacketCreator::GetDestinationConnectionIdIncluded() const { - if (VersionHasIetfInvariantHeader(framer_->transport_version()) || - GetQuicRestartFlag(quic_do_not_override_connection_id)) { - // In versions that do not support client connection IDs, the destination - // connection ID is only sent from client to server. - return (framer_->perspective() == Perspective::IS_CLIENT || - framer_->version().SupportsClientConnectionIds()) - ? CONNECTION_ID_PRESENT - : CONNECTION_ID_ABSENT; - } - return server_connection_id_included_; + // In versions that do not support client connection IDs, the destination + // connection ID is only sent from client to server. + return (framer_->perspective() == Perspective::IS_CLIENT || + framer_->version().SupportsClientConnectionIds()) + ? CONNECTION_ID_PRESENT + : CONNECTION_ID_ABSENT; } QuicConnectionIdIncluded QuicPacketCreator::GetSourceConnectionIdIncluded() @@ -806,9 +795,7 @@ QuicConnectionIdIncluded QuicPacketCreator::GetSourceConnectionIdIncluded() framer_->version().SupportsClientConnectionIds())) { return CONNECTION_ID_PRESENT; } - if (GetQuicRestartFlag(quic_do_not_override_connection_id) && - framer_->perspective() == Perspective::IS_SERVER) { - QUIC_RESTART_FLAG_COUNT_N(quic_do_not_override_connection_id, 2, 7); + if (framer_->perspective() == Perspective::IS_SERVER) { return server_connection_id_included_; } return CONNECTION_ID_ABSENT; @@ -1068,8 +1055,6 @@ void QuicPacketCreator::SetClientConnectionId( QuicConnectionId client_connection_id) { DCHECK(client_connection_id.IsEmpty() || framer_->version().SupportsClientConnectionIds()); - DCHECK(client_connection_id.IsEmpty() || - GetQuicRestartFlag(quic_do_not_override_connection_id)); client_connection_id_ = client_connection_id; } diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_packet_creator.h b/chromium/net/third_party/quiche/src/quic/core/quic_packet_creator.h index 2c2fffb12fb..affc15c894f 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_packet_creator.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_packet_creator.h @@ -187,6 +187,7 @@ class QUIC_EXPORT_PRIVATE QuicPacketCreator { // Creates a version negotiation packet which supports |supported_versions|. std::unique_ptr<QuicEncryptedPacket> SerializeVersionNegotiationPacket( bool ietf_quic, + bool use_length_prefix, const ParsedQuicVersionVector& supported_versions); // Creates a connectivity probing packet for versions prior to version 99. diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_packet_creator_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_packet_creator_test.cc index 802c07b4ee6..ff9960f7d4b 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_packet_creator_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_packet_creator_test.cc @@ -813,8 +813,11 @@ TEST_P(QuicPacketCreatorTest, SerializeVersionNegotiationPacket) { versions.push_back(test::QuicVersionMax()); const bool ietf_quic = VersionHasIetfInvariantHeader(GetParam().version.transport_version); + const bool has_length_prefix = + GetParam().version.HasLengthPrefixedConnectionIds(); std::unique_ptr<QuicEncryptedPacket> encrypted( - creator_.SerializeVersionNegotiationPacket(ietf_quic, versions)); + creator_.SerializeVersionNegotiationPacket(ietf_quic, has_length_prefix, + versions)); { InSequence s; @@ -1820,6 +1823,9 @@ TEST_P(QuicPacketCreatorTest, GetGuaranteedLargestMessagePayload) { if (QuicVersionHasLongHeaderLengths(version)) { expected_largest_payload -= 2; } + if (GetParam().version.HasLengthPrefixedConnectionIds()) { + expected_largest_payload -= 1; + } EXPECT_EQ(expected_largest_payload, creator_.GetGuaranteedLargestMessagePayload()); } @@ -1920,17 +1926,11 @@ TEST_P(QuicPacketCreatorTest, RetryToken) { } TEST_P(QuicPacketCreatorTest, GetConnectionId) { - if (!GetQuicRestartFlag(quic_do_not_override_connection_id)) { - EXPECT_EQ(TestConnectionId(2), creator_.GetDestinationConnectionId()); - EXPECT_EQ(TestConnectionId(2), creator_.GetSourceConnectionId()); - return; - } EXPECT_EQ(TestConnectionId(2), creator_.GetDestinationConnectionId()); EXPECT_EQ(EmptyQuicConnectionId(), creator_.GetSourceConnectionId()); } TEST_P(QuicPacketCreatorTest, ClientConnectionId) { - SetQuicRestartFlag(quic_do_not_override_connection_id, true); if (!client_framer_.version().SupportsClientConnectionIds()) { return; } diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_packet_generator.cc b/chromium/net/third_party/quiche/src/quic/core/quic_packet_generator.cc index fe170697f42..160222f27f4 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_packet_generator.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_packet_generator.cc @@ -297,9 +297,10 @@ void QuicPacketGenerator::SetMaxPacketLength(QuicByteCount length) { std::unique_ptr<QuicEncryptedPacket> QuicPacketGenerator::SerializeVersionNegotiationPacket( bool ietf_quic, + bool use_length_prefix, const ParsedQuicVersionVector& supported_versions) { - return packet_creator_.SerializeVersionNegotiationPacket(ietf_quic, - supported_versions); + return packet_creator_.SerializeVersionNegotiationPacket( + ietf_quic, use_length_prefix, supported_versions); } OwningSerializedPacketPointer diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_packet_generator.h b/chromium/net/third_party/quiche/src/quic/core/quic_packet_generator.h index ea7da5d968c..c6902e0ad9b 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_packet_generator.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_packet_generator.h @@ -141,6 +141,7 @@ class QUIC_EXPORT_PRIVATE QuicPacketGenerator { // Creates a version negotiation packet which supports |supported_versions|. std::unique_ptr<QuicEncryptedPacket> SerializeVersionNegotiationPacket( bool ietf_quic, + bool use_length_prefix, const ParsedQuicVersionVector& supported_versions); // Creates a connectivity probing packet. diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_packet_generator_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_packet_generator_test.cc index cdb19b33373..9c2e3f3f0e3 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_packet_generator_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_packet_generator_test.cc @@ -430,8 +430,9 @@ TEST_F(QuicPacketGeneratorTest, ConsumeData_NotWritable) { MakeIOVector("foo", &iov_); QuicConsumedData consumed = generator_.ConsumeData( - QuicUtils::GetHeadersStreamId(framer_.transport_version()), &iov_, 1u, - iov_.iov_len, 0, FIN); + QuicUtils::GetFirstBidirectionalStreamId(framer_.transport_version(), + Perspective::IS_CLIENT), + &iov_, 1u, iov_.iov_len, 0, FIN); EXPECT_EQ(0u, consumed.bytes_consumed); EXPECT_FALSE(consumed.fin_consumed); EXPECT_FALSE(generator_.HasPendingFrames()); @@ -443,8 +444,9 @@ TEST_F(QuicPacketGeneratorTest, ConsumeData_WritableAndShouldNotFlush) { MakeIOVector("foo", &iov_); QuicConsumedData consumed = generator_.ConsumeData( - QuicUtils::GetHeadersStreamId(framer_.transport_version()), &iov_, 1u, - iov_.iov_len, 0, FIN); + QuicUtils::GetFirstBidirectionalStreamId(framer_.transport_version(), + Perspective::IS_CLIENT), + &iov_, 1u, iov_.iov_len, 0, FIN); EXPECT_EQ(3u, consumed.bytes_consumed); EXPECT_TRUE(consumed.fin_consumed); EXPECT_TRUE(generator_.HasPendingFrames()); @@ -458,8 +460,9 @@ TEST_F(QuicPacketGeneratorTest, ConsumeData_WritableAndShouldFlush) { .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); MakeIOVector("foo", &iov_); QuicConsumedData consumed = generator_.ConsumeData( - QuicUtils::GetHeadersStreamId(framer_.transport_version()), &iov_, 1u, - iov_.iov_len, 0, FIN); + QuicUtils::GetFirstBidirectionalStreamId(framer_.transport_version(), + Perspective::IS_CLIENT), + &iov_, 1u, iov_.iov_len, 0, FIN); generator_.Flush(); EXPECT_EQ(3u, consumed.bytes_consumed); EXPECT_TRUE(consumed.fin_consumed); @@ -564,9 +567,10 @@ TEST_F(QuicPacketGeneratorTest, ConsumeData_Handshake_PaddingDisabled) { TEST_F(QuicPacketGeneratorTest, ConsumeData_EmptyData) { delegate_.SetCanWriteAnything(); - EXPECT_QUIC_BUG(generator_.ConsumeData(QuicUtils::GetHeadersStreamId( - framer_.transport_version()), - nullptr, 0, 0, 0, NO_FIN), + EXPECT_QUIC_BUG(generator_.ConsumeData( + QuicUtils::QuicUtils::GetFirstBidirectionalStreamId( + framer_.transport_version(), Perspective::IS_CLIENT), + nullptr, 0, 0, 0, NO_FIN), "Attempt to consume empty data without FIN."); } @@ -576,8 +580,9 @@ TEST_F(QuicPacketGeneratorTest, MakeIOVector("foo", &iov_); generator_.ConsumeData( - QuicUtils::GetHeadersStreamId(framer_.transport_version()), &iov_, 1u, - iov_.iov_len, 0, FIN); + QuicUtils::GetFirstBidirectionalStreamId(framer_.transport_version(), + Perspective::IS_CLIENT), + &iov_, 1u, iov_.iov_len, 0, FIN); MakeIOVector("quux", &iov_); QuicConsumedData consumed = generator_.ConsumeData(3, &iov_, 1u, iov_.iov_len, 3, NO_FIN); @@ -592,12 +597,14 @@ TEST_F(QuicPacketGeneratorTest, ConsumeData_BatchOperations) { MakeIOVector("foo", &iov_); generator_.ConsumeData( - QuicUtils::GetHeadersStreamId(framer_.transport_version()), &iov_, 1u, - iov_.iov_len, 0, FIN); + QuicUtils::GetFirstBidirectionalStreamId(framer_.transport_version(), + Perspective::IS_CLIENT), + &iov_, 1u, iov_.iov_len, 0, FIN); MakeIOVector("quux", &iov_); QuicConsumedData consumed = generator_.ConsumeData( - QuicUtils::GetHeadersStreamId(framer_.transport_version()), &iov_, 1u, - iov_.iov_len, 3, NO_FIN); + QuicUtils::GetFirstBidirectionalStreamId(framer_.transport_version(), + Perspective::IS_CLIENT), + &iov_, 1u, iov_.iov_len, 3, NO_FIN); EXPECT_EQ(4u, consumed.bytes_consumed); EXPECT_FALSE(consumed.fin_consumed); EXPECT_TRUE(generator_.HasPendingFrames()); @@ -650,8 +657,9 @@ TEST_F(QuicPacketGeneratorTest, ConsumeData_FramesPreviouslyQueued) { // fitting. MakeIOVector("foo", &iov_); QuicConsumedData consumed = generator_.ConsumeData( - QuicUtils::GetHeadersStreamId(framer_.transport_version()), &iov_, 1u, - iov_.iov_len, 0, NO_FIN); + QuicUtils::GetFirstBidirectionalStreamId(framer_.transport_version(), + Perspective::IS_CLIENT), + &iov_, 1u, iov_.iov_len, 0, NO_FIN); EXPECT_EQ(3u, consumed.bytes_consumed); EXPECT_FALSE(consumed.fin_consumed); EXPECT_TRUE(generator_.HasPendingFrames()); @@ -661,8 +669,9 @@ TEST_F(QuicPacketGeneratorTest, ConsumeData_FramesPreviouslyQueued) { // to be serialized, and it will be added to a new open packet. MakeIOVector("bar", &iov_); consumed = generator_.ConsumeData( - QuicUtils::GetHeadersStreamId(framer_.transport_version()), &iov_, 1u, - iov_.iov_len, 3, FIN); + QuicUtils::GetFirstBidirectionalStreamId(framer_.transport_version(), + Perspective::IS_CLIENT), + &iov_, 1u, iov_.iov_len, 3, FIN); EXPECT_EQ(3u, consumed.bytes_consumed); EXPECT_TRUE(consumed.fin_consumed); EXPECT_TRUE(generator_.HasPendingFrames()); @@ -688,8 +697,9 @@ TEST_F(QuicPacketGeneratorTest, ConsumeDataFastPath) { EXPECT_CALL(delegate_, OnSerializedPacket(_)) .WillRepeatedly(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); QuicConsumedData consumed = generator_.ConsumeDataFastPath( - QuicUtils::GetHeadersStreamId(framer_.transport_version()), &iov_, 1u, - iov_.iov_len, 0, true); + QuicUtils::GetFirstBidirectionalStreamId(framer_.transport_version(), + Perspective::IS_CLIENT), + &iov_, 1u, iov_.iov_len, 0, true); EXPECT_EQ(10000u, consumed.bytes_consumed); EXPECT_TRUE(consumed.fin_consumed); EXPECT_FALSE(generator_.HasPendingFrames()); @@ -716,8 +726,9 @@ TEST_F(QuicPacketGeneratorTest, ConsumeDataLarge) { EXPECT_CALL(delegate_, OnSerializedPacket(_)) .WillRepeatedly(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); QuicConsumedData consumed = generator_.ConsumeData( - QuicUtils::GetHeadersStreamId(framer_.transport_version()), &iov_, 1u, - iov_.iov_len, 0, FIN); + QuicUtils::GetFirstBidirectionalStreamId(framer_.transport_version(), + Perspective::IS_CLIENT), + &iov_, 1u, iov_.iov_len, 0, FIN); EXPECT_EQ(10000u, consumed.bytes_consumed); EXPECT_TRUE(consumed.fin_consumed); EXPECT_FALSE(generator_.HasPendingFrames()); @@ -759,8 +770,9 @@ TEST_F(QuicPacketGeneratorTest, ConsumeDataLargeSendAckFalse) { QuicFrame(CreateRstStreamFrame()), /*bundle_ack=*/true); QuicConsumedData consumed = generator_.ConsumeData( - QuicUtils::GetHeadersStreamId(framer_.transport_version()), &iov_, 1u, - iov_.iov_len, 0, FIN); + QuicUtils::GetFirstBidirectionalStreamId(framer_.transport_version(), + Perspective::IS_CLIENT), + &iov_, 1u, iov_.iov_len, 0, FIN); generator_.Flush(); EXPECT_EQ(10000u, consumed.bytes_consumed); @@ -789,8 +801,9 @@ TEST_F(QuicPacketGeneratorTest, ConsumeDataLargeSendAckTrue) { EXPECT_CALL(delegate_, OnSerializedPacket(_)) .WillRepeatedly(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); QuicConsumedData consumed = generator_.ConsumeData( - QuicUtils::GetHeadersStreamId(framer_.transport_version()), &iov_, 1u, - iov_.iov_len, 0, FIN); + QuicUtils::GetFirstBidirectionalStreamId(framer_.transport_version(), + Perspective::IS_CLIENT), + &iov_, 1u, iov_.iov_len, 0, FIN); generator_.Flush(); EXPECT_EQ(10000u, consumed.bytes_consumed); @@ -925,8 +938,8 @@ TEST_F(QuicPacketGeneratorTest, PacketTransmissionType) { size_t data_len = 1324; CreateData(data_len); - QuicStreamId stream1_id = - QuicUtils::GetHeadersStreamId(framer_.transport_version()); + QuicStreamId stream1_id = QuicUtils::GetFirstBidirectionalStreamId( + framer_.transport_version(), Perspective::IS_CLIENT); QuicConsumedData consumed = generator_.ConsumeData(stream1_id, &iov_, 1u, iov_.iov_len, 0, NO_FIN); EXPECT_EQ(data_len, consumed.bytes_consumed); @@ -993,8 +1006,9 @@ TEST_F(QuicPacketGeneratorTest, SetMaxPacketLength_Initial) { .WillRepeatedly(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); CreateData(data_len); QuicConsumedData consumed = generator_.ConsumeData( - QuicUtils::GetHeadersStreamId(framer_.transport_version()), &iov_, 1u, - iov_.iov_len, + QuicUtils::GetFirstBidirectionalStreamId(framer_.transport_version(), + Perspective::IS_CLIENT), + &iov_, 1u, iov_.iov_len, /*offset=*/0, FIN); EXPECT_EQ(data_len, consumed.bytes_consumed); EXPECT_TRUE(consumed.fin_consumed); @@ -1030,8 +1044,9 @@ TEST_F(QuicPacketGeneratorTest, SetMaxPacketLength_Middle) { // Send two packets before packet size change. CreateData(data_len); QuicConsumedData consumed = generator_.ConsumeData( - QuicUtils::GetHeadersStreamId(framer_.transport_version()), &iov_, 1u, - iov_.iov_len, + QuicUtils::GetFirstBidirectionalStreamId(framer_.transport_version(), + Perspective::IS_CLIENT), + &iov_, 1u, iov_.iov_len, /*offset=*/0, NO_FIN); generator_.Flush(); EXPECT_EQ(data_len, consumed.bytes_consumed); @@ -1050,8 +1065,9 @@ TEST_F(QuicPacketGeneratorTest, SetMaxPacketLength_Middle) { CreateData(data_len); generator_.AttachPacketFlusher(); consumed = generator_.ConsumeData( - QuicUtils::GetHeadersStreamId(framer_.transport_version()), &iov_, 1u, - iov_.iov_len, data_len, FIN); + QuicUtils::GetFirstBidirectionalStreamId(framer_.transport_version(), + Perspective::IS_CLIENT), + &iov_, 1u, iov_.iov_len, data_len, FIN); generator_.Flush(); EXPECT_EQ(data_len, consumed.bytes_consumed); EXPECT_TRUE(consumed.fin_consumed); @@ -1080,8 +1096,9 @@ TEST_F(QuicPacketGeneratorTest, SetMaxPacketLength_MidpacketFlush) { // should not cause packet serialization. CreateData(first_write_len); QuicConsumedData consumed = generator_.ConsumeData( - QuicUtils::GetHeadersStreamId(framer_.transport_version()), &iov_, 1u, - iov_.iov_len, + QuicUtils::GetFirstBidirectionalStreamId(framer_.transport_version(), + Perspective::IS_CLIENT), + &iov_, 1u, iov_.iov_len, /*offset=*/0, NO_FIN); EXPECT_EQ(first_write_len, consumed.bytes_consumed); EXPECT_FALSE(consumed.fin_consumed); @@ -1113,8 +1130,9 @@ TEST_F(QuicPacketGeneratorTest, SetMaxPacketLength_MidpacketFlush) { // trigger serialization of one packet, and queue another one. CreateData(second_write_len); consumed = generator_.ConsumeData( - QuicUtils::GetHeadersStreamId(framer_.transport_version()), &iov_, 1u, - iov_.iov_len, + QuicUtils::GetFirstBidirectionalStreamId(framer_.transport_version(), + Perspective::IS_CLIENT), + &iov_, 1u, iov_.iov_len, /*offset=*/first_write_len, FIN); EXPECT_EQ(second_write_len, consumed.bytes_consumed); EXPECT_TRUE(consumed.fin_consumed); @@ -1201,8 +1219,9 @@ TEST_F(QuicPacketGeneratorTest, GenerateMtuDiscoveryPacket_SurroundedByData) { // Send data before the MTU probe. CreateData(data_len); QuicConsumedData consumed = generator_.ConsumeData( - QuicUtils::GetHeadersStreamId(framer_.transport_version()), &iov_, 1u, - iov_.iov_len, + QuicUtils::GetFirstBidirectionalStreamId(framer_.transport_version(), + Perspective::IS_CLIENT), + &iov_, 1u, iov_.iov_len, /*offset=*/0, NO_FIN); generator_.Flush(); EXPECT_EQ(data_len, consumed.bytes_consumed); @@ -1219,8 +1238,9 @@ TEST_F(QuicPacketGeneratorTest, GenerateMtuDiscoveryPacket_SurroundedByData) { CreateData(data_len); generator_.AttachPacketFlusher(); consumed = generator_.ConsumeData( - QuicUtils::GetHeadersStreamId(framer_.transport_version()), &iov_, 1u, - iov_.iov_len, + QuicUtils::GetFirstBidirectionalStreamId(framer_.transport_version(), + Perspective::IS_CLIENT), + &iov_, 1u, iov_.iov_len, /*offset=*/data_len, FIN); generator_.Flush(); EXPECT_EQ(data_len, consumed.bytes_consumed); @@ -1445,8 +1465,9 @@ TEST_F(QuicPacketGeneratorTest, AddMessageFrame) { MakeIOVector("foo", &iov_); generator_.ConsumeData( - QuicUtils::GetHeadersStreamId(framer_.transport_version()), &iov_, 1u, - iov_.iov_len, 0, FIN); + QuicUtils::GetFirstBidirectionalStreamId(framer_.transport_version(), + Perspective::IS_CLIENT), + &iov_, 1u, iov_.iov_len, 0, FIN); EXPECT_EQ(MESSAGE_STATUS_SUCCESS, generator_.AddMessageFrame( 1, MakeSpan(&allocator_, "message", &storage))); @@ -1475,7 +1496,6 @@ TEST_F(QuicPacketGeneratorTest, AddMessageFrame) { } TEST_F(QuicPacketGeneratorTest, ConnectionId) { - SetQuicRestartFlag(quic_do_not_override_connection_id, true); generator_.SetServerConnectionId(TestConnectionId(0x1337)); EXPECT_EQ(TestConnectionId(0x1337), creator_->GetDestinationConnectionId()); EXPECT_EQ(EmptyQuicConnectionId(), creator_->GetSourceConnectionId()); diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_packets.cc b/chromium/net/third_party/quiche/src/quic/core/quic_packets.cc index 4887e8273ad..2d6d0c92a6d 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_packets.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_packets.cc @@ -21,8 +21,7 @@ namespace quic { QuicConnectionId GetServerConnectionIdAsRecipient( const QuicPacketHeader& header, Perspective perspective) { - if (perspective == Perspective::IS_SERVER || - !GetQuicRestartFlag(quic_do_not_override_connection_id)) { + if (perspective == Perspective::IS_SERVER) { return header.destination_connection_id; } return header.source_connection_id; @@ -31,7 +30,6 @@ QuicConnectionId GetServerConnectionIdAsRecipient( QuicConnectionId GetClientConnectionIdAsRecipient( const QuicPacketHeader& header, Perspective perspective) { - DCHECK(GetQuicRestartFlag(quic_do_not_override_connection_id)); if (perspective == Perspective::IS_CLIENT) { return header.destination_connection_id; } @@ -40,40 +38,33 @@ QuicConnectionId GetClientConnectionIdAsRecipient( QuicConnectionId GetServerConnectionIdAsSender(const QuicPacketHeader& header, Perspective perspective) { - if (perspective == Perspective::IS_CLIENT || - !GetQuicRestartFlag(quic_do_not_override_connection_id)) { + if (perspective == Perspective::IS_CLIENT) { return header.destination_connection_id; } - QUIC_RESTART_FLAG_COUNT_N(quic_do_not_override_connection_id, 3, 7); return header.source_connection_id; } QuicConnectionIdIncluded GetServerConnectionIdIncludedAsSender( const QuicPacketHeader& header, Perspective perspective) { - if (perspective == Perspective::IS_CLIENT || - !GetQuicRestartFlag(quic_do_not_override_connection_id)) { + if (perspective == Perspective::IS_CLIENT) { return header.destination_connection_id_included; } - QUIC_RESTART_FLAG_COUNT_N(quic_do_not_override_connection_id, 4, 7); return header.source_connection_id_included; } QuicConnectionId GetClientConnectionIdAsSender(const QuicPacketHeader& header, Perspective perspective) { - if (perspective == Perspective::IS_CLIENT || - !GetQuicRestartFlag(quic_do_not_override_connection_id)) { + if (perspective == Perspective::IS_CLIENT) { return header.source_connection_id; } - QUIC_RESTART_FLAG_COUNT_N(quic_do_not_override_connection_id, 7, 7); return header.destination_connection_id; } QuicConnectionIdIncluded GetClientConnectionIdIncludedAsSender( const QuicPacketHeader& header, Perspective perspective) { - if (perspective == Perspective::IS_CLIENT || - !GetQuicRestartFlag(quic_do_not_override_connection_id)) { + if (perspective == Perspective::IS_CLIENT) { return header.source_connection_id_included; } return header.destination_connection_id_included; @@ -127,13 +118,14 @@ size_t GetPacketHeaderSize( // Long header. size_t size = kPacketHeaderTypeSize + kConnectionIdLengthSize + destination_connection_id_length + - source_connection_id_length + - (version > QUIC_VERSION_44 ? packet_number_length - : PACKET_4BYTE_PACKET_NUMBER) + + source_connection_id_length + packet_number_length + kQuicVersionSize; if (include_diversification_nonce) { size += kDiversificationNonceSize; } + if (VersionHasLengthPrefixedConnectionIds(version)) { + size += kConnectionIdLengthSize; + } DCHECK(QuicVersionHasLongHeaderLengths(version) || !GetQuicReloadableFlag(quic_fix_get_packet_header_size) || retry_token_length_length + retry_token_length + length_length == @@ -152,8 +144,6 @@ size_t GetPacketHeaderSize( // Google QUIC versions <= 43 can only carry one connection ID. DCHECK(destination_connection_id_length == 0 || source_connection_id_length == 0); - DCHECK(source_connection_id_length == 0 || - GetQuicRestartFlag(quic_do_not_override_connection_id)); return kPublicFlagsSize + destination_connection_id_length + source_connection_id_length + (include_version ? kQuicVersionSize : 0) + packet_number_length + @@ -284,6 +274,11 @@ QuicData::QuicData(const char* buffer, size_t length) QuicData::QuicData(const char* buffer, size_t length, bool owns_buffer) : buffer_(buffer), length_(length), owns_buffer_(owns_buffer) {} +QuicData::QuicData(QuicStringPiece packet_data) + : buffer_(packet_data.data()), + length_(packet_data.length()), + owns_buffer_(false) {} + QuicData::~QuicData() { if (owns_buffer_) { delete[] const_cast<char*>(buffer_); @@ -338,6 +333,9 @@ QuicEncryptedPacket::QuicEncryptedPacket(const char* buffer, bool owns_buffer) : QuicData(buffer, length, owns_buffer) {} +QuicEncryptedPacket::QuicEncryptedPacket(QuicStringPiece data) + : QuicData(data) {} + std::unique_ptr<QuicEncryptedPacket> QuicEncryptedPacket::Clone() const { char* buffer = new char[this->length()]; memcpy(buffer, this->data(), this->length()); @@ -508,6 +506,7 @@ ReceivedPacketInfo::ReceivedPacketInfo(const QuicSocketAddress& self_address, packet(packet), form(GOOGLE_QUIC_PACKET), version_flag(false), + use_length_prefix(false), version_label(0), version(PROTOCOL_UNSUPPORTED, QUIC_VERSION_UNSUPPORTED), destination_connection_id(EmptyQuicConnectionId()), diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_packets.h b/chromium/net/third_party/quiche/src/quic/core/quic_packets.h index cf6099e14a2..5c34b64e1ae 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_packets.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_packets.h @@ -204,8 +204,13 @@ struct QUIC_EXPORT_PRIVATE QuicIetfStatelessResetPacket { class QUIC_EXPORT_PRIVATE QuicData { public: + // Creates a QuicData from a buffer and length. Does not own the buffer. QuicData(const char* buffer, size_t length); + // Creates a QuicData from a buffer and length, + // optionally taking ownership of the buffer. QuicData(const char* buffer, size_t length, bool owns_buffer); + // Creates a QuicData from a QuicStringPiece. Does not own the buffer. + QuicData(QuicStringPiece data); QuicData(const QuicData&) = delete; QuicData& operator=(const QuicData&) = delete; virtual ~QuicData(); @@ -263,8 +268,16 @@ class QUIC_EXPORT_PRIVATE QuicPacket : public QuicData { class QUIC_EXPORT_PRIVATE QuicEncryptedPacket : public QuicData { public: + // Creates a QuicEncryptedPacket from a buffer and length. + // Does not own the buffer. QuicEncryptedPacket(const char* buffer, size_t length); + // Creates a QuicEncryptedPacket from a buffer and length, + // optionally taking ownership of the buffer. QuicEncryptedPacket(const char* buffer, size_t length, bool owns_buffer); + // Creates a QuicEncryptedPacket from a QuicStringPiece. + // Does not own the buffer. + QuicEncryptedPacket(QuicStringPiece data); + QuicEncryptedPacket(const QuicEncryptedPacket&) = delete; QuicEncryptedPacket& operator=(const QuicEncryptedPacket&) = delete; @@ -425,6 +438,7 @@ struct QUIC_EXPORT_PRIVATE ReceivedPacketInfo { // Fields below are populated by QuicFramer::ProcessPacketDispatcher. PacketHeaderFormat form; bool version_flag; + bool use_length_prefix; QuicVersionLabel version_label; ParsedQuicVersion version; QuicConnectionId destination_connection_id; diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_packets_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_packets_test.cc index be35d201f19..7ee77979a0c 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_packets_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_packets_test.cc @@ -24,14 +24,6 @@ class QuicPacketsTest : public QuicTest {}; TEST_F(QuicPacketsTest, GetServerConnectionIdAsRecipient) { QuicPacketHeader header = CreateFakePacketHeader(); - if (!GetQuicRestartFlag(quic_do_not_override_connection_id)) { - EXPECT_EQ(TestConnectionId(1), - GetServerConnectionIdAsRecipient(header, Perspective::IS_SERVER)); - EXPECT_EQ(TestConnectionId(1), - GetServerConnectionIdAsRecipient(header, Perspective::IS_CLIENT)); - return; - } - EXPECT_EQ(TestConnectionId(1), GetServerConnectionIdAsRecipient(header, Perspective::IS_SERVER)); EXPECT_EQ(TestConnectionId(2), @@ -40,14 +32,6 @@ TEST_F(QuicPacketsTest, GetServerConnectionIdAsRecipient) { TEST_F(QuicPacketsTest, GetServerConnectionIdAsSender) { QuicPacketHeader header = CreateFakePacketHeader(); - if (!GetQuicRestartFlag(quic_do_not_override_connection_id)) { - EXPECT_EQ(TestConnectionId(1), - GetServerConnectionIdAsSender(header, Perspective::IS_SERVER)); - EXPECT_EQ(TestConnectionId(1), - GetServerConnectionIdAsSender(header, Perspective::IS_CLIENT)); - return; - } - EXPECT_EQ(TestConnectionId(2), GetServerConnectionIdAsSender(header, Perspective::IS_SERVER)); EXPECT_EQ(TestConnectionId(1), @@ -56,14 +40,6 @@ TEST_F(QuicPacketsTest, GetServerConnectionIdAsSender) { TEST_F(QuicPacketsTest, GetServerConnectionIdIncludedAsSender) { QuicPacketHeader header = CreateFakePacketHeader(); - if (!GetQuicRestartFlag(quic_do_not_override_connection_id)) { - EXPECT_EQ(CONNECTION_ID_PRESENT, GetServerConnectionIdIncludedAsSender( - header, Perspective::IS_SERVER)); - EXPECT_EQ(CONNECTION_ID_PRESENT, GetServerConnectionIdIncludedAsSender( - header, Perspective::IS_CLIENT)); - return; - } - EXPECT_EQ(CONNECTION_ID_ABSENT, GetServerConnectionIdIncludedAsSender( header, Perspective::IS_SERVER)); EXPECT_EQ(CONNECTION_ID_PRESENT, GetServerConnectionIdIncludedAsSender( @@ -72,14 +48,6 @@ TEST_F(QuicPacketsTest, GetServerConnectionIdIncludedAsSender) { TEST_F(QuicPacketsTest, GetClientConnectionIdIncludedAsSender) { QuicPacketHeader header = CreateFakePacketHeader(); - if (!GetQuicRestartFlag(quic_do_not_override_connection_id)) { - EXPECT_EQ(CONNECTION_ID_ABSENT, GetClientConnectionIdIncludedAsSender( - header, Perspective::IS_SERVER)); - EXPECT_EQ(CONNECTION_ID_ABSENT, GetClientConnectionIdIncludedAsSender( - header, Perspective::IS_CLIENT)); - return; - } - EXPECT_EQ(CONNECTION_ID_PRESENT, GetClientConnectionIdIncludedAsSender( header, Perspective::IS_SERVER)); EXPECT_EQ(CONNECTION_ID_ABSENT, GetClientConnectionIdIncludedAsSender( @@ -87,7 +55,6 @@ TEST_F(QuicPacketsTest, GetClientConnectionIdIncludedAsSender) { } TEST_F(QuicPacketsTest, GetClientConnectionIdAsRecipient) { - SetQuicRestartFlag(quic_do_not_override_connection_id, true); QuicPacketHeader header = CreateFakePacketHeader(); EXPECT_EQ(TestConnectionId(2), GetClientConnectionIdAsRecipient(header, Perspective::IS_SERVER)); @@ -97,14 +64,6 @@ TEST_F(QuicPacketsTest, GetClientConnectionIdAsRecipient) { TEST_F(QuicPacketsTest, GetClientConnectionIdAsSender) { QuicPacketHeader header = CreateFakePacketHeader(); - if (!GetQuicRestartFlag(quic_do_not_override_connection_id)) { - EXPECT_EQ(TestConnectionId(2), - GetClientConnectionIdAsSender(header, Perspective::IS_SERVER)); - EXPECT_EQ(TestConnectionId(2), - GetClientConnectionIdAsSender(header, Perspective::IS_CLIENT)); - return; - } - EXPECT_EQ(TestConnectionId(1), GetClientConnectionIdAsSender(header, Perspective::IS_SERVER)); EXPECT_EQ(TestConnectionId(2), diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_received_packet_manager.cc b/chromium/net/third_party/quiche/src/quic/core/quic_received_packet_manager.cc index 2994748c8f9..e21f4c48b8e 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_received_packet_manager.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_received_packet_manager.cc @@ -58,6 +58,8 @@ QuicReceivedPacketManager::QuicReceivedPacketManager(QuicConnectionStats* stats) ack_decimation_delay_(kAckDecimationDelay), unlimited_ack_decimation_(false), fast_ack_after_quiescence_(false), + local_max_ack_delay_( + QuicTime::Delta::FromMilliseconds(kDefaultDelayedAckTimeMs)), ack_timeout_(QuicTime::Zero()), time_of_previous_received_packet_(QuicTime::Zero()), was_last_packet_missing_(false) { @@ -218,8 +220,7 @@ void QuicReceivedPacketManager::MaybeUpdateAckTimeout( QuicPacketNumber last_received_packet_number, QuicTime time_of_last_received_packet, QuicTime now, - const RttStats* rtt_stats, - QuicTime::Delta local_max_ack_delay) { + const RttStats* rtt_stats) { if (!ack_frame_updated_) { // ACK frame has not been updated, nothing to do. return; @@ -251,7 +252,7 @@ void QuicReceivedPacketManager::MaybeUpdateAckTimeout( // Wait for the minimum of the ack decimation delay or the delayed ack time // before sending an ack. QuicTime::Delta ack_delay = std::min( - local_max_ack_delay, rtt_stats->min_rtt() * ack_decimation_delay_); + local_max_ack_delay_, rtt_stats->min_rtt() * ack_decimation_delay_); if (fast_ack_after_quiescence_ && now - time_of_previous_received_packet_ > rtt_stats->SmoothedOrInitialRtt()) { // Ack the first packet out of queiscence faster, because QUIC does @@ -273,7 +274,7 @@ void QuicReceivedPacketManager::MaybeUpdateAckTimeout( // or TLP packets, which we'd like to acknowledge quickly. MaybeUpdateAckTimeoutTo(now + QuicTime::Delta::FromMilliseconds(1)); } else { - MaybeUpdateAckTimeoutTo(now + local_max_ack_delay); + MaybeUpdateAckTimeoutTo(now + local_max_ack_delay_); } } diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_received_packet_manager.h b/chromium/net/third_party/quiche/src/quic/core/quic_received_packet_manager.h index aaae7f12f9e..293b03cf4db 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_received_packet_manager.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_received_packet_manager.h @@ -64,8 +64,7 @@ class QUIC_EXPORT_PRIVATE QuicReceivedPacketManager { QuicPacketNumber last_received_packet_number, QuicTime time_of_last_received_packet, QuicTime now, - const RttStats* rtt_stats, - QuicTime::Delta local_max_ack_delay); + const RttStats* rtt_stats); // Resets ACK related states, called after an ACK is successfully sent. void ResetAckStates(); @@ -119,6 +118,11 @@ class QUIC_EXPORT_PRIVATE QuicReceivedPacketManager { ack_frequency_before_ack_decimation_ = new_value; } + QuicTime::Delta local_max_ack_delay() const { return local_max_ack_delay_; } + void set_local_max_ack_delay(QuicTime::Delta local_max_ack_delay) { + local_max_ack_delay_ = local_max_ack_delay; + } + QuicTime ack_timeout() const { return ack_timeout_; } private: @@ -172,6 +176,9 @@ class QUIC_EXPORT_PRIVATE QuicReceivedPacketManager { // was received. bool fast_ack_after_quiescence_; + // The local node's maximum ack delay time. This is the maximum amount of + // time to wait before sending an acknowledgement. + QuicTime::Delta local_max_ack_delay_; // Time that an ACK needs to be sent. 0 means no ACK is pending. Used when // decide_when_to_send_acks_ is true. QuicTime ack_timeout_; diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_received_packet_manager_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_received_packet_manager_test.cc index 86ac933d0ff..db5e2600bfb 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_received_packet_manager_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_received_packet_manager_test.cc @@ -90,7 +90,7 @@ class QuicReceivedPacketManagerTest : public QuicTestWithParam<TestParams> { received_manager_.MaybeUpdateAckTimeout( should_last_packet_instigate_acks, QuicPacketNumber(last_received_packet_number), clock_.ApproximateNow(), - clock_.ApproximateNow(), &rtt_stats_, kDelayedAckTime); + clock_.ApproximateNow(), &rtt_stats_); } void CheckAckTimeout(QuicTime time) { diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_sent_packet_manager.cc b/chromium/net/third_party/quiche/src/quic/core/quic_sent_packet_manager.cc index 5762333dabe..a70dd01b036 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_sent_packet_manager.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_sent_packet_manager.cc @@ -111,24 +111,22 @@ QuicSentPacketManager::QuicSentPacketManager( ietf_style_2x_tlp_(false), largest_mtu_acked_(0), handshake_confirmed_(false), - local_max_ack_delay_( - QuicTime::Delta::FromMilliseconds(kDefaultDelayedAckTimeMs)), peer_max_ack_delay_( QuicTime::Delta::FromMilliseconds(kDefaultDelayedAckTimeMs)), rtt_updated_(false), acked_packets_iter_(last_ack_frame_.packets.rbegin()), + pto_enabled_(false), + max_probe_packets_per_pto_(2), + consecutive_pto_count_(0), loss_removes_from_inflight_( GetQuicReloadableFlag(quic_loss_removes_from_inflight)), ignore_tlpr_if_no_pending_stream_data_( GetQuicReloadableFlag(quic_ignore_tlpr_if_no_pending_stream_data)), - fix_rto_retransmission_( - GetQuicReloadableFlag(quic_fix_rto_retransmission)) { + fix_rto_retransmission_(false), + handshake_mode_disabled_(false) { if (loss_removes_from_inflight_) { QUIC_RELOADABLE_FLAG_COUNT(quic_loss_removes_from_inflight); } - if (fix_rto_retransmission_) { - QUIC_RELOADABLE_FLAG_COUNT(quic_fix_rto_retransmission); - } SetSendAlgorithm(congestion_control_type); } @@ -151,23 +149,51 @@ void QuicSentPacketManager::SetFromConfig(const QuicConfig& config) { SetInitialRtt(QuicTime::Delta::FromMicroseconds( config.GetInitialRoundTripTimeUsToSend())); } + if (config.HasReceivedMaxAckDelayMs()) { + peer_max_ack_delay_ = + QuicTime::Delta::FromMilliseconds(config.ReceivedMaxAckDelayMs()); + } if (config.HasClientSentConnectionOption(kMAD0, perspective)) { rtt_stats_.set_ignore_max_ack_delay(true); } if (config.HasClientSentConnectionOption(kMAD1, perspective)) { - rtt_stats_.set_initial_max_ack_delay(local_max_ack_delay_); - } - if (config.HasClientSentConnectionOption(kMAD2, perspective)) { - min_tlp_timeout_ = QuicTime::Delta::Zero(); + rtt_stats_.set_initial_max_ack_delay(peer_max_ack_delay_); } - if (config.HasClientSentConnectionOption(kMAD3, perspective)) { - min_rto_timeout_ = QuicTime::Delta::Zero(); - } - if (config.HasClientSentConnectionOption(kMAD4, perspective)) { - ietf_style_tlp_ = true; + if (GetQuicReloadableFlag(quic_sent_packet_manager_cleanup)) { + QUIC_RELOADABLE_FLAG_COUNT(quic_sent_packet_manager_cleanup); + if (config.HasClientSentConnectionOption(kMAD2, perspective)) { + // Set the minimum to the alarm granularity. + min_tlp_timeout_ = QuicTime::Delta::FromMilliseconds(1); + } + if (config.HasClientSentConnectionOption(kMAD3, perspective)) { + // Set the minimum to the alarm granularity. + min_rto_timeout_ = QuicTime::Delta::FromMilliseconds(1); + } + } else { + if (config.HasClientSentConnectionOption(kMAD2, perspective)) { + min_tlp_timeout_ = QuicTime::Delta::Zero(); + } + if (config.HasClientSentConnectionOption(kMAD3, perspective)) { + min_rto_timeout_ = QuicTime::Delta::Zero(); + } + if (config.HasClientSentConnectionOption(kMAD4, perspective)) { + ietf_style_tlp_ = true; + } + if (config.HasClientSentConnectionOption(kMAD5, perspective)) { + ietf_style_2x_tlp_ = true; + } } - if (config.HasClientSentConnectionOption(kMAD5, perspective)) { - ietf_style_2x_tlp_ = true; + + if (GetQuicReloadableFlag(quic_enable_pto) && fix_rto_retransmission_) { + if (config.HasClientSentConnectionOption(k2PTO, perspective)) { + pto_enabled_ = true; + QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_pto, 2, 4); + } + if (config.HasClientSentConnectionOption(k1PTO, perspective)) { + pto_enabled_ = true; + max_probe_packets_per_pto_ = 1; + QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_pto, 1, 4); + } } // Configure congestion control. @@ -330,6 +356,7 @@ void QuicSentPacketManager::PostProcessNewlyAckedPackets( // Reset all retransmit counters any time a new packet is acked. consecutive_rto_count_ = 0; consecutive_tlp_count_ = 0; + consecutive_pto_count_ = 0; consecutive_crypto_retransmission_count_ = 0; } @@ -460,7 +487,7 @@ void QuicSentPacketManager::MarkForRetransmission( << "transmission_type: " << QuicUtils::TransmissionTypeToString(transmission_type); // Handshake packets should never be sent as probing retransmissions. - DCHECK(!transmission_info->has_crypto_handshake || + DCHECK(pto_enabled_ || !transmission_info->has_crypto_handshake || transmission_type != PROBING_RETRANSMISSION); if (!loss_removes_from_inflight_ && !RetransmissionLeavesBytesInFlight(transmission_type)) { @@ -698,8 +725,10 @@ bool QuicSentPacketManager::OnPacketSent( return in_flight; } -void QuicSentPacketManager::OnRetransmissionTimeout() { - DCHECK(unacked_packets_.HasInFlightPackets()); +QuicSentPacketManager::RetransmissionTimeoutMode +QuicSentPacketManager::OnRetransmissionTimeout() { + DCHECK(unacked_packets_.HasInFlightPackets() || + (handshake_mode_disabled_ && !handshake_confirmed_)); DCHECK_EQ(0u, pending_timer_transmission_count_); // Handshake retransmission, timer based loss detection, TLP, and RTO are // implemented with a single alarm. The handshake alarm is set when the @@ -708,16 +737,17 @@ void QuicSentPacketManager::OnRetransmissionTimeout() { // The TLP alarm is always set to run for under an RTO. switch (GetRetransmissionMode()) { case HANDSHAKE_MODE: + DCHECK(!handshake_mode_disabled_); ++stats_->crypto_retransmit_count; RetransmitCryptoPackets(); - return; + return HANDSHAKE_MODE; case LOSS_MODE: { ++stats_->loss_timeout_count; QuicByteCount prior_in_flight = unacked_packets_.bytes_in_flight(); const QuicTime now = clock_->Now(); InvokeLossDetection(now); MaybeInvokeCongestionEvent(false, prior_in_flight, now); - return; + return LOSS_MODE; } case TLP_MODE: ++stats_->tlp_count; @@ -725,11 +755,17 @@ void QuicSentPacketManager::OnRetransmissionTimeout() { pending_timer_transmission_count_ = 1; // TLPs prefer sending new data instead of retransmitting data, so // give the connection a chance to write before completing the TLP. - return; + return TLP_MODE; case RTO_MODE: ++stats_->rto_count; RetransmitRtoPackets(); - return; + return RTO_MODE; + case PTO_MODE: + QUIC_DVLOG(1) << ENDPOINT << "PTO mode"; + ++stats_->pto_count; + ++consecutive_pto_count_; + pending_timer_transmission_count_ = max_probe_packets_per_pto_; + return PTO_MODE; } } @@ -765,6 +801,7 @@ void QuicSentPacketManager::RetransmitCryptoPackets() { } bool QuicSentPacketManager::MaybeRetransmitTailLossProbe() { + DCHECK(!pto_enabled_); if (pending_timer_transmission_count_ == 0) { return false; } @@ -793,6 +830,7 @@ bool QuicSentPacketManager::MaybeRetransmitOldestPacket(TransmissionType type) { } void QuicSentPacketManager::RetransmitRtoPackets() { + DCHECK(!pto_enabled_); QUIC_BUG_IF(pending_timer_transmission_count_ > 0) << "Retransmissions already queued:" << pending_timer_transmission_count_; // Mark two packets for retransmission. @@ -848,15 +886,62 @@ void QuicSentPacketManager::RetransmitRtoPackets() { } } +void QuicSentPacketManager::MaybeSendProbePackets() { + if (pending_timer_transmission_count_ == 0) { + return; + } + QuicPacketNumber packet_number = unacked_packets_.GetLeastUnacked(); + std::vector<QuicPacketNumber> probing_packets; + for (QuicUnackedPacketMap::const_iterator it = unacked_packets_.begin(); + it != unacked_packets_.end(); ++it, ++packet_number) { + if (it->state == OUTSTANDING && + unacked_packets_.HasRetransmittableFrames(*it)) { + probing_packets.push_back(packet_number); + if (probing_packets.size() == pending_timer_transmission_count_) { + break; + } + } + } + + for (QuicPacketNumber retransmission : probing_packets) { + QUIC_DVLOG(1) << ENDPOINT << "Marking " << retransmission + << " for probing retransmission"; + MarkForRetransmission(retransmission, PROBING_RETRANSMISSION); + } + // It is possible that there is not enough outstanding data for probing. +} + +void QuicSentPacketManager::AdjustPendingTimerTransmissions() { + if (pending_timer_transmission_count_ < max_probe_packets_per_pto_) { + // There are packets sent already, clear credit. + pending_timer_transmission_count_ = 0; + return; + } + // No packet gets sent, leave 1 credit to allow data to be write eventually. + pending_timer_transmission_count_ = 1; +} + +void QuicSentPacketManager::DisableHandshakeMode() { + DCHECK(session_decides_what_to_write()); + fix_rto_retransmission_ = true; + pto_enabled_ = true; + handshake_mode_disabled_ = true; +} + QuicSentPacketManager::RetransmissionTimeoutMode QuicSentPacketManager::GetRetransmissionMode() const { - DCHECK(unacked_packets_.HasInFlightPackets()); - if (!handshake_confirmed_ && unacked_packets_.HasPendingCryptoPackets()) { + DCHECK(unacked_packets_.HasInFlightPackets() || + (handshake_mode_disabled_ && !handshake_confirmed_)); + if (!handshake_mode_disabled_ && !handshake_confirmed_ && + unacked_packets_.HasPendingCryptoPackets()) { return HANDSHAKE_MODE; } if (loss_algorithm_->GetLossTimeout() != QuicTime::Zero()) { return LOSS_MODE; } + if (pto_enabled_) { + return PTO_MODE; + } if (consecutive_tlp_count_ < max_tail_loss_probes_) { if (unacked_packets_.HasUnackedRetransmittableFrames()) { return TLP_MODE; @@ -934,13 +1019,21 @@ QuicTime::Delta QuicSentPacketManager::TimeUntilSend(QuicTime now) const { } const QuicTime QuicSentPacketManager::GetRetransmissionTime() const { - // Don't set the timer if there is nothing to retransmit or we've already - // queued a tlp transmission and it hasn't been sent yet. - if (!unacked_packets_.HasInFlightPackets() || - pending_timer_transmission_count_ > 0) { + if (!unacked_packets_.HasInFlightPackets() && + (!handshake_mode_disabled_ || handshake_confirmed_ || + unacked_packets_.perspective() == Perspective::IS_SERVER)) { + // Do not set the timer if there is nothing in flight. However, to avoid + // handshake deadlock due to anti-amplification limit, client needs to set + // PTO timer when the handshake is not confirmed even there is nothing in + // flight. return QuicTime::Zero(); } - if (!unacked_packets_.HasUnackedRetransmittableFrames()) { + if (pending_timer_transmission_count_ > 0) { + // Do not set the timer if there is any credit left. + return QuicTime::Zero(); + } + if (!fix_rto_retransmission_ && + !unacked_packets_.HasUnackedRetransmittableFrames()) { return QuicTime::Zero(); } switch (GetRetransmissionMode()) { @@ -950,6 +1043,7 @@ const QuicTime QuicSentPacketManager::GetRetransmissionTime() const { case LOSS_MODE: return loss_algorithm_->GetLossTimeout(); case TLP_MODE: { + DCHECK(!pto_enabled_); // TODO(ianswett): When CWND is available, it would be preferable to // set the timer based on the earliest retransmittable packet. // Base the updated timer on the send time of the last packet. @@ -959,6 +1053,7 @@ const QuicTime QuicSentPacketManager::GetRetransmissionTime() const { return std::max(clock_->ApproximateNow(), tlp_time); } case RTO_MODE: { + DCHECK(!pto_enabled_); // The RTO is based on the first outstanding packet. const QuicTime sent_time = unacked_packets_.GetLastPacketSentTime(); QuicTime rto_time = sent_time + GetRetransmissionDelay(); @@ -967,6 +1062,19 @@ const QuicTime QuicSentPacketManager::GetRetransmissionTime() const { unacked_packets_.GetLastPacketSentTime() + GetTailLossProbeDelay(); return std::max(tlp_time, rto_time); } + case PTO_MODE: { + if (handshake_mode_disabled_ && !handshake_confirmed_ && + !unacked_packets_.HasInFlightPackets()) { + DCHECK_EQ(Perspective::IS_CLIENT, unacked_packets_.perspective()); + return std::max(clock_->ApproximateNow(), + unacked_packets_.GetLastCryptoPacketSentTime() + + GetProbeTimeoutDelay()); + } + // Ensure PTO never gets set to a time in the past. + return std::max( + clock_->ApproximateNow(), + unacked_packets_.GetLastPacketSentTime() + GetProbeTimeoutDelay()); + } } DCHECK(false); return QuicTime::Zero(); @@ -1058,6 +1166,22 @@ const QuicTime::Delta QuicSentPacketManager::GetRetransmissionDelay( return retransmission_delay; } +const QuicTime::Delta QuicSentPacketManager::GetProbeTimeoutDelay() const { + DCHECK(pto_enabled_); + if (rtt_stats_.smoothed_rtt().IsZero()) { + if (rtt_stats_.initial_rtt().IsZero()) { + return QuicTime::Delta::FromSeconds(1); + } + return 2 * rtt_stats_.initial_rtt(); + } + const QuicTime::Delta pto_delay = + rtt_stats_.smoothed_rtt() + + std::max(4 * rtt_stats_.mean_deviation(), + QuicTime::Delta::FromMilliseconds(1)) + + peer_max_ack_delay_; + return pto_delay * (1 << consecutive_pto_count_); +} + QuicTime::Delta QuicSentPacketManager::GetSlowStartDuration() const { if (send_algorithm_->GetCongestionControlType() != kBBR) { return QuicTime::Delta::Infinite(); @@ -1112,6 +1236,7 @@ void QuicSentPacketManager::OnConnectionMigration(AddressChangeType type) { } consecutive_rto_count_ = 0; consecutive_tlp_count_ = 0; + consecutive_pto_count_ = 0; rtt_stats_.OnConnectionMigration(); send_algorithm_->OnConnectionMigration(); } @@ -1273,6 +1398,16 @@ void QuicSentPacketManager::SetInitialRtt(QuicTime::Delta rtt) { rtt_stats_.set_initial_rtt(std::max(min_rtt, std::min(max_rtt, rtt))); } +void QuicSentPacketManager::SetSessionDecideWhatToWrite( + bool session_decides_what_to_write) { + if (GetQuicReloadableFlag(quic_fix_rto_retransmission3) && + session_decides_what_to_write) { + fix_rto_retransmission_ = true; + QUIC_RELOADABLE_FLAG_COUNT(quic_fix_rto_retransmission3); + } + unacked_packets_.SetSessionDecideWhatToWrite(session_decides_what_to_write); +} + void QuicSentPacketManager::EnableMultiplePacketNumberSpacesSupport() { unacked_packets_.EnableMultiplePacketNumberSpacesSupport(); } diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_sent_packet_manager.h b/chromium/net/third_party/quiche/src/quic/core/quic_sent_packet_manager.h index b08704de229..2d8381c7ca4 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_sent_packet_manager.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_sent_packet_manager.h @@ -91,6 +91,23 @@ class QUIC_EXPORT_PRIVATE QuicSentPacketManager { virtual void OnPathMtuIncreased(QuicPacketLength packet_size) = 0; }; + // The retransmission timer is a single timer which switches modes depending + // upon connection state. + enum RetransmissionTimeoutMode { + // A conventional TCP style RTO. + RTO_MODE, + // A tail loss probe. By default, QUIC sends up to two before RTOing. + TLP_MODE, + // Retransmission of handshake packets prior to handshake completion. + HANDSHAKE_MODE, + // Re-invoke the loss detection when a packet is not acked before the + // loss detection algorithm expects. + LOSS_MODE, + // A probe timeout. At least one probe packet must be sent when timer + // expires. + PTO_MODE, + }; + QuicSentPacketManager(Perspective perspective, const QuicClock* clock, QuicRandom* random, @@ -183,8 +200,9 @@ class QUIC_EXPORT_PRIVATE QuicSentPacketManager { TransmissionType transmission_type, HasRetransmittableData has_retransmittable_data); - // Called when the retransmission timer expires. - void OnRetransmissionTimeout(); + // Called when the retransmission timer expires and returns the retransmission + // mode. + RetransmissionTimeoutMode OnRetransmissionTimeout(); // Calculate the time until we can send the next packet to the wire. // Note 1: When kUnknownWaitTime is returned, there is no need to poll @@ -283,9 +301,7 @@ class QUIC_EXPORT_PRIVATE QuicSentPacketManager { EncryptionLevel ack_decrypted_level); // Called to enable/disable letting session decide what to write. - void SetSessionDecideWhatToWrite(bool session_decides_what_to_write) { - unacked_packets_.SetSessionDecideWhatToWrite(session_decides_what_to_write); - } + void SetSessionDecideWhatToWrite(bool session_decides_what_to_write); void EnableMultiplePacketNumberSpacesSupport(); @@ -324,6 +340,8 @@ class QUIC_EXPORT_PRIVATE QuicSentPacketManager { size_t GetConsecutiveTlpCount() const { return consecutive_tlp_count_; } + size_t GetConsecutivePtoCount() const { return consecutive_pto_count_; } + void OnApplicationLimited(); const SendAlgorithmInterface* GetSendAlgorithm() const { @@ -355,17 +373,11 @@ class QUIC_EXPORT_PRIVATE QuicSentPacketManager { return pending_timer_transmission_count_; } - QuicTime::Delta local_max_ack_delay() const { return local_max_ack_delay_; } - - void set_local_max_ack_delay(QuicTime::Delta local_max_ack_delay) { - // The delayed ack time should never be more than one half the min RTO time. - DCHECK_LE(local_max_ack_delay, (min_rto_timeout_ * 0.5)); - local_max_ack_delay_ = local_max_ack_delay; - } - QuicTime::Delta peer_max_ack_delay() const { return peer_max_ack_delay_; } void set_peer_max_ack_delay(QuicTime::Delta peer_max_ack_delay) { + // The delayed ack time should never be more than one half the min RTO time. + DCHECK_LE(peer_max_ack_delay, (min_rto_timeout_ * 0.5)); peer_max_ack_delay_ = peer_max_ack_delay; } @@ -383,6 +395,15 @@ class QUIC_EXPORT_PRIVATE QuicSentPacketManager { // Setting the send algorithm once the connection is underway is dangerous. void SetSendAlgorithm(SendAlgorithmInterface* send_algorithm); + // Sends up to max_probe_packets_per_pto_ probe packets. + void MaybeSendProbePackets(); + + // Called to adjust pending_timer_transmission_count_ accordingly. + void AdjustPendingTimerTransmissions(); + + // Called to disable HANDSHAKE_MODE, and only PTO and LOSS modes are used. + void DisableHandshakeMode(); + bool supports_multiple_packet_number_spaces() const { return unacked_packets_.supports_multiple_packet_number_spaces(); } @@ -393,24 +414,14 @@ class QUIC_EXPORT_PRIVATE QuicSentPacketManager { bool fix_rto_retransmission() const { return fix_rto_retransmission_; } + bool pto_enabled() const { return pto_enabled_; } + + bool handshake_mode_disabled() const { return handshake_mode_disabled_; } + private: friend class test::QuicConnectionPeer; friend class test::QuicSentPacketManagerPeer; - // The retransmission timer is a single timer which switches modes depending - // upon connection state. - enum RetransmissionTimeoutMode { - // A conventional TCP style RTO. - RTO_MODE, - // A tail loss probe. By default, QUIC sends up to two before RTOing. - TLP_MODE, - // Retransmission of handshake packets prior to handshake completion. - HANDSHAKE_MODE, - // Re-invoke the loss detection when a packet is not acked before the - // loss detection algorithm expects. - LOSS_MODE, - }; - typedef QuicLinkedHashMap<QuicPacketNumber, TransmissionType, QuicPacketNumberHash> @@ -452,6 +463,9 @@ class QUIC_EXPORT_PRIVATE QuicSentPacketManager { return GetRetransmissionDelay(consecutive_rto_count_); } + // Returns the probe timeout. + const QuicTime::Delta GetProbeTimeoutDelay() const; + // Returns the newest transmission associated with a packet. QuicPacketNumber GetNewestRetransmission( QuicPacketNumber packet_number, @@ -615,10 +629,6 @@ class QUIC_EXPORT_PRIVATE QuicSentPacketManager { QuicPacketNumber largest_packets_peer_knows_is_acked_[NUM_PACKET_NUMBER_SPACES]; - // The local node's maximum ack delay time. This is the maximum amount of - // time to wait before sending an acknowledgement. - QuicTime::Delta local_max_ack_delay_; - // The maximum ACK delay time that the peer uses. Initialized to be the // same as local_max_ack_delay_, may be changed via transport parameter // negotiation. @@ -634,14 +644,28 @@ class QUIC_EXPORT_PRIVATE QuicSentPacketManager { // OnAckRangeStart, and gradually moves in OnAckRange.. PacketNumberQueue::const_reverse_iterator acked_packets_iter_; + // Indicates whether PTO mode has been enabled. PTO mode unifies TLP and RTO + // modes. + bool pto_enabled_; + + // Maximum number of probes to send when PTO fires. + size_t max_probe_packets_per_pto_; + + // Number of times the PTO timer has fired in a row without receiving an ack. + size_t consecutive_pto_count_; + // Latched value of quic_loss_removes_from_inflight. const bool loss_removes_from_inflight_; // Latched value of quic_ignore_tlpr_if_no_pending_stream_data. const bool ignore_tlpr_if_no_pending_stream_data_; - // Latched value of quic_fix_rto_retransmission. - const bool fix_rto_retransmission_; + // Latched value of quic_fix_rto_retransmission3 and + // session_decides_what_to_write. + bool fix_rto_retransmission_; + + // True if HANDSHAKE mode has been disabled. + bool handshake_mode_disabled_; }; } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_sent_packet_manager_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_sent_packet_manager_test.cc index 4fce40f1859..43b00945dc0 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_sent_packet_manager_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_sent_packet_manager_test.cc @@ -357,6 +357,20 @@ class QuicSentPacketManagerTest : public QuicTestWithParam<bool> { pending.transmission_type, HAS_RETRANSMITTABLE_DATA); } + void EnablePto(QuicTag tag) { + SetQuicReloadableFlag(quic_fix_rto_retransmission3, true); + manager_.SetSessionDecideWhatToWrite(true); + SetQuicReloadableFlag(quic_enable_pto, true); + QuicConfig config; + QuicTagVector options; + options.push_back(tag); + QuicConfigPeer::SetReceivedConnectionOptions(&config, options); + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + EXPECT_CALL(*network_change_visitor_, OnCongestionChange()); + manager_.SetFromConfig(config); + EXPECT_TRUE(manager_.pto_enabled()); + } + QuicSentPacketManager manager_; MockClock clock_; QuicConnectionStats stats_; @@ -2259,12 +2273,16 @@ TEST_P(QuicSentPacketManagerTest, NegotiateNoMinTLPFromOptionsAtServer) { EXPECT_EQ(QuicTime::Delta::FromMicroseconds(100002), QuicSentPacketManagerPeer::GetTailLossProbeDelay(&manager_, 0)); - // Send two packets, and the TLP should be 2 us. + // Send two packets, and the TLP should be 2 us or 1ms. + QuicTime::Delta expected_tlp_delay = + GetQuicReloadableFlag(quic_sent_packet_manager_cleanup) + ? QuicTime::Delta::FromMilliseconds(1) + : QuicTime::Delta::FromMicroseconds(2); SendDataPacket(1); SendDataPacket(2); - EXPECT_EQ(QuicTime::Delta::FromMicroseconds(2), + EXPECT_EQ(expected_tlp_delay, QuicSentPacketManagerPeer::GetTailLossProbeDelay(&manager_)); - EXPECT_EQ(QuicTime::Delta::FromMicroseconds(2), + EXPECT_EQ(expected_tlp_delay, QuicSentPacketManagerPeer::GetTailLossProbeDelay(&manager_, 0)); } @@ -2290,16 +2308,23 @@ TEST_P(QuicSentPacketManagerTest, NegotiateNoMinTLPFromOptionsAtClient) { QuicSentPacketManagerPeer::GetTailLossProbeDelay(&manager_)); EXPECT_EQ(QuicTime::Delta::FromMicroseconds(100002), QuicSentPacketManagerPeer::GetTailLossProbeDelay(&manager_, 0)); - // Send two packets, and the TLP should be 2 us. + // Send two packets, and the TLP should be 2 us or 1ms. + QuicTime::Delta expected_tlp_delay = + GetQuicReloadableFlag(quic_sent_packet_manager_cleanup) + ? QuicTime::Delta::FromMilliseconds(1) + : QuicTime::Delta::FromMicroseconds(2); SendDataPacket(1); SendDataPacket(2); - EXPECT_EQ(QuicTime::Delta::FromMicroseconds(2), + EXPECT_EQ(expected_tlp_delay, QuicSentPacketManagerPeer::GetTailLossProbeDelay(&manager_)); - EXPECT_EQ(QuicTime::Delta::FromMicroseconds(2), + EXPECT_EQ(expected_tlp_delay, QuicSentPacketManagerPeer::GetTailLossProbeDelay(&manager_, 0)); } TEST_P(QuicSentPacketManagerTest, NegotiateIETFTLPFromOptionsAtServer) { + if (GetQuicReloadableFlag(quic_sent_packet_manager_cleanup)) { + return; + } QuicConfig config; QuicTagVector options; @@ -2328,6 +2353,9 @@ TEST_P(QuicSentPacketManagerTest, NegotiateIETFTLPFromOptionsAtServer) { } TEST_P(QuicSentPacketManagerTest, NegotiateIETFTLPFromOptionsAtClient) { + if (GetQuicReloadableFlag(quic_sent_packet_manager_cleanup)) { + return; + } QuicConfig client_config; QuicTagVector options; @@ -2369,14 +2397,22 @@ TEST_P(QuicSentPacketManagerTest, NegotiateNoMinRTOFromOptionsAtServer) { RttStats* rtt_stats = const_cast<RttStats*>(manager_.GetRttStats()); rtt_stats->UpdateRtt(QuicTime::Delta::FromMicroseconds(1), QuicTime::Delta::Zero(), QuicTime::Zero()); - EXPECT_EQ(QuicTime::Delta::FromMicroseconds(1), + QuicTime::Delta expected_rto_delay = + GetQuicReloadableFlag(quic_sent_packet_manager_cleanup) + ? QuicTime::Delta::FromMilliseconds(1) + : QuicTime::Delta::FromMicroseconds(1); + EXPECT_EQ(expected_rto_delay, QuicSentPacketManagerPeer::GetRetransmissionDelay(&manager_)); - EXPECT_EQ(QuicTime::Delta::FromMicroseconds(1), + EXPECT_EQ(expected_rto_delay, QuicSentPacketManagerPeer::GetRetransmissionDelay(&manager_, 0)); // The TLP with fewer than 2 packets outstanding includes 1/2 min RTO(0ms). - EXPECT_EQ(QuicTime::Delta::FromMicroseconds(2), + QuicTime::Delta expected_tlp_delay = + GetQuicReloadableFlag(quic_sent_packet_manager_cleanup) + ? QuicTime::Delta::FromMicroseconds(502) + : QuicTime::Delta::FromMicroseconds(2); + EXPECT_EQ(expected_tlp_delay, QuicSentPacketManagerPeer::GetTailLossProbeDelay(&manager_)); - EXPECT_EQ(QuicTime::Delta::FromMicroseconds(2), + EXPECT_EQ(expected_tlp_delay, QuicSentPacketManagerPeer::GetTailLossProbeDelay(&manager_, 0)); } @@ -2394,14 +2430,22 @@ TEST_P(QuicSentPacketManagerTest, NegotiateNoMinRTOFromOptionsAtClient) { RttStats* rtt_stats = const_cast<RttStats*>(manager_.GetRttStats()); rtt_stats->UpdateRtt(QuicTime::Delta::FromMicroseconds(1), QuicTime::Delta::Zero(), QuicTime::Zero()); - EXPECT_EQ(QuicTime::Delta::FromMicroseconds(1), + QuicTime::Delta expected_rto_delay = + GetQuicReloadableFlag(quic_sent_packet_manager_cleanup) + ? QuicTime::Delta::FromMilliseconds(1) + : QuicTime::Delta::FromMicroseconds(1); + EXPECT_EQ(expected_rto_delay, QuicSentPacketManagerPeer::GetRetransmissionDelay(&manager_)); - EXPECT_EQ(QuicTime::Delta::FromMicroseconds(1), + EXPECT_EQ(expected_rto_delay, QuicSentPacketManagerPeer::GetRetransmissionDelay(&manager_, 0)); // The TLP with fewer than 2 packets outstanding includes 1/2 min RTO(0ms). - EXPECT_EQ(QuicTime::Delta::FromMicroseconds(2), + QuicTime::Delta expected_tlp_delay = + GetQuicReloadableFlag(quic_sent_packet_manager_cleanup) + ? QuicTime::Delta::FromMicroseconds(502) + : QuicTime::Delta::FromMicroseconds(2); + EXPECT_EQ(expected_tlp_delay, QuicSentPacketManagerPeer::GetTailLossProbeDelay(&manager_)); - EXPECT_EQ(QuicTime::Delta::FromMicroseconds(2), + EXPECT_EQ(expected_tlp_delay, QuicSentPacketManagerPeer::GetTailLossProbeDelay(&manager_, 0)); } @@ -2902,15 +2946,10 @@ TEST_P(QuicSentPacketManagerTest, PacketInLimbo) { uint64_t acked2[] = {5, 6}; uint64_t loss[] = {2}; - if (GetQuicReloadableFlag(quic_fix_packets_acked)) { - // Verify packet 2 is detected lost. - EXPECT_CALL(notifier_, OnFrameLost(_)).Times(1); - ExpectAcksAndLosses(true, acked2, QUIC_ARRAYSIZE(acked2), loss, - QUIC_ARRAYSIZE(loss)); - } else { - // Packet 2 in limbo, bummer. - ExpectAcksAndLosses(true, acked2, QUIC_ARRAYSIZE(acked2), nullptr, 0); - } + // Verify packet 2 is detected lost. + EXPECT_CALL(notifier_, OnFrameLost(_)).Times(1); + ExpectAcksAndLosses(true, acked2, QUIC_ARRAYSIZE(acked2), loss, + QUIC_ARRAYSIZE(loss)); manager_.OnAckFrameStart(QuicPacketNumber(6), QuicTime::Delta::Infinite(), clock_.Now()); manager_.OnAckRange(QuicPacketNumber(3), QuicPacketNumber(7)); @@ -2942,7 +2981,7 @@ TEST_P(QuicSentPacketManagerTest, RtoFiresNoPacketToRetransmit) { EXPECT_CALL(notifier_, RetransmitFrames(_, _)).Times(0); manager_.OnRetransmissionTimeout(); EXPECT_EQ(2u, stats_.rto_count); - if (GetQuicReloadableFlag(quic_fix_rto_retransmission)) { + if (GetQuicReloadableFlag(quic_fix_rto_retransmission3)) { // Verify a credit is raised up. EXPECT_EQ(1u, manager_.pending_timer_transmission_count()); } else { @@ -2950,6 +2989,151 @@ TEST_P(QuicSentPacketManagerTest, RtoFiresNoPacketToRetransmit) { } } +TEST_P(QuicSentPacketManagerTest, ComputingProbeTimeout) { + EnablePto(k2PTO); + EXPECT_CALL(*send_algorithm_, PacingRate(_)) + .WillRepeatedly(Return(QuicBandwidth::Zero())); + EXPECT_CALL(*send_algorithm_, GetCongestionWindow()) + .WillRepeatedly(Return(10 * kDefaultTCPMSS)); + RttStats* rtt_stats = const_cast<RttStats*>(manager_.GetRttStats()); + rtt_stats->UpdateRtt(QuicTime::Delta::FromMilliseconds(100), + QuicTime::Delta::Zero(), QuicTime::Zero()); + QuicTime::Delta srtt = rtt_stats->smoothed_rtt(); + + SendDataPacket(1, ENCRYPTION_FORWARD_SECURE); + // Verify PTO is correctly set. + QuicTime::Delta expected_pto_delay = + srtt + 4 * rtt_stats->mean_deviation() + + QuicTime::Delta::FromMilliseconds(kDefaultDelayedAckTimeMs); + EXPECT_EQ(clock_.Now() + expected_pto_delay, + manager_.GetRetransmissionTime()); + + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10)); + SendDataPacket(2, ENCRYPTION_FORWARD_SECURE); + // Verify PTO is correctly set based on sent time of packet 2. + EXPECT_EQ(clock_.Now() + expected_pto_delay, + manager_.GetRetransmissionTime()); + EXPECT_EQ(0u, stats_.pto_count); + + // Invoke PTO. + clock_.AdvanceTime(expected_pto_delay); + manager_.OnRetransmissionTimeout(); + EXPECT_EQ(QuicTime::Delta::Zero(), manager_.TimeUntilSend(clock_.Now())); + EXPECT_EQ(1u, stats_.pto_count); + + // Verify two probe packets get sent. + EXPECT_CALL(notifier_, RetransmitFrames(_, _)) + .Times(2) + .WillOnce(WithArgs<1>(Invoke([this](TransmissionType type) { + RetransmitDataPacket(3, type, ENCRYPTION_FORWARD_SECURE); + }))) + .WillOnce(WithArgs<1>(Invoke([this](TransmissionType type) { + RetransmitDataPacket(4, type, ENCRYPTION_FORWARD_SECURE); + }))); + manager_.MaybeSendProbePackets(); + // Verify PTO period gets set to twice the current value. + QuicTime sent_time = clock_.Now(); + EXPECT_EQ(sent_time + expected_pto_delay * 2, + manager_.GetRetransmissionTime()); + + // Received ACK for packets 1 and 2. + uint64_t acked[] = {1, 2}; + ExpectAcksAndLosses(true, acked, QUIC_ARRAYSIZE(acked), nullptr, 0); + manager_.OnAckFrameStart(QuicPacketNumber(2), QuicTime::Delta::Infinite(), + clock_.Now()); + manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(3)); + EXPECT_EQ(PACKETS_NEWLY_ACKED, + manager_.OnAckFrameEnd(clock_.Now(), QuicPacketNumber(1), + ENCRYPTION_FORWARD_SECURE)); + expected_pto_delay = + rtt_stats->SmoothedOrInitialRtt() + + std::max(4 * rtt_stats->mean_deviation(), + QuicTime::Delta::FromMilliseconds(1)) + + QuicTime::Delta::FromMilliseconds(kDefaultDelayedAckTimeMs); + + // Verify PTO is correctly re-armed based on sent time of packet 4. + EXPECT_EQ(sent_time + expected_pto_delay, manager_.GetRetransmissionTime()); +} + +TEST_P(QuicSentPacketManagerTest, SendOneProbePacket) { + EnablePto(k1PTO); + EXPECT_CALL(*send_algorithm_, PacingRate(_)) + .WillRepeatedly(Return(QuicBandwidth::Zero())); + EXPECT_CALL(*send_algorithm_, GetCongestionWindow()) + .WillRepeatedly(Return(10 * kDefaultTCPMSS)); + + SendDataPacket(1, ENCRYPTION_FORWARD_SECURE); + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10)); + SendDataPacket(2, ENCRYPTION_FORWARD_SECURE); + + RttStats* rtt_stats = const_cast<RttStats*>(manager_.GetRttStats()); + rtt_stats->UpdateRtt(QuicTime::Delta::FromMilliseconds(100), + QuicTime::Delta::Zero(), QuicTime::Zero()); + QuicTime::Delta srtt = rtt_stats->smoothed_rtt(); + // Verify PTO period is correctly set. + QuicTime::Delta expected_pto_delay = + srtt + 4 * rtt_stats->mean_deviation() + + QuicTime::Delta::FromMilliseconds(kDefaultDelayedAckTimeMs); + EXPECT_EQ(clock_.Now() + expected_pto_delay, + manager_.GetRetransmissionTime()); + + // Invoke PTO. + clock_.AdvanceTime(expected_pto_delay); + manager_.OnRetransmissionTimeout(); + EXPECT_EQ(QuicTime::Delta::Zero(), manager_.TimeUntilSend(clock_.Now())); + + // Verify one probe packet gets sent. + EXPECT_CALL(notifier_, RetransmitFrames(_, _)) + .WillOnce(WithArgs<1>(Invoke([this](TransmissionType type) { + RetransmitDataPacket(3, type, ENCRYPTION_FORWARD_SECURE); + }))); + manager_.MaybeSendProbePackets(); +} + +TEST_P(QuicSentPacketManagerTest, DisableHandshakeModeClient) { + QuicSentPacketManagerPeer::SetPerspective(&manager_, Perspective::IS_CLIENT); + manager_.SetSessionDecideWhatToWrite(true); + manager_.DisableHandshakeMode(); + // Send CHLO. + SendCryptoPacket(1); + EXPECT_NE(QuicTime::Zero(), manager_.GetRetransmissionTime()); + // Ack packet 1. + ExpectAck(1); + manager_.OnAckFrameStart(QuicPacketNumber(1), QuicTime::Delta::Infinite(), + clock_.Now()); + manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2)); + EXPECT_EQ(PACKETS_NEWLY_ACKED, + manager_.OnAckFrameEnd(clock_.Now(), QuicPacketNumber(1), + ENCRYPTION_INITIAL)); + EXPECT_EQ(0u, manager_.GetBytesInFlight()); + // Verify retransmission timeout is not zero because handshake is not + // confirmed although there is no in flight packet. + EXPECT_NE(QuicTime::Zero(), manager_.GetRetransmissionTime()); + // Fire PTO. + EXPECT_EQ(QuicSentPacketManager::PTO_MODE, + manager_.OnRetransmissionTimeout()); +} + +TEST_P(QuicSentPacketManagerTest, DisableHandshakeModeServer) { + manager_.SetSessionDecideWhatToWrite(true); + manager_.DisableHandshakeMode(); + // Send SHLO. + SendCryptoPacket(1); + EXPECT_NE(QuicTime::Zero(), manager_.GetRetransmissionTime()); + // Ack packet 1. + ExpectAck(1); + manager_.OnAckFrameStart(QuicPacketNumber(1), QuicTime::Delta::Infinite(), + clock_.Now()); + manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2)); + EXPECT_EQ(PACKETS_NEWLY_ACKED, + manager_.OnAckFrameEnd(clock_.Now(), QuicPacketNumber(1), + ENCRYPTION_INITIAL)); + EXPECT_EQ(0u, manager_.GetBytesInFlight()); + // Verify retransmission timeout is not set on server side because there is + // nothing in flight. + EXPECT_EQ(QuicTime::Zero(), manager_.GetRetransmissionTime()); +} + } // namespace } // namespace test } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_session.cc b/chromium/net/third_party/quiche/src/quic/core/quic_session.cc index 221d75eb000..57de3771543 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_session.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_session.cc @@ -47,10 +47,12 @@ class ClosedStreamsCleanUpDelegate : public QuicAlarm::Delegate { #define ENDPOINT \ (perspective() == Perspective::IS_SERVER ? "Server: " : "Client: ") -QuicSession::QuicSession(QuicConnection* connection, - Visitor* owner, - const QuicConfig& config, - const ParsedQuicVersionVector& supported_versions) +QuicSession::QuicSession( + QuicConnection* connection, + Visitor* owner, + const QuicConfig& config, + const ParsedQuicVersionVector& supported_versions, + QuicStreamCount num_expected_unidirectional_static_streams) : connection_(connection), visitor_(owner), write_blocked_streams_(connection->transport_version()), @@ -69,7 +71,7 @@ QuicSession::QuicSession(QuicConnection* connection, num_outgoing_static_streams_(0), num_incoming_static_streams_(0), num_locally_closed_incoming_streams_highest_offset_(0), - error_(QUIC_NO_ERROR), + on_closed_frame_(QUIC_NO_ERROR, ""), flow_controller_( this, QuicUtils::GetInvalidStreamId(connection->transport_version()), @@ -88,10 +90,18 @@ QuicSession::QuicSession(QuicConnection* connection, control_frame_manager_(this), last_message_id_(0), closed_streams_clean_up_alarm_(nullptr), - supported_versions_(supported_versions) { + supported_versions_(supported_versions), + use_http2_priority_write_scheduler_(false), + is_configured_(false), + num_expected_unidirectional_static_streams_( + num_expected_unidirectional_static_streams) { closed_streams_clean_up_alarm_ = QuicWrapUnique<QuicAlarm>(connection_->alarm_factory()->CreateAlarm( new ClosedStreamsCleanUpDelegate(this))); + if (perspective() == Perspective::IS_SERVER && + connection_->version().handshake_protocol == PROTOCOL_TLS1_3) { + config_.SetStatelessResetTokenToSend(GetStatelessResetToken()); + } } void QuicSession::Initialize() { @@ -106,32 +116,28 @@ void QuicSession::Initialize() { DCHECK_EQ(QuicUtils::GetCryptoStreamId(connection_->transport_version()), GetMutableCryptoStream()->id()); - - QuicStreamId id = - QuicUtils::GetCryptoStreamId(connection_->transport_version()); - if (VersionHasIetfQuicFrames(connection_->transport_version())) { - v99_streamid_manager_.RegisterStaticStream(id, false); - } } QuicSession::~QuicSession() { QUIC_LOG_IF(WARNING, !zombie_streams_.empty()) << "Still have zombie streams"; } -void QuicSession::RegisterStaticStream(std::unique_ptr<QuicStream> stream, - bool stream_already_counted) { +void QuicSession::RegisterStaticStream(std::unique_ptr<QuicStream> stream) { DCHECK(stream->is_static()); QuicStreamId stream_id = stream->id(); stream_map_[stream_id] = std::move(stream); - if (VersionHasIetfQuicFrames(connection_->transport_version())) { - v99_streamid_manager_.RegisterStaticStream(stream_id, - stream_already_counted); - } if (IsIncomingStream(stream_id)) { ++num_incoming_static_streams_; } else { ++num_outgoing_static_streams_; } + if (VersionHasIetfQuicFrames(transport_version()) && + !QuicUtils::IsBidirectionalStreamId(stream_id)) { + DCHECK_LE(num_incoming_static_streams_, + num_expected_unidirectional_static_streams_); + DCHECK_LE(num_outgoing_static_streams_, + num_expected_unidirectional_static_streams_); + } } void QuicSession::PendingStreamOnStreamFrame(const QuicStreamFrame& frame) { @@ -157,6 +163,10 @@ void QuicSession::PendingStreamOnStreamFrame(const QuicStreamFrame& frame) { DCHECK(IsClosedStream(stream_id) || IsOpenStream(stream_id)) << "Stream " << stream_id << " not created"; pending_stream_map_.erase(stream_id); + return; + } + if (pending->sequencer()->IsClosed()) { + ClosePendingStream(stream_id); } } @@ -313,6 +323,7 @@ void QuicSession::PendingStreamOnRstStream(const QuicRstStreamFrame& frame) { } pending->OnRstStreamFrame(frame); + SendRstStream(stream_id, QUIC_RST_ACKNOWLEDGEMENT, 0); ClosePendingStream(stream_id); } @@ -386,8 +397,9 @@ void QuicSession::OnConnectionClosed(const QuicConnectionCloseFrame& frame, RecordConnectionCloseAtServer(frame.quic_error_code, source); } - if (error_ == QUIC_NO_ERROR) { - error_ = frame.quic_error_code; + if (on_closed_frame_.extracted_error_code == QUIC_NO_ERROR) { + // Save all of the connection close information + on_closed_frame_ = frame; } // Copy all non static streams in a new map for the ease of deleting. @@ -418,8 +430,8 @@ void QuicSession::OnConnectionClosed(const QuicConnectionCloseFrame& frame, if (visitor_) { visitor_->OnConnectionClosed(connection_->connection_id(), - frame.quic_error_code, frame.error_details, - source); + frame.extracted_error_code, + frame.error_details, source); } } @@ -437,10 +449,10 @@ void QuicSession::OnSuccessfulVersionNegotiation( GetMutableCryptoStream()->OnSuccessfulVersionNegotiation(version); } -void QuicSession::OnConnectivityProbeReceived( - const QuicSocketAddress& /*self_address*/, - const QuicSocketAddress& peer_address) { - if (perspective() == Perspective::IS_SERVER) { +void QuicSession::OnPacketReceived(const QuicSocketAddress& /*self_address*/, + const QuicSocketAddress& peer_address, + bool is_connectivity_probe) { + if (is_connectivity_probe && perspective() == Perspective::IS_SERVER) { // Server only sends back a connectivity probe after received a // connectivity probe from a new peer address. connection_->SendConnectivityProbingResponsePacket(peer_address); @@ -463,10 +475,10 @@ void QuicSession::OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) { QuicUtils::GetInvalidStreamId(connection_->transport_version())) { // This is a window update that applies to the connection, rather than an // individual stream. - QUIC_DLOG(INFO) << ENDPOINT - << "Received connection level flow control window " - "update with byte offset: " - << frame.byte_offset; + QUIC_DVLOG(1) << ENDPOINT + << "Received connection level flow control window " + "update with byte offset: " + << frame.byte_offset; flow_controller_.UpdateSendWindowOffset(frame.byte_offset); return; } @@ -561,11 +573,24 @@ void QuicSession::OnCanWrite() { size_t num_writes = flow_controller_.IsBlocked() ? write_blocked_streams_.NumBlockedSpecialStreams() : write_blocked_streams_.NumBlockedStreams(); - if (num_writes == 0 && !control_frame_manager_.WillingToWrite()) { + if (num_writes == 0 && !control_frame_manager_.WillingToWrite() && + (!QuicVersionUsesCryptoFrames(connection_->transport_version()) || + !GetCryptoStream()->HasBufferedCryptoFrames())) { return; } QuicConnection::ScopedPacketFlusher flusher(connection_); + if (QuicVersionUsesCryptoFrames(connection_->transport_version())) { + QuicCryptoStream* crypto_stream = GetMutableCryptoStream(); + if (crypto_stream->HasBufferedCryptoFrames()) { + crypto_stream->WriteBufferedCryptoFrames(); + } + if (crypto_stream->HasBufferedCryptoFrames()) { + // Cannot finish writing buffered crypto frames, connection is write + // blocked. + return; + } + } if (control_frame_manager_.WillingToWrite()) { control_frame_manager_.OnCanWrite(); } @@ -615,6 +640,10 @@ bool QuicSession::WillingAndAbleToWrite() const { // 3) If the crypto or headers streams are blocked, or // 4) connection is not flow control blocked and there are write blocked // streams. + if (QuicVersionUsesCryptoFrames(connection_->transport_version()) && + HasPendingHandshake()) { + return true; + } return control_frame_manager_.WillingToWrite() || !streams_with_pending_retransmission_.empty() || write_blocked_streams_.HasWriteBlockedSpecialStream() || @@ -624,9 +653,8 @@ bool QuicSession::WillingAndAbleToWrite() const { bool QuicSession::HasPendingHandshake() const { if (QuicVersionUsesCryptoFrames(connection_->transport_version())) { - // Writing CRYPTO frames is not subject to flow control, so there can't be - // pending data to write, only pending retransmissions. - return GetCryptoStream()->HasPendingCryptoRetransmission(); + return GetCryptoStream()->HasPendingCryptoRetransmission() || + GetCryptoStream()->HasBufferedCryptoFrames(); } return QuicContainsKey( streams_with_pending_retransmission_, @@ -879,19 +907,7 @@ void QuicSession::CloseStreamInner(QuicStreamId stream_id, bool locally_reset) { void QuicSession::ClosePendingStream(QuicStreamId stream_id) { QUIC_DVLOG(1) << ENDPOINT << "Closing stream " << stream_id; - if (pending_stream_map_.find(stream_id) == pending_stream_map_.end()) { - QUIC_BUG << ENDPOINT << "Stream is already closed: " << stream_id; - return; - } - - SendRstStream(stream_id, QUIC_RST_ACKNOWLEDGEMENT, 0); - - // The pending stream may have been deleted and removed during SendRstStream. - // Remove the stream from pending stream map iff it is still in the map. - if (pending_stream_map_.find(stream_id) != pending_stream_map_.end()) { - pending_stream_map_.erase(stream_id); - } - + pending_stream_map_.erase(stream_id); if (VersionHasIetfQuicFrames(connection_->transport_version())) { v99_streamid_manager_.OnStreamClosed(stream_id); } @@ -954,8 +970,7 @@ void QuicSession::OnConfigNegotiated() { } QUIC_DVLOG(1) << "Setting Bidirectional outgoing_max_streams_ to " << max_streams; - v99_streamid_manager_.AdjustMaxOpenOutgoingBidirectionalStreams( - max_streams); + v99_streamid_manager_.SetMaxOpenOutgoingBidirectionalStreams(max_streams); max_streams = 0; if (config_.HasReceivedMaxIncomingUnidirectionalStreams()) { @@ -963,8 +978,7 @@ void QuicSession::OnConfigNegotiated() { } QUIC_DVLOG(1) << "Setting Unidirectional outgoing_max_streams_ to " << max_streams; - v99_streamid_manager_.AdjustMaxOpenOutgoingUnidirectionalStreams( - max_streams); + v99_streamid_manager_.SetMaxOpenOutgoingUnidirectionalStreams(max_streams); } else { uint32_t max_streams = 0; if (config_.HasReceivedMaxIncomingBidirectionalStreams()) { @@ -993,6 +1007,31 @@ void QuicSession::OnConfigNegotiated() { if (ContainsQuicTag(config_.ReceivedConnectionOptions(), kIFWA)) { AdjustInitialFlowControlWindows(1024 * 1024); } + if (GetQuicReloadableFlag(quic_use_http2_priority_write_scheduler) && + ContainsQuicTag(config_.ReceivedConnectionOptions(), kH2PR) && + !VersionHasIetfQuicFrames(connection_->transport_version())) { + // Enable HTTP2 (tree-style) priority write scheduler. + use_http2_priority_write_scheduler_ = + write_blocked_streams_.SwitchWriteScheduler( + spdy::WriteSchedulerType::HTTP2, + connection_->transport_version()); + } else if (GetQuicReloadableFlag(quic_enable_fifo_write_scheduler) && + ContainsQuicTag(config_.ReceivedConnectionOptions(), kFIFO)) { + // Enable FIFO write scheduler. + if (write_blocked_streams_.SwitchWriteScheduler( + spdy::WriteSchedulerType::FIFO, + connection_->transport_version())) { + QUIC_RELOADABLE_FLAG_COUNT(quic_enable_fifo_write_scheduler); + } + } else if (GetQuicReloadableFlag(quic_enable_lifo_write_scheduler) && + ContainsQuicTag(config_.ReceivedConnectionOptions(), kLIFO)) { + // Enable LIFO write scheduler. + if (write_blocked_streams_.SwitchWriteScheduler( + spdy::WriteSchedulerType::LIFO, + connection_->transport_version())) { + QUIC_RELOADABLE_FLAG_COUNT(quic_enable_lifo_write_scheduler); + } + } } config_.SetStatelessResetTokenToSend(GetStatelessResetToken()); @@ -1028,6 +1067,7 @@ void QuicSession::OnConfigNegotiated() { OnNewSessionFlowControlWindow( config_.ReceivedInitialSessionFlowControlWindowBytes()); } + is_configured_ = true; } void QuicSession::AdjustInitialFlowControlWindows(size_t stream_window) { @@ -1120,14 +1160,7 @@ void QuicSession::OnNewSessionFlowControlWindow(QuicStreamOffset new_window) { void QuicSession::OnCryptoHandshakeEvent(CryptoHandshakeEvent event) { switch (event) { - // TODO(satyamshekhar): Move the logic of setting the encrypter/decrypter - // to QuicSession since it is the glue. - case ENCRYPTION_FIRST_ESTABLISHED: - // Given any streams blocked by encryption a chance to write. - OnCanWrite(); - break; - - case ENCRYPTION_REESTABLISHED: + case ENCRYPTION_ESTABLISHED: // Retransmit originally packets that were sent, since they can't be // decrypted by the peer. connection_->RetransmitUnackedPackets(ALL_INITIAL_RETRANSMISSION); @@ -1155,19 +1188,21 @@ void QuicSession::OnCryptoHandshakeMessageSent( void QuicSession::OnCryptoHandshakeMessageReceived( const CryptoHandshakeMessage& /*message*/) {} -void QuicSession::RegisterStreamPriority(QuicStreamId id, - bool is_static, - SpdyPriority priority) { - write_blocked_streams()->RegisterStream(id, is_static, priority); +void QuicSession::RegisterStreamPriority( + QuicStreamId id, + bool is_static, + const spdy::SpdyStreamPrecedence& precedence) { + write_blocked_streams()->RegisterStream(id, is_static, precedence); } void QuicSession::UnregisterStreamPriority(QuicStreamId id, bool is_static) { write_blocked_streams()->UnregisterStream(id, is_static); } -void QuicSession::UpdateStreamPriority(QuicStreamId id, - SpdyPriority new_priority) { - write_blocked_streams()->UpdateStreamPriority(id, new_priority); +void QuicSession::UpdateStreamPriority( + QuicStreamId id, + const spdy::SpdyStreamPrecedence& new_precedence) { + write_blocked_streams()->UpdateStreamPriority(id, new_precedence); } QuicConfig* QuicSession::config() { @@ -1321,7 +1356,8 @@ PendingStream* QuicSession::GetOrCreatePendingStream(QuicStreamId stream_id) { QuicStream* QuicSession::GetOrCreateDynamicStream( const QuicStreamId stream_id) { - DCHECK(!GetQuicReloadableFlag(quic_inline_getorcreatedynamicstream)); + DCHECK(!GetQuicReloadableFlag(quic_inline_getorcreatedynamicstream) || + !GetQuicReloadableFlag(quic_handle_staticness_for_spdy_stream)); StreamMap::iterator it = stream_map_.find(stream_id); if (it != stream_map_.end()) { return it->second.get(); @@ -1906,5 +1942,17 @@ size_t QuicSession::max_open_incoming_unidirectional_streams() const { return stream_id_manager_.max_open_incoming_streams(); } +std::vector<QuicStringPiece>::const_iterator QuicSession::SelectAlpn( + const std::vector<QuicStringPiece>& alpns) const { + const std::string alpn = AlpnForVersion(connection()->version()); + return std::find(alpns.cbegin(), alpns.cend(), alpn); +} + +void QuicSession::OnAlpnSelected(QuicStringPiece alpn) { + QUIC_DLOG(INFO) << (perspective() == Perspective::IS_SERVER ? "Server: " + : "Client: ") + << "ALPN selected: " << alpn; +} + #undef ENDPOINT // undef for jumbo builds } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_session.h b/chromium/net/third_party/quiche/src/quic/core/quic_session.h index b7c02122f93..16c7efc7a16 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_session.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_session.h @@ -8,6 +8,7 @@ #define QUICHE_QUIC_CORE_QUIC_SESSION_H_ #include <cstddef> +#include <cstdint> #include <map> #include <memory> #include <string> @@ -70,15 +71,9 @@ class QUIC_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface, // CryptoHandshakeEvent enumerates the events generated by a QuicCryptoStream. enum CryptoHandshakeEvent { - // ENCRYPTION_FIRST_ESTABLISHED indicates that a full client hello has been - // sent by a client and that subsequent packets will be encrypted. (Client - // only.) - ENCRYPTION_FIRST_ESTABLISHED, - // ENCRYPTION_REESTABLISHED indicates that a client hello was rejected by - // the server and thus the encryption key has been updated. Therefore the - // connection should resend any packets that were sent under - // ENCRYPTION_ZERO_RTT. (Client only.) - ENCRYPTION_REESTABLISHED, + // ENCRYPTION_ESTABLISHED indicates that a client hello has been sent and + // subsequent packets will be encrypted. (Client only.) + ENCRYPTION_ESTABLISHED, // HANDSHAKE_CONFIRMED, in a client, indicates the server has accepted // our handshake. In a server it indicates that a full, valid client hello // has been received. (Client and server.) @@ -89,7 +84,8 @@ class QUIC_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface, QuicSession(QuicConnection* connection, Visitor* owner, const QuicConfig& config, - const ParsedQuicVersionVector& supported_versions); + const ParsedQuicVersionVector& supported_versions, + QuicStreamCount num_expected_unidirectional_static_streams); QuicSession(const QuicSession&) = delete; QuicSession& operator=(const QuicSession&) = delete; @@ -110,9 +106,9 @@ class QUIC_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface, void OnWriteBlocked() override; void OnSuccessfulVersionNegotiation( const ParsedQuicVersion& version) override; - void OnConnectivityProbeReceived( - const QuicSocketAddress& self_address, - const QuicSocketAddress& peer_address) override; + void OnPacketReceived(const QuicSocketAddress& self_address, + const QuicSocketAddress& peer_address, + bool is_connectivity_probe) override; void OnCanWrite() override; bool SendProbingData() override; void OnCongestionWindowChange(QuicTime /*now*/) override {} @@ -240,8 +236,7 @@ class QUIC_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface, // Called by the QuicCryptoStream when the handshake enters a new state. // // Clients will call this function in the order: - // ENCRYPTION_FIRST_ESTABLISHED - // zero or more ENCRYPTION_REESTABLISHED + // zero or more ENCRYPTION_ESTABLISHED // HANDSHAKE_CONFIRMED // // Servers will simply call it once with HANDSHAKE_CONFIRMED. @@ -256,16 +251,18 @@ class QUIC_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface, const CryptoHandshakeMessage& message); // Called by the stream on creation to set priority in the write blocked list. - virtual void RegisterStreamPriority(QuicStreamId id, - bool is_static, - spdy::SpdyPriority priority); + virtual void RegisterStreamPriority( + QuicStreamId id, + bool is_static, + const spdy::SpdyStreamPrecedence& precedence); // Called by the stream on deletion to clear priority from the write blocked // list. virtual void UnregisterStreamPriority(QuicStreamId id, bool is_static); // Called by the stream on SetPriority to update priority on the write blocked // list. - virtual void UpdateStreamPriority(QuicStreamId id, - spdy::SpdyPriority new_priority); + virtual void UpdateStreamPriority( + QuicStreamId id, + const spdy::SpdyStreamPrecedence& new_precedence); // Returns mutable config for this session. Returned config is owned // by QuicSession. @@ -288,19 +285,19 @@ class QUIC_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface, return connection_->connection_id(); } - // Returns the number of currently open streams, excluding the reserved - // headers and crypto streams, and never counting unfinished streams. + // Returns the number of currently open streams, excluding static streams, and + // never counting unfinished streams. size_t GetNumActiveStreams() const; // Returns the number of currently draining streams. size_t GetNumDrainingStreams() const; - // Returns the number of currently open peer initiated streams, excluding the - // reserved headers and crypto streams. + // Returns the number of currently open peer initiated streams, excluding + // static streams. size_t GetNumOpenIncomingStreams() const; - // Returns the number of currently open self initiated streams, excluding the - // reserved headers and crypto streams. + // Returns the number of currently open self initiated streams, excluding + // static streams. size_t GetNumOpenOutgoingStreams() const; // Returns the number of open peer initiated static streams. @@ -317,7 +314,7 @@ class QUIC_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface, // connection-level flow control but not by its own stream-level flow control. // The stream will be given a chance to write when a connection-level // WINDOW_UPDATE arrives. - void MarkConnectionLevelWriteBlocked(QuicStreamId id); + virtual void MarkConnectionLevelWriteBlocked(QuicStreamId id); // Called when stream |id| is done waiting for acks either because all data // gets acked or is not interested in data being acked (which happens when @@ -348,7 +345,20 @@ class QUIC_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface, bool goaway_received() const { return goaway_received_; } - QuicErrorCode error() const { return error_; } + // Returns the Google QUIC error code + QuicErrorCode error() const { return on_closed_frame_.extracted_error_code; } + uint64_t transport_close_frame_type() const { + return on_closed_frame_.transport_close_frame_type; + } + QuicConnectionCloseType close_type() const { + return on_closed_frame_.close_type; + } + QuicIetfTransportErrorCodes transport_error_code() const { + return on_closed_frame_.transport_error_code; + } + uint16_t application_error_code() const { + return on_closed_frame_.application_error_code; + } Perspective perspective() const { return connection_->perspective(); } @@ -377,7 +387,7 @@ class QUIC_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface, virtual void StreamDraining(QuicStreamId id); // Returns true if this stream should yield writes to another blocked stream. - bool ShouldYield(QuicStreamId stream_id); + virtual bool ShouldYield(QuicStreamId stream_id); // Set transmission type of next sending packets. void SetTransmissionType(TransmissionType type); @@ -432,6 +442,39 @@ class QUIC_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface, return connection_->transport_version(); } + bool use_http2_priority_write_scheduler() const { + return use_http2_priority_write_scheduler_; + } + + bool is_configured() const { return is_configured_; } + + QuicStreamCount num_expected_unidirectional_static_streams() const { + return num_expected_unidirectional_static_streams_; + } + + // Set the number of unidirectional stream that the peer is allowed to open to + // be |max_stream| + |num_expected_static_streams_|. + void ConfigureMaxIncomingDynamicStreamsToSend(QuicStreamCount max_stream) { + config_.SetMaxIncomingUnidirectionalStreamsToSend( + max_stream + num_expected_unidirectional_static_streams_); + } + + // Returns the ALPN values to negotiate on this session. + virtual std::vector<std::string> GetAlpnsToOffer() const { + // TODO(vasilvv): this currently sets HTTP/3 by default. Switch all + // non-HTTP applications to appropriate ALPNs. + return std::vector<std::string>({AlpnForVersion(connection()->version())}); + } + + // Provided a list of ALPNs offered by the client, selects an ALPN from the + // list, or alpns.end() if none of the ALPNs are acceptable. + virtual std::vector<QuicStringPiece>::const_iterator SelectAlpn( + const std::vector<QuicStringPiece>& alpns) const; + + // Called when the ALPN of the connection is established for a connection that + // uses TLS handshake. + virtual void OnAlpnSelected(QuicStringPiece alpn); + protected: using StreamMap = QuicSmallMap<QuicStreamId, std::unique_ptr<QuicStream>, 10>; @@ -505,8 +548,7 @@ class QUIC_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface, // |stream| as static in stream id manager. |stream_already_counted| is true // if |stream| is created from pending stream and is already known as an open // stream. - void RegisterStaticStream(std::unique_ptr<QuicStream> stream, - bool stream_already_counted); + void RegisterStaticStream(std::unique_ptr<QuicStream> stream); StreamMap& stream_map() { return stream_map_; } const StreamMap& stream_map() const { return stream_map_; } @@ -518,7 +560,10 @@ class QUIC_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface, void set_largest_peer_created_stream_id( QuicStreamId largest_peer_created_stream_id); - void set_error(QuicErrorCode error) { error_ = error; } + void set_error(QuicErrorCode error) { + on_closed_frame_.extracted_error_code = error; + } + QuicWriteBlockedList* write_blocked_streams() { return &write_blocked_streams_; } @@ -569,6 +614,8 @@ class QUIC_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface, return false; } + bool IsHandshakeConfirmed() { return is_handshake_confirmed_; } + private: friend class test::QuicSessionPeer; @@ -684,8 +731,8 @@ class QUIC_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface, // locally_closed_streams_highest_offset_. size_t num_locally_closed_incoming_streams_highest_offset_; - // The latched error with which the connection was closed. - QuicErrorCode error_; + // Received information for a connection close. + QuicConnectionCloseFrame on_closed_frame_; // Used for connection-level flow control. QuicFlowController flow_controller_; @@ -719,6 +766,17 @@ class QUIC_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface, // Supported version list used by the crypto handshake only. Please note, this // list may be a superset of the connection framer's supported versions. ParsedQuicVersionVector supported_versions_; + + // If true, write_blocked_streams_ uses HTTP2 (tree-style) priority write + // scheduler. + bool use_http2_priority_write_scheduler_; + + // Initialized to false. Set to true when the session has been properly + // configured and is ready for general operation. + bool is_configured_; + + // The number of expected static streams. + QuicStreamCount num_expected_unidirectional_static_streams_; }; } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_session_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_session_test.cc index 90577d54483..7480eeedb32 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_session_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_session_test.cc @@ -142,7 +142,8 @@ class TestSession : public QuicSession { : QuicSession(connection, session_visitor, DefaultQuicConfig(), - CurrentSupportedVersions()), + CurrentSupportedVersions(), + /*num_expected_unidirectional_static_streams = */ 0), crypto_stream_(this), writev_consumes_all_data_(false), uses_pending_streams_(false), @@ -153,7 +154,10 @@ class TestSession : public QuicSession { QuicMakeUnique<NullEncrypter>(connection->perspective())); } - ~TestSession() override { delete connection(); } + ~TestSession() override { + EXPECT_TRUE(is_configured()); + delete connection(); + } TestCryptoStream* GetMutableCryptoStream() override { return &crypto_stream_; @@ -237,7 +241,7 @@ class TestSession : public QuicSession { } bool ShouldKeepConnectionAlive() const override { - return GetNumOpenDynamicStreams() > 0; + return GetNumActiveStreams() > 0; } QuicConsumedData WritevData(QuicStream* stream, @@ -329,7 +333,13 @@ class QuicSessionTestBase : public QuicTestWithParam<ParsedQuicVersion> { kInitialStreamFlowControlWindowForTest); session_.config()->SetInitialSessionFlowControlWindowToSend( kInitialSessionFlowControlWindowForTest); + + QuicConfigPeer::SetReceivedMaxIncomingBidirectionalStreams( + session_.config(), kDefaultMaxStreamsPerConnection); + QuicConfigPeer::SetReceivedMaxIncomingUnidirectionalStreams( + session_.config(), kDefaultMaxStreamsPerConnection); connection_->AdvanceTime(QuicTime::Delta::FromSeconds(1)); + session_.OnConfigNegotiated(); TestCryptoStream* crypto_stream = session_.GetMutableCryptoStream(); EXPECT_CALL(*crypto_stream, HasPendingRetransmission()) .Times(testing::AnyNumber()); @@ -890,7 +900,7 @@ TEST_P(QuicSessionTestServer, TestBatchedWrites) { // Now let stream 4 do the 2nd of its 3 writes, but add a block for a high // priority stream 6. 4 should be preempted. 6 will write but *not* block so // will cede back to 4. - stream6->SetPriority(kV3HighestPriority); + stream6->SetPriority(spdy::SpdyStreamPrecedence(kV3HighestPriority)); EXPECT_CALL(*stream4, OnCanWrite()) .WillOnce(Invoke([this, stream4, stream6]() { session_.SendLargeFakeData(stream4, 6000); @@ -917,6 +927,88 @@ TEST_P(QuicSessionTestServer, TestBatchedWrites) { session_.OnCanWrite(); } +TEST_P(QuicSessionTestServer, Http2Priority) { + if (VersionHasIetfQuicFrames(GetParam().transport_version)) { + return; + } + SetQuicReloadableFlag(quic_use_http2_priority_write_scheduler, true); + QuicTagVector copt; + copt.push_back(kH2PR); + QuicConfigPeer::SetReceivedConnectionOptions(session_.config(), copt); + session_.OnConfigNegotiated(); + ASSERT_TRUE(session_.use_http2_priority_write_scheduler()); + + session_.set_writev_consumes_all_data(true); + TestStream* stream2 = session_.CreateOutgoingBidirectionalStream(); + TestStream* stream4 = session_.CreateOutgoingBidirectionalStream(); + TestStream* stream6 = session_.CreateOutgoingBidirectionalStream(); + + session_.set_writev_consumes_all_data(true); + /* + 0 + /|\ + 2 4 6 + */ + session_.MarkConnectionLevelWriteBlocked(stream2->id()); + session_.MarkConnectionLevelWriteBlocked(stream4->id()); + session_.MarkConnectionLevelWriteBlocked(stream6->id()); + + // Verify streams are scheduled round robin. + InSequence s; + EXPECT_CALL(*stream2, OnCanWrite()); + EXPECT_CALL(*stream4, OnCanWrite()); + EXPECT_CALL(*stream6, OnCanWrite()); + session_.OnCanWrite(); + + /* + 0 + | + 4 + / \ + 2 6 + */ + // Update stream 4's priority. + stream4->SetPriority( + spdy::SpdyStreamPrecedence(0, spdy::kHttp2DefaultStreamWeight, true)); + session_.MarkConnectionLevelWriteBlocked(stream2->id()); + session_.MarkConnectionLevelWriteBlocked(stream4->id()); + session_.MarkConnectionLevelWriteBlocked(stream6->id()); + + EXPECT_CALL(*stream4, OnCanWrite()).WillOnce(Invoke([this, stream4]() { + session_.MarkConnectionLevelWriteBlocked(stream4->id()); + })); + EXPECT_CALL(*stream4, OnCanWrite()); + EXPECT_CALL(*stream2, OnCanWrite()); + session_.OnCanWrite(); + EXPECT_CALL(*stream6, OnCanWrite()); + session_.OnCanWrite(); + + /* + 0 + | + 6 + | + 4 + | + 2 + */ + // Update stream 6's priority. + stream6->SetPriority( + spdy::SpdyStreamPrecedence(0, spdy::kHttp2DefaultStreamWeight, true)); + session_.MarkConnectionLevelWriteBlocked(stream2->id()); + session_.MarkConnectionLevelWriteBlocked(stream4->id()); + session_.MarkConnectionLevelWriteBlocked(stream6->id()); + + EXPECT_CALL(*stream6, OnCanWrite()).WillOnce(Invoke([this, stream6]() { + session_.MarkConnectionLevelWriteBlocked(stream6->id()); + })); + EXPECT_CALL(*stream6, OnCanWrite()); + EXPECT_CALL(*stream4, OnCanWrite()); + session_.OnCanWrite(); + EXPECT_CALL(*stream2, OnCanWrite()); + session_.OnCanWrite(); +} + TEST_P(QuicSessionTestServer, OnCanWriteBundlesStreams) { // Encryption needs to be established before data can be sent. CryptoHandshakeMessage msg; @@ -1168,6 +1260,7 @@ TEST_P(QuicSessionTestServer, SendGoAway) { // GoAway frames are not in version 99 return; } + connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); MockPacketWriter* writer = static_cast<MockPacketWriter*>( QuicConnectionPeer::GetWriter(session_.connection())); EXPECT_CALL(*writer, WritePacket(_, _, _, _, _)) @@ -1235,8 +1328,8 @@ TEST_P(QuicSessionTestServer, ServerReplyToConnectivityProbe) { connection_->OnPathChallengeFrame( QuicPathChallengeFrame(0, path_frame_buffer1_)); } - session_.OnConnectivityProbeReceived(session_.self_address(), - new_peer_address); + session_.OnPacketReceived(session_.self_address(), new_peer_address, + /*is_connectivity_probe=*/true); EXPECT_EQ(old_peer_address, session_.peer_address()); } @@ -1275,8 +1368,8 @@ TEST_P(QuicSessionTestServer, ServerReplyToConnectivityProbes) { QuicPathChallengeFrame(0, path_frame_buffer1_)); connection_->OnPathChallengeFrame( QuicPathChallengeFrame(0, path_frame_buffer2_)); - session_.OnConnectivityProbeReceived(session_.self_address(), - old_peer_address); + session_.OnPacketReceived(session_.self_address(), old_peer_address, + /*is_connectivity_probe=*/true); } TEST_P(QuicSessionTestServer, IncreasedTimeoutAfterCryptoHandshake) { @@ -1289,7 +1382,7 @@ TEST_P(QuicSessionTestServer, IncreasedTimeoutAfterCryptoHandshake) { } TEST_P(QuicSessionTestServer, OnStreamFrameFinStaticStreamId) { - if (connection_->version().DoesNotHaveHeadersStream()) { + if (VersionUsesQpack(connection_->transport_version())) { return; } QuicStreamId headers_stream_id = @@ -1308,7 +1401,7 @@ TEST_P(QuicSessionTestServer, OnStreamFrameFinStaticStreamId) { } TEST_P(QuicSessionTestServer, OnRstStreamStaticStreamId) { - if (connection_->version().DoesNotHaveHeadersStream()) { + if (VersionUsesQpack(connection_->transport_version())) { return; } QuicStreamId headers_stream_id = @@ -1712,7 +1805,7 @@ TEST_P(QuicSessionTestServer, NoPendingStreams) { } TEST_P(QuicSessionTestServer, PendingStreams) { - if (!VersionHasIetfQuicFrames(transport_version())) { + if (!VersionHasStreamType(transport_version())) { return; } session_.set_uses_pending_streams(true); @@ -1721,15 +1814,17 @@ TEST_P(QuicSessionTestServer, PendingStreams) { transport_version(), Perspective::IS_CLIENT); QuicStreamFrame data1(stream_id, true, 10, QuicStringPiece("HT")); session_.OnStreamFrame(data1); + EXPECT_TRUE(QuicSessionPeer::GetPendingStream(&session_, stream_id)); EXPECT_EQ(0, session_.num_incoming_streams_created()); QuicStreamFrame data2(stream_id, false, 0, QuicStringPiece("HT")); session_.OnStreamFrame(data2); + EXPECT_FALSE(QuicSessionPeer::GetPendingStream(&session_, stream_id)); EXPECT_EQ(1, session_.num_incoming_streams_created()); } TEST_P(QuicSessionTestServer, RstPendingStreams) { - if (!VersionHasIetfQuicFrames(transport_version())) { + if (!VersionHasStreamType(transport_version())) { return; } session_.set_uses_pending_streams(true); @@ -1738,6 +1833,7 @@ TEST_P(QuicSessionTestServer, RstPendingStreams) { transport_version(), Perspective::IS_CLIENT); QuicStreamFrame data1(stream_id, true, 10, QuicStringPiece("HT")); session_.OnStreamFrame(data1); + EXPECT_TRUE(QuicSessionPeer::GetPendingStream(&session_, stream_id)); EXPECT_EQ(0, session_.num_incoming_streams_created()); EXPECT_EQ(0u, session_.GetNumOpenIncomingStreams()); @@ -1747,17 +1843,35 @@ TEST_P(QuicSessionTestServer, RstPendingStreams) { QuicRstStreamFrame rst1(kInvalidControlFrameId, stream_id, QUIC_ERROR_PROCESSING_STREAM, 12); session_.OnRstStream(rst1); + EXPECT_FALSE(QuicSessionPeer::GetPendingStream(&session_, stream_id)); EXPECT_EQ(0, session_.num_incoming_streams_created()); EXPECT_EQ(0u, session_.GetNumOpenIncomingStreams()); QuicStreamFrame data2(stream_id, false, 0, QuicStringPiece("HT")); session_.OnStreamFrame(data2); + EXPECT_FALSE(QuicSessionPeer::GetPendingStream(&session_, stream_id)); + EXPECT_EQ(0, session_.num_incoming_streams_created()); + EXPECT_EQ(0u, session_.GetNumOpenIncomingStreams()); +} + +TEST_P(QuicSessionTestServer, OnFinPendingStreams) { + if (!VersionHasStreamType(transport_version())) { + return; + } + session_.set_uses_pending_streams(true); + + QuicStreamId stream_id = QuicUtils::GetFirstUnidirectionalStreamId( + transport_version(), Perspective::IS_CLIENT); + QuicStreamFrame data(stream_id, true, 0, ""); + session_.OnStreamFrame(data); + + EXPECT_FALSE(QuicSessionPeer::GetPendingStream(&session_, stream_id)); EXPECT_EQ(0, session_.num_incoming_streams_created()); EXPECT_EQ(0u, session_.GetNumOpenIncomingStreams()); } TEST_P(QuicSessionTestServer, PendingStreamOnWindowUpdate) { - if (!VersionHasIetfQuicFrames(transport_version())) { + if (!VersionHasStreamType(transport_version())) { return; } @@ -1766,6 +1880,7 @@ TEST_P(QuicSessionTestServer, PendingStreamOnWindowUpdate) { transport_version(), Perspective::IS_CLIENT); QuicStreamFrame data1(stream_id, true, 10, QuicStringPiece("HT")); session_.OnStreamFrame(data1); + EXPECT_TRUE(QuicSessionPeer::GetPendingStream(&session_, stream_id)); EXPECT_EQ(0, session_.num_incoming_streams_created()); QuicWindowUpdateFrame window_update_frame(kInvalidControlFrameId, stream_id, 0); @@ -2579,6 +2694,31 @@ TEST_P(QuicSessionTestServer, OnStopSendingInputValidStream) { EXPECT_TRUE(stream->write_side_closed()); } +TEST_P(QuicSessionTestServer, WriteBufferedCryptoFrames) { + if (!QuicVersionUsesCryptoFrames(connection_->transport_version())) { + return; + } + std::string data(1350, 'a'); + TestCryptoStream* crypto_stream = session_.GetMutableCryptoStream(); + // Only consumed 1000 bytes. + EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_INITIAL, 1350, 0)) + .WillOnce(Return(1000)); + crypto_stream->WriteCryptoData(ENCRYPTION_INITIAL, data); + EXPECT_TRUE(session_.HasPendingHandshake()); + EXPECT_TRUE(session_.WillingAndAbleToWrite()); + + EXPECT_CALL(*connection_, SendCryptoData(_, _, _)).Times(0); + crypto_stream->WriteCryptoData(ENCRYPTION_ZERO_RTT, data); + + EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_INITIAL, 350, 1000)) + .WillOnce(Return(350)); + EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_ZERO_RTT, 1350, 0)) + .WillOnce(Return(1350)); + session_.OnCanWrite(); + EXPECT_FALSE(session_.HasPendingHandshake()); + EXPECT_FALSE(session_.WillingAndAbleToWrite()); +} + } // namespace } // namespace test } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_stream.cc b/chromium/net/third_party/quiche/src/quic/core/quic_stream.cc index 8ef4717dc7a..eb8b96ba491 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_stream.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_stream.cc @@ -58,13 +58,12 @@ PendingStream::PendingStream(QuicStreamId id, QuicSession* session) sequencer_(this) {} void PendingStream::OnDataAvailable() { - // It will be called when pending stream receives its first byte. But this - // call should simply be ignored so that data remains in sequencer. + // Data should be kept in the sequencer so that + // QuicSession::ProcessPendingStream() can read it. } void PendingStream::OnFinRead() { - QUIC_BUG << "OnFinRead should not be called."; - CloseConnectionWithDetails(QUIC_INTERNAL_ERROR, "Unexpected fin read"); + DCHECK(sequencer_.IsClosed()); } void PendingStream::AddBytesConsumed(QuicByteCount bytes) { @@ -74,6 +73,7 @@ void PendingStream::AddBytesConsumed(QuicByteCount bytes) { } void PendingStream::Reset(QuicRstStreamErrorCode error) { + // TODO: RESET_STREAM must not be sent for READ_UNIDIRECTIONAL stream. session_->SendRstStream(id_, error, 0); } @@ -173,6 +173,11 @@ void PendingStream::MarkConsumed(size_t num_bytes) { sequencer_.MarkConsumed(num_bytes); } +void PendingStream::StopReading() { + QUIC_DVLOG(1) << "Stop reading from pending stream " << id(); + sequencer_.StopReading(); +} + QuicStream::QuicStream(PendingStream* pending, StreamType type, bool is_static) : QuicStream(pending->id_, pending->session_, @@ -236,7 +241,12 @@ QuicStream::QuicStream(QuicStreamId id, : sequencer_(std::move(sequencer)), id_(id), session_(session), - priority_(kDefaultPriority), + precedence_( + session->use_http2_priority_write_scheduler() + ? spdy::SpdyStreamPrecedence(0, + spdy::kHttp2DefaultStreamWeight, + false) + : spdy::SpdyStreamPrecedence(kDefaultPriority)), stream_bytes_read_(stream_bytes_read), stream_error_(QUIC_STREAM_NO_ERROR), connection_error_(QUIC_NO_ERROR), @@ -276,7 +286,7 @@ QuicStream::QuicStream(QuicStreamId id, } SetFromConfig(); if (type_ != CRYPTO) { - session_->RegisterStreamPriority(id, is_static_, priority_); + session_->RegisterStreamPriority(id, is_static_, precedence_); } } @@ -432,13 +442,13 @@ void QuicStream::CloseConnectionWithDetails(QuicErrorCode error, error, details, ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); } -SpdyPriority QuicStream::priority() const { - return priority_; +const spdy::SpdyStreamPrecedence& QuicStream::precedence() const { + return precedence_; } -void QuicStream::SetPriority(SpdyPriority priority) { - priority_ = priority; - session_->UpdateStreamPriority(id(), priority); +void QuicStream::SetPriority(const spdy::SpdyStreamPrecedence& precedence) { + precedence_ = precedence; + session_->UpdateStreamPriority(id(), precedence); } void QuicStream::WriteOrBufferData( diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_stream.h b/chromium/net/third_party/quiche/src/quic/core/quic_stream.h index 6f5536d3991..c8e5012767a 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_stream.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_stream.h @@ -78,6 +78,10 @@ class QUIC_EXPORT_PRIVATE PendingStream void MarkConsumed(size_t num_bytes); + // Tells the sequencer to ignore all incoming data itself and not call + // OnDataAvailable(). + void StopReading(); + private: friend class QuicStream; @@ -176,11 +180,11 @@ class QUIC_EXPORT_PRIVATE QuicStream virtual void OnConnectionClosed(QuicErrorCode error, ConnectionCloseSource source); - spdy::SpdyPriority priority() const; + const spdy::SpdyStreamPrecedence& precedence() const; // Sets priority_ to priority. This should only be called before bytes are // written to the server. - void SetPriority(spdy::SpdyPriority priority); + void SetPriority(const spdy::SpdyStreamPrecedence& precedence); // Returns true if this stream is still waiting for acks of sent data. // This will return false if all data has been acked, or if the stream @@ -456,8 +460,8 @@ class QUIC_EXPORT_PRIVATE QuicStream QuicStreamId id_; // Pointer to the owning QuicSession object. QuicSession* session_; - // The priority of the stream, once parsed. - spdy::SpdyPriority priority_; + // The precedence of the stream, once parsed. + spdy::SpdyStreamPrecedence precedence_; // Bytes read refers to payload bytes only: they do not include framing, // encryption overhead etc. uint64_t stream_bytes_read_; diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_stream_id_manager.cc b/chromium/net/third_party/quiche/src/quic/core/quic_stream_id_manager.cc index f38477688e0..440b97adc19 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_stream_id_manager.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_stream_id_manager.cc @@ -30,14 +30,12 @@ QuicStreamIdManager::QuicStreamIdManager( outgoing_max_streams_(max_allowed_outgoing_streams), next_outgoing_stream_id_(GetFirstOutgoingStreamId()), outgoing_stream_count_(0), - outgoing_static_stream_count_(0), using_default_max_streams_(true), incoming_actual_max_streams_(max_allowed_incoming_streams), // Advertised max starts at actual because it's communicated in the // handshake. incoming_advertised_max_streams_(max_allowed_incoming_streams), incoming_initial_max_open_streams_(max_allowed_incoming_streams), - incoming_static_stream_count_(0), incoming_stream_count_(0), largest_peer_created_stream_id_( QuicUtils::GetInvalidStreamId(transport_version())), @@ -55,7 +53,7 @@ bool QuicStreamIdManager::OnMaxStreamsFrame(const QuicMaxStreamsFrame& frame) { const QuicStreamCount current_outgoing_max_streams = outgoing_max_streams_; // Set the limit to be exactly the stream count in the frame. - if (!ConfigureMaxOpenOutgoingStreams(frame.stream_count)) { + if (!SetMaxOpenOutgoingStreams(frame.stream_count)) { return false; } // If we were at the previous limit and this MAX_STREAMS frame @@ -99,8 +97,7 @@ bool QuicStreamIdManager::OnStreamsBlockedFrame( // Used when configuration has been done and we have an initial // maximum stream count from the peer. -bool QuicStreamIdManager::ConfigureMaxOpenOutgoingStreams( - size_t max_open_streams) { +bool QuicStreamIdManager::SetMaxOpenOutgoingStreams(size_t max_open_streams) { if (using_default_max_streams_) { // This is the first MAX_STREAMS/transport negotiation we've received. Treat // this a bit differently than later ones. The difference is that @@ -133,43 +130,11 @@ bool QuicStreamIdManager::ConfigureMaxOpenOutgoingStreams( return true; } -void QuicStreamIdManager::SetMaxOpenOutgoingStreams(size_t max_open_streams) { - QUIC_BUG_IF(!using_default_max_streams_); - // TODO(fkastenholz): when static streams are removed from I-Quic, this - // should be revised to invoke ConfigureMaxOpen... - AdjustMaxOpenOutgoingStreams(max_open_streams); -} - -// Adjust the outgoing stream limit - max_open_streams is the limit, not -// including static streams. If the new stream limit wraps, will peg -// the limit at the implementation max. -// TODO(fkastenholz): AdjustMax is cognizant of the number of static streams and -// sets the maximum to be max_streams + number_of_statics. This should be -// removed from IETF QUIC when static streams are gone. -void QuicStreamIdManager::AdjustMaxOpenOutgoingStreams( - size_t max_open_streams) { - if ((outgoing_static_stream_count_ + max_open_streams) < max_open_streams) { - // New limit causes us to wrap, set limit to be the implementation maximum. - ConfigureMaxOpenOutgoingStreams( - QuicUtils::GetMaxStreamCount(unidirectional_, perspective())); - return; - } - // Does not wrap, set limit to what is requested. - ConfigureMaxOpenOutgoingStreams(outgoing_static_stream_count_ + - max_open_streams); -} - void QuicStreamIdManager::SetMaxOpenIncomingStreams(size_t max_open_streams) { QuicStreamCount implementation_max = QuicUtils::GetMaxStreamCount(unidirectional_, perspective()); - QuicStreamCount new_max = - std::min(implementation_max, - static_cast<QuicStreamCount>(max_open_streams + - incoming_static_stream_count_)); - if (new_max < max_open_streams) { - // wrapped around ... - new_max = implementation_max; - } + QuicStreamCount new_max = std::min( + implementation_max, static_cast<QuicStreamCount>(max_open_streams)); if (new_max < incoming_stream_count_) { session_->connection()->CloseConnection( QUIC_MAX_STREAMS_ERROR, "Stream limit less than existing stream count", @@ -221,7 +186,8 @@ QuicStreamId QuicStreamIdManager::GetNextOutgoingStreamId() { // TODO(fkastenholz): Should we close the connection? QUIC_BUG_IF(outgoing_stream_count_ >= outgoing_max_streams_) << "Attempt to allocate a new outgoing stream that would exceed the " - "limit"; + "limit (" + << outgoing_max_streams_ << ")"; QuicStreamId id = next_outgoing_stream_id_; next_outgoing_stream_id_ += QuicUtils::StreamIdDelta(transport_version()); outgoing_stream_count_++; @@ -239,63 +205,6 @@ bool QuicStreamIdManager::CanOpenNextOutgoingStream() { return false; } -bool QuicStreamIdManager::RegisterStaticStream(QuicStreamId stream_id, - bool stream_already_counted) { - DCHECK_NE(QuicUtils::IsBidirectionalStreamId(stream_id), unidirectional_); - if (IsIncomingStream(stream_id)) { - // This code is predicated on static stream ids being allocated densely, in - // order, and starting with the first stream allowed. QUIC_BUG if this is - // not so. - // This is a stream id for a stream that is started by the peer, deal with - // the incoming stream ids. Increase the floor and adjust everything - // accordingly. - - QUIC_BUG_IF(incoming_actual_max_streams_ > - QuicUtils::GetMaxStreamCount(unidirectional_, perspective())); - - // If we have reached the limit on stream creation, do not create - // the static stream; return false. - if (incoming_stream_count_ >= - QuicUtils::GetMaxStreamCount(unidirectional_, perspective())) { - return false; - } - - if (incoming_actual_max_streams_ < - QuicUtils::GetMaxStreamCount(unidirectional_, perspective())) { - incoming_actual_max_streams_++; - } - if (incoming_advertised_max_streams_ < - QuicUtils::GetMaxStreamCount(unidirectional_, perspective())) { - incoming_advertised_max_streams_++; - } - - if (!stream_already_counted) { - incoming_stream_count_++; - } - incoming_static_stream_count_++; - return true; - } - - QUIC_BUG_IF(!using_default_max_streams_) - << "Attempted to allocate static stream (id " << stream_id - << ") after receiving a MAX_STREAMS frame"; - - // If we have reached the limit on stream creation, do not create - // the static stream; return false. - if (outgoing_max_streams_ >= - QuicUtils::GetMaxStreamCount(unidirectional_, perspective())) { - return false; - } - - // Increase the outgoing_max_streams_ limit to reflect the semantic that - // outgoing_max_streams_ was inialized to a "maximum request/response" count - // and only becomes a maximum stream count when we receive the first - // MAX_STREAMS. - outgoing_max_streams_++; - outgoing_static_stream_count_++; - return true; -} - // Stream_id is the id of a new incoming stream. Check if it can be // created (doesn't violate limits, etc). bool QuicStreamIdManager::MaybeIncreaseLargestPeerStreamId( diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_stream_id_manager.h b/chromium/net/third_party/quiche/src/quic/core/quic_stream_id_manager.h index d0b08246204..baba5ac8a3e 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_stream_id_manager.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_stream_id_manager.h @@ -47,12 +47,10 @@ class QUIC_EXPORT_PRIVATE QuicStreamIdManager { ", outgoing_max_streams_: ", outgoing_max_streams_, ", next_outgoing_stream_id_: ", next_outgoing_stream_id_, ", outgoing_stream_count_: ", outgoing_stream_count_, - ", outgoing_static_stream_count_: ", outgoing_static_stream_count_, ", using_default_max_streams_: ", using_default_max_streams_, ", incoming_actual_max_streams_: ", incoming_actual_max_streams_, ", incoming_advertised_max_streams_: ", incoming_advertised_max_streams_, - ", incoming_static_stream_count_: ", incoming_static_stream_count_, ", incoming_stream_count_: ", incoming_stream_count_, ", available_streams_.size(): ", available_streams_.size(), ", largest_peer_created_stream_id_: ", largest_peer_created_stream_id_, @@ -88,37 +86,14 @@ class QUIC_EXPORT_PRIVATE QuicStreamIdManager { // allocates a stream ID past the peer specified limit. QuicStreamId GetNextOutgoingStreamId(); - // Set the outgoing stream limits to be |max_open_streams| plus the number - // of static streams that have been opened. For outgoing and incoming, - // respectively. - // SetMaxOpenOutgoingStreams will QUIC_BUG if it is called after - // a MAX_STREAMS frame has been received. - // TODO(fkastenholz): When static streams disappear, these should be removed. - void SetMaxOpenOutgoingStreams(size_t max_open_streams); void SetMaxOpenIncomingStreams(size_t max_open_streams); - // Adjust the outgoing stream limit - max_open_streams is the limit, not - // including static streams. Does not QUIC_BUG if it is called _after_ - // receiving a MAX_STREAMS. - void AdjustMaxOpenOutgoingStreams(size_t max_open_streams); - // Sets the maximum number of outgoing streams to max_open_streams. // Used when configuration has been done and we have an initial // maximum stream count from the peer. Note that if the stream count is such // that it would result in stream ID values that are greater than the // implementation limit, it pegs the count at the implementation limit. - bool ConfigureMaxOpenOutgoingStreams(size_t max_open_streams); - - // Register a new stream as a static stream. This is used so that the - // advertised MAX STREAMS can be calculated based on the start of the - // dynamic stream space. This method will take any stream ID, one that either - // this node or the peer will initiate. - // If |stream_already_counted| is true, the stream is already counted as an - // open stream else where, so no need to count it again. - // Returns false if this fails because the new static stream would cause the - // stream limit to be exceeded. - bool RegisterStaticStream(QuicStreamId stream_id, - bool stream_already_counted); + bool SetMaxOpenOutgoingStreams(size_t max_open_streams); // Checks if the incoming stream ID exceeds the MAX_STREAMS limit. If the // limit is exceeded, closes the connection and returns false. Uses the @@ -134,10 +109,6 @@ class QUIC_EXPORT_PRIVATE QuicStreamIdManager { // Return true if given stream is peer initiated. bool IsIncomingStream(QuicStreamId id) const; - size_t outgoing_static_stream_count() const { - return outgoing_static_stream_count_; - } - size_t incoming_initial_max_open_streams() const { return incoming_initial_max_open_streams_; } @@ -203,8 +174,6 @@ class QUIC_EXPORT_PRIVATE QuicStreamIdManager { bool unidirectional_; // This is the number of streams that this node can initiate. - // This limit applies to both static and dynamic streams - the total - // of the two can not exceed this count. // This limit is: // - Initiated to a value specified in the constructor // - May be updated when the config is received. @@ -219,11 +188,6 @@ class QUIC_EXPORT_PRIVATE QuicStreamIdManager { // outgoing_max_streams_. QuicStreamCount outgoing_stream_count_; - // Number of outgoing static streams created. - // TODO(fkastenholz): Remove when static streams no longer supported for IETF - // QUIC. - QuicStreamCount outgoing_static_stream_count_; - // Set to true while the default (from the constructor) outgoing stream limit // is in use. It is set to false when either a MAX STREAMS frame is received // or the transport negotiation completes and sets the stream limit (this is @@ -232,12 +196,7 @@ class QUIC_EXPORT_PRIVATE QuicStreamIdManager { // until we receive an authoritative value from the peer. // outgoing_max_streams_ is initialized in the constructor // to some hard-coded value, which may or may not be consistent - // with what the peer wants. Furthermore, as we create outgoing - // static streams, the cap raises as static streams get inserted - // "beneath" the dynamic streams because, prior to receiving - // a MAX_STREAMS, the values setting the limit are interpreted - // as "number of request/responses" that can be created. Once - // a MAX_STREAMS is received, it becomes a hard limit. + // with what the peer wants. bool using_default_max_streams_; // FOR INCOMING STREAMS @@ -249,11 +208,6 @@ class QUIC_EXPORT_PRIVATE QuicStreamIdManager { // Initial maximum on the number of open streams allowed. QuicStreamCount incoming_initial_max_open_streams_; - // Number of outgoing static streams created. - // TODO(fkastenholz): Remove when static streams no longer supported for IETF - // QUIC. - QuicStreamCount incoming_static_stream_count_; - // This is the number of streams that have been created -- some are still // open, the others have been closed. It is the number that is compared // against MAX_STREAMS when deciding whether to accept a new stream or not. diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_stream_id_manager_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_stream_id_manager_test.cc index 30ff954f236..19e4d59fe14 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_stream_id_manager_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_stream_id_manager_test.cc @@ -194,18 +194,8 @@ INSTANTIATE_TEST_SUITE_P(Tests, QuicStreamIdManagerTestClient, testing::Bool()); TEST_P(QuicStreamIdManagerTestClient, StreamIdManagerClientInitialization) { // These fields are inited via the QuicSession constructor to default // values defined as a constant. - // If bidi, Crypto stream default created at start up, it is one - // more stream to account for since initialization is "number of - // request/responses" & crypto is added in to that, not streams. - size_t extra_stream_count = 0; - if (GetParam() && !QuicVersionUsesCryptoFrames(transport_version())) { - extra_stream_count = 1; - } - EXPECT_EQ(kDefaultMaxStreamsPerConnection + extra_stream_count, + EXPECT_EQ(kDefaultMaxStreamsPerConnection, stream_id_manager_->outgoing_max_streams()); - // Test is predicated on having 1 static stream going if bidi, 0 if not...) - EXPECT_EQ(extra_stream_count, - stream_id_manager_->outgoing_static_stream_count()); EXPECT_EQ(kDefaultMaxStreamsPerConnection, stream_id_manager_->incoming_actual_max_streams()); @@ -230,29 +220,6 @@ TEST_P(QuicStreamIdManagerTestClient, CheckMaxStreamsWindow1) { EXPECT_EQ(1u, stream_id_manager_->max_streams_window()); } -// Test that stream counts that would exceed the implementation maximum are -// safely handled. -// First, check that setting up to and including the implementation maximum -// is OK. -TEST_P(QuicStreamIdManagerTestClient, CheckMaxStreamsBadValuesToMaxOkOutgoing) { - QuicStreamCount implementation_max = - QuicUtils::GetMaxStreamCount(!GetParam(), /* GetParam==true for bidi */ - Perspective::IS_CLIENT); - stream_id_manager_->AdjustMaxOpenOutgoingStreams(implementation_max - 1u); - // If bidi, Crypto stream default created at start up, it is one - // more stream to account for since initialization is "number of - // request/responses" & crypto is added in to that, not streams. - size_t extra_stream_count = 0; - if (GetParam() && !QuicVersionUsesCryptoFrames(transport_version())) { - extra_stream_count = 1; - } - EXPECT_EQ(implementation_max - 1u + extra_stream_count, - stream_id_manager_->outgoing_max_streams()); - - stream_id_manager_->AdjustMaxOpenOutgoingStreams(implementation_max); - EXPECT_EQ(implementation_max, stream_id_manager_->outgoing_max_streams()); -} - // Now check that setting to a value larger than the maximum fails. TEST_P(QuicStreamIdManagerTestClient, CheckMaxStreamsBadValuesOverMaxFailsOutgoing) { @@ -263,7 +230,7 @@ TEST_P(QuicStreamIdManagerTestClient, EXPECT_LT(stream_id_manager_->outgoing_max_streams(), implementation_max); // Try to go over. - stream_id_manager_->AdjustMaxOpenOutgoingStreams(implementation_max + 1); + stream_id_manager_->SetMaxOpenOutgoingStreams(implementation_max + 1); // Should be pegged at the max. EXPECT_EQ(implementation_max, stream_id_manager_->outgoing_max_streams()); } @@ -388,15 +355,14 @@ TEST_P(QuicStreamIdManagerTestClient, StreamIdManagerClientOnMaxStreamsFrame) { QuicStreamCount initial_stream_count = // need to know the number of request/response streams. // This is the total number of outgoing streams (which includes both - // req/resp and statics) minus just the statics... - stream_id_manager_->outgoing_max_streams() - - stream_id_manager_->outgoing_static_stream_count(); + // req/resp and statics). + stream_id_manager_->outgoing_max_streams(); QuicMaxStreamsFrame frame; // Even though the stream count in the frame is < the initial maximum, - // it is should be ignored since the initial max was set via - // the constructor (an educated guess) and the MAX STREAMS frame + // it shouldn't be ignored since the initial max was set via + // the constructor (an educated guess) but the MAX STREAMS frame // is authoritative. frame.stream_count = initial_stream_count - 1; @@ -491,20 +457,12 @@ TEST_P(QuicStreamIdManagerTestClient, StreamIdManagerOnStreamsBlockedFrame) { TEST_P(QuicStreamIdManagerTestClient, StreamIdManagerGetNextOutgoingStream) { // Number of streams we can open and the first one we should get when // opening... - int number_of_streams = kDefaultMaxStreamsPerConnection; + size_t number_of_streams = kDefaultMaxStreamsPerConnection; QuicStreamId stream_id = IsUnidi() ? session_->next_outgoing_unidirectional_stream_id() : session_->next_outgoing_bidirectional_stream_id(); - // If bidi, Crypto stream default created at start up, it is one - // more stream to account for since initialization is "number of - // request/responses" & crypto is added in to that, not streams. - size_t extra_stream_count = 0; - if (IsBidi() && !QuicVersionUsesCryptoFrames(transport_version())) { - extra_stream_count = 1; - } - EXPECT_EQ(number_of_streams + extra_stream_count, - stream_id_manager_->outgoing_max_streams()); + EXPECT_EQ(number_of_streams, stream_id_manager_->outgoing_max_streams()); while (number_of_streams) { EXPECT_TRUE(stream_id_manager_->CanOpenNextOutgoingStream()); EXPECT_EQ(stream_id, stream_id_manager_->GetNextOutgoingStreamId()); @@ -522,7 +480,7 @@ TEST_P(QuicStreamIdManagerTestClient, StreamIdManagerGetNextOutgoingStream) { // If bidi, Crypto stream default created at start up, it is one // more stream to account for since initialization is "number of // request/responses" & crypto is added in to that, not streams. - EXPECT_EQ(kDefaultMaxStreamsPerConnection + extra_stream_count, + EXPECT_EQ(kDefaultMaxStreamsPerConnection, session_->save_frame().max_streams_frame.stream_count); // If we try to get the next id (above the limit), it should cause a quic-bug. EXPECT_QUIC_BUG( @@ -625,46 +583,6 @@ TEST_P(QuicStreamIdManagerTestClient, StreamIdManagerServerMaxStreams) { session_->save_frame().max_streams_frame.stream_count); } -// Test that registering static stream IDs causes the stream limit to rise -// accordingly. This is server/client agnostic. -TEST_P(QuicStreamIdManagerTestClient, TestStaticStreamAdjustment) { - QuicStreamId first_dynamic = - QuicStreamIdManagerPeer::GetFirstIncomingStreamId(stream_id_manager_); - QuicStreamCount actual_max = - stream_id_manager_->incoming_actual_max_streams(); - - // First test will register the first dynamic stream id as being for a static - // stream. - stream_id_manager_->RegisterStaticStream(first_dynamic, - /*stream_already_counted = */ false); - // Should go up by 1 stream/stream id. - EXPECT_EQ(actual_max + 1u, stream_id_manager_->incoming_actual_max_streams()); -} - -// Check that the OnMaxStreamFrame logic properly handles all the -// cases of offered max streams and outgoing_static_stream_count_, -// checking for the wrap conditions. Tests in client perspective, necessary -// because internally, some calculations depend on the client/server -// perspective. -TEST_P(QuicStreamIdManagerTestClient, TestMaxStreamsWrapChecks) { - QuicStreamCount max_stream_count = - QuicUtils::GetMaxStreamCount(IsUnidi(), Perspective::IS_CLIENT); - QuicMaxStreamsFrame frame; - frame.unidirectional = IsUnidi(); - - // Check the case where the offered stream count is less than the - // maximum - frame.stream_count = max_stream_count - 10; - EXPECT_TRUE(stream_id_manager_->OnMaxStreamsFrame(frame)); - EXPECT_EQ(max_stream_count - 10u, stream_id_manager_->outgoing_max_streams()); - - // Now check if the offered count is larger than the max. - // The count should be pegged at the max. - frame.stream_count = max_stream_count + 10; - EXPECT_TRUE(stream_id_manager_->OnMaxStreamsFrame(frame)); - EXPECT_EQ(max_stream_count, stream_id_manager_->outgoing_max_streams()); -} - // Check that edge conditions of the stream count in a STREAMS_BLOCKED frame // are. properly handled. TEST_P(QuicStreamIdManagerTestClient, StreamsBlockedEdgeConditions) { @@ -787,15 +705,8 @@ TEST_P(QuicStreamIdManagerTestServer, StreamIdManagerServerInitialization) { // values defined as a constant. EXPECT_EQ(kDefaultMaxStreamsPerConnection, stream_id_manager_->incoming_initial_max_open_streams()); - // If bidi, Crypto stream default created at start up, it is one - // more stream to account for since initialization is "number of - // request/responses" & crypto is added in to that, not streams. - // Since this is the server, the stream is incoming. - size_t extra_stream_count = 0; - if (IsBidi() && !QuicVersionUsesCryptoFrames(transport_version())) { - extra_stream_count = 1; - } - EXPECT_EQ(kDefaultMaxStreamsPerConnection + extra_stream_count, + + EXPECT_EQ(kDefaultMaxStreamsPerConnection, stream_id_manager_->incoming_actual_max_streams()); EXPECT_EQ(kDefaultMaxStreamsPerConnection, stream_id_manager_->outgoing_max_streams()); @@ -843,30 +754,6 @@ TEST_P(QuicStreamIdManagerTestServer, ExtremeMaybeIncreaseLargestPeerStreamId) { stream_id_manager_->MaybeIncreaseLargestPeerStreamId(too_big_stream_id)); } -// Check that the OnMaxStreamFrame logic properly handles all the -// cases of offered max streams and outgoing_static_stream_count_, -// checking for the wrap conditions. Tests in server perspective, necessary -// because internally, some calculations depend on the client/server -// perspective. -TEST_P(QuicStreamIdManagerTestServer, TestMaxStreamsWrapChecks) { - QuicStreamCount max_stream_count = - QuicUtils::GetMaxStreamCount(IsUnidi(), Perspective::IS_SERVER); - QuicMaxStreamsFrame frame; - frame.unidirectional = IsUnidi(); - - // Check the case where the offered stream count is less than the - // implementation maximum, - frame.stream_count = max_stream_count - 10; - EXPECT_TRUE(stream_id_manager_->OnMaxStreamsFrame(frame)); - EXPECT_EQ(max_stream_count - 10u, stream_id_manager_->outgoing_max_streams()); - - // Check the case where the offered stream count is greater than the - // implementation maximum. The count should peg at the maximum. - frame.stream_count = max_stream_count + 10; - EXPECT_TRUE(stream_id_manager_->OnMaxStreamsFrame(frame)); - EXPECT_EQ(max_stream_count, stream_id_manager_->outgoing_max_streams()); -} - } // namespace } // namespace test } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer.cc b/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer.cc index 9e47a6f4e34..dc7b0cd291d 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer.cc @@ -16,6 +16,7 @@ #include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" #include "net/third_party/quiche/src/quic/platform/api/quic_clock.h" #include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" #include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h" #include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" @@ -42,10 +43,16 @@ void QuicStreamSequencer::OnStreamFrame(const QuicStreamFrame& frame) { const size_t data_len = frame.data_length; if (frame.fin) { - CloseStreamAtOffset(frame.offset + data_len); + bool should_process_data = CloseStreamAtOffset(frame.offset + data_len); if (data_len == 0) { return; } + if (GetQuicReloadableFlag(quic_no_stream_data_after_reset)) { + QUIC_RELOADABLE_FLAG_COUNT(quic_no_stream_data_after_reset); + if (!should_process_data) { + return; + } + } } OnFrameData(byte_offset, data_len, frame.data_buffer); } @@ -109,24 +116,25 @@ void QuicStreamSequencer::OnFrameData(QuicStreamOffset byte_offset, } } -void QuicStreamSequencer::CloseStreamAtOffset(QuicStreamOffset offset) { +bool QuicStreamSequencer::CloseStreamAtOffset(QuicStreamOffset offset) { const QuicStreamOffset kMaxOffset = std::numeric_limits<QuicStreamOffset>::max(); // If there is a scheduled close, the new offset should match it. if (close_offset_ != kMaxOffset && offset != close_offset_) { stream_->Reset(QUIC_MULTIPLE_TERMINATION_OFFSETS); - return; + return false; } close_offset_ = offset; MaybeCloseStream(); + return true; } -bool QuicStreamSequencer::MaybeCloseStream() { +void QuicStreamSequencer::MaybeCloseStream() { if (blocked_ || !IsClosed()) { - return false; + return; } QUIC_DVLOG(1) << "Passing up termination, as we've processed " @@ -143,7 +151,6 @@ bool QuicStreamSequencer::MaybeCloseStream() { stream_->OnDataAvailable(); } buffered_frames_.Clear(); - return true; } int QuicStreamSequencer::GetReadableRegions(iovec* iov, size_t iov_len) const { @@ -162,11 +169,6 @@ bool QuicStreamSequencer::PeekRegion(QuicStreamOffset offset, return buffered_frames_.PeekRegion(offset, iov); } -bool QuicStreamSequencer::PrefetchNextRegion(iovec* iov) { - DCHECK(!blocked_); - return buffered_frames_.PrefetchNextRegion(iov); -} - void QuicStreamSequencer::Read(std::string* buffer) { DCHECK(!blocked_); buffer->resize(buffer->size() + ReadableBytes()); diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer.h b/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer.h index 5888a4a3cd8..a735a1ebbf5 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer.h @@ -86,10 +86,6 @@ class QUIC_EXPORT_PRIVATE QuicStreamSequencer { // not been received yet or has already been consumed. bool PeekRegion(QuicStreamOffset offset, iovec* iov) const; - // Fills in one iovec with the next unread region. - // Returns false if no readable region is available. - bool PrefetchNextRegion(iovec* iov); - // Copies the data into the iov_len buffers provided. Returns the number of // bytes read. Any buffered data no longer in use will be released. // TODO(rch): remove this method and instead implement it as a helper method @@ -167,11 +163,13 @@ class QUIC_EXPORT_PRIVATE QuicStreamSequencer { void FlushBufferedFrames(); // Wait until we've seen 'offset' bytes, and then terminate the stream. - void CloseStreamAtOffset(QuicStreamOffset offset); + // Returns true if |stream_| is still available to receive data, and false if + // |stream_| is reset. + bool CloseStreamAtOffset(QuicStreamOffset offset); // If we've received a FIN and have processed all remaining data, then inform // the stream of FIN, and clear buffers. - bool MaybeCloseStream(); + void MaybeCloseStream(); // Shared implementation between OnStreamFrame and OnCryptoFrame. void OnFrameData(QuicStreamOffset byte_offset, diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer_buffer.cc b/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer_buffer.cc index e68925b1fc7..89a6ad750f3 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer_buffer.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer_buffer.cc @@ -33,8 +33,7 @@ QuicStreamSequencerBuffer::QuicStreamSequencerBuffer(size_t max_capacity_bytes) : max_buffer_capacity_bytes_(max_capacity_bytes), blocks_count_(CalculateBlockCount(max_capacity_bytes)), total_bytes_read_(0), - blocks_(nullptr), - total_bytes_prefetched_(0) { + blocks_(nullptr) { Clear(); } @@ -53,7 +52,6 @@ void QuicStreamSequencerBuffer::Clear() { num_bytes_buffered_ = 0; bytes_received_.Clear(); bytes_received_.Add(0, total_bytes_read_); - total_bytes_prefetched_ = total_bytes_read_; } bool QuicStreamSequencerBuffer::RetireBlock(size_t idx) { @@ -267,8 +265,6 @@ QuicErrorCode QuicStreamSequencerBuffer::Readv(const iovec* dest_iov, } } } - total_bytes_prefetched_ = - std::max(total_bytes_prefetched_, total_bytes_read_); return QUIC_NO_ERROR; } @@ -367,19 +363,6 @@ bool QuicStreamSequencerBuffer::PeekRegion(QuicStreamOffset offset, return true; } -bool QuicStreamSequencerBuffer::PrefetchNextRegion(iovec* iov) { - DCHECK(iov); - DCHECK_LE(total_bytes_read_, total_bytes_prefetched_); - DCHECK_LE(total_bytes_prefetched_, FirstMissingByte()); - - if (!PeekRegion(total_bytes_prefetched_, iov)) { - return false; - } - - total_bytes_prefetched_ += iov->iov_len; - return true; -} - bool QuicStreamSequencerBuffer::MarkConsumed(size_t bytes_used) { if (bytes_used > ReadableBytes()) { return false; @@ -400,8 +383,7 @@ bool QuicStreamSequencerBuffer::MarkConsumed(size_t bytes_used) { RetireBlockIfEmpty(block_idx); } } - total_bytes_prefetched_ = - std::max(total_bytes_read_, total_bytes_prefetched_); + return true; } diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer_buffer.h b/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer_buffer.h index b0478c02b70..93b723f1781 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer_buffer.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer_buffer.h @@ -137,16 +137,6 @@ class QUIC_EXPORT_PRIVATE QuicStreamSequencerBuffer { // Does not consume data. bool PeekRegion(QuicStreamOffset offset, iovec* iov) const; - // DEPRECATED, use PeekRegion() instead. - // Called to return the next region that has not been returned by this method - // previously, except if Clear() has been called then prefetch offset is set - // to BytesConsumed(), and if Readv() or MarkConsumed() reads further than - // prefetch offset, then offset is set to beginning of not yet consumed area. - // This method only returns reference of underlying data. The caller is - // responsible for copying and consuming the data. - // Returns true if the data is read, false otherwise. - bool PrefetchNextRegion(iovec* iov); - // Called after GetReadableRegions() to free up |bytes_used| space if these // bytes are processed. // Pre-requisite: bytes_used <= available bytes to read. @@ -243,10 +233,8 @@ class QUIC_EXPORT_PRIVATE QuicStreamSequencerBuffer { // Currently received data. QuicIntervalSet<QuicStreamOffset> bytes_received_; - - // Total number of bytes that have been prefetched. - QuicStreamOffset total_bytes_prefetched_; }; + } // namespace quic #endif // QUICHE_QUIC_CORE_QUIC_STREAM_SEQUENCER_BUFFER_H_ diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer_buffer_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer_buffer_test.cc index 20dbe05f0ae..a7872dd2485 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer_buffer_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer_buffer_test.cc @@ -583,101 +583,6 @@ TEST_F(QuicStreamSequencerBufferTest, GetReadableRegionTillGap) { IovecToStringPiece(iov)); } -TEST_F(QuicStreamSequencerBufferTest, PrefetchEmptyBuffer) { - iovec iov; - EXPECT_FALSE(buffer_->PrefetchNextRegion(&iov)); -} - -TEST_F(QuicStreamSequencerBufferTest, PrefetchInitialBuffer) { - std::string source(kBlockSizeBytes, 'a'); - buffer_->OnStreamData(0, source, &written_, &error_details_); - iovec iov; - EXPECT_TRUE(buffer_->PrefetchNextRegion(&iov)); - EXPECT_EQ(source, IovecToStringPiece(iov)); -} - -TEST_F(QuicStreamSequencerBufferTest, PrefetchBufferWithOffset) { - std::string source(1024, 'a'); - buffer_->OnStreamData(0, source, &written_, &error_details_); - iovec iov; - EXPECT_TRUE(buffer_->PrefetchNextRegion(&iov)); - EXPECT_EQ(source, IovecToStringPiece(iov)); - // The second frame goes into the same block. - std::string source2(800, 'a'); - buffer_->OnStreamData(1024, source2, &written_, &error_details_); - EXPECT_TRUE(buffer_->PrefetchNextRegion(&iov)); - EXPECT_EQ(source2, IovecToStringPiece(iov)); -} - -TEST_F(QuicStreamSequencerBufferTest, PrefetchBufferWithMultipleBlocks) { - const size_t data_size = 1024; - std::string source(data_size, 'a'); - buffer_->OnStreamData(0, source, &written_, &error_details_); - iovec iov; - EXPECT_TRUE(buffer_->PrefetchNextRegion(&iov)); - EXPECT_EQ(source, IovecToStringPiece(iov)); - std::string source2(kBlockSizeBytes, 'b'); - buffer_->OnStreamData(data_size, source2, &written_, &error_details_); - EXPECT_TRUE(buffer_->PrefetchNextRegion(&iov)); - EXPECT_EQ(std::string(kBlockSizeBytes - data_size, 'b'), - IovecToStringPiece(iov)); - EXPECT_TRUE(buffer_->PrefetchNextRegion(&iov)); - EXPECT_EQ(std::string(data_size, 'b'), IovecToStringPiece(iov)); -} - -TEST_F(QuicStreamSequencerBufferTest, PrefetchBufferAfterBlockRetired) { - std::string source(kBlockSizeBytes, 'a'); - buffer_->OnStreamData(0, source, &written_, &error_details_); - iovec iov; - EXPECT_TRUE(buffer_->PrefetchNextRegion(&iov)); - EXPECT_EQ(source, IovecToStringPiece(iov)); - // Read the whole block so it's retired. - char dest[kBlockSizeBytes]; - helper_->Read(dest, kBlockSizeBytes); - - std::string source2(300, 'b'); - buffer_->OnStreamData(kBlockSizeBytes, source2, &written_, &error_details_); - - EXPECT_TRUE(buffer_->PrefetchNextRegion(&iov)); - EXPECT_EQ(source2, IovecToStringPiece(iov)); -} - -TEST_F(QuicStreamSequencerBufferTest, PrefetchContinously) { - std::string source(kBlockSizeBytes, 'a'); - buffer_->OnStreamData(0, source, &written_, &error_details_); - iovec iov; - EXPECT_TRUE(buffer_->PrefetchNextRegion(&iov)); - EXPECT_EQ(source, IovecToStringPiece(iov)); - std::string source2(kBlockSizeBytes, 'b'); - buffer_->OnStreamData(kBlockSizeBytes, source2, &written_, &error_details_); - EXPECT_TRUE(buffer_->PrefetchNextRegion(&iov)); - EXPECT_EQ(source2, IovecToStringPiece(iov)); -} - -TEST_F(QuicStreamSequencerBufferTest, ConsumeMoreThanPrefetch) { - std::string source(100, 'a'); - buffer_->OnStreamData(0, source, &written_, &error_details_); - char dest[30]; - helper_->Read(dest, 30); - iovec iov; - EXPECT_TRUE(buffer_->PrefetchNextRegion(&iov)); - EXPECT_EQ(std::string(70, 'a'), IovecToStringPiece(iov)); - std::string source2(100, 'b'); - buffer_->OnStreamData(100, source2, &written_, &error_details_); - buffer_->MarkConsumed(100); - EXPECT_TRUE(buffer_->PrefetchNextRegion(&iov)); - EXPECT_EQ(std::string(70, 'b'), IovecToStringPiece(iov)); -} - -TEST_F(QuicStreamSequencerBufferTest, PrefetchMoreThanBufferHas) { - std::string source(100, 'a'); - buffer_->OnStreamData(0, source, &written_, &error_details_); - iovec iov; - EXPECT_TRUE(buffer_->PrefetchNextRegion(&iov)); - EXPECT_EQ(std::string(100, 'a'), IovecToStringPiece(iov)); - EXPECT_FALSE(buffer_->PrefetchNextRegion(&iov)); -} - TEST_F(QuicStreamSequencerBufferTest, PeekEmptyBuffer) { iovec iov; EXPECT_FALSE(buffer_->PeekRegion(0, &iov)); @@ -1179,29 +1084,6 @@ TEST_F(QuicStreamSequencerBufferRandomIOTest, RandomWriteAndConsumeInPlace) { EXPECT_LE(bytes_to_buffer_, total_bytes_written_); } -// Regression test for https://crbug.com/969391. -TEST_F(QuicStreamSequencerBufferTest, PrefetchAfterClear) { - // Write a few bytes. - const std::string kData("foo"); - EXPECT_EQ(QUIC_NO_ERROR, buffer_->OnStreamData(/* offset = */ 0, kData, - &written_, &error_details_)); - EXPECT_EQ(kData.size(), written_); - - // Prefetch all buffered data. - iovec iov; - EXPECT_TRUE(buffer_->PrefetchNextRegion(&iov)); - EXPECT_EQ(kData, IovecToStringPiece(iov)); - - // No more data to prefetch. - EXPECT_FALSE(buffer_->PrefetchNextRegion(&iov)); - - // Clear all buffered data. - buffer_->Clear(); - - // Still no data to prefetch. - EXPECT_FALSE(buffer_->PrefetchNextRegion(&iov)); -} - } // anonymous namespace } // namespace test diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer_test.cc index 60525f8484b..b57fa6e32c7 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer_test.cc @@ -758,6 +758,17 @@ TEST_F(QuicStreamSequencerTest, StopReadingWithLevelTriggered) { OnFinFrame(6u, "ghi"); } +// Regression test for https://crbug.com/992486. +TEST_F(QuicStreamSequencerTest, CorruptFinFrames) { + SetQuicReloadableFlag(quic_no_stream_data_after_reset, true); + EXPECT_CALL(stream_, OnDataAvailable()).Times(1); + EXPECT_CALL(stream_, Reset(QUIC_MULTIPLE_TERMINATION_OFFSETS)); + + OnFinFrame(0u, ""); + OnFinFrame(0u, "a"); + EXPECT_FALSE(sequencer_->HasBytesToRead()); +} + } // namespace } // namespace test } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_stream_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_stream_test.cc index ee9676f03b7..d371866e944 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_stream_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_stream_test.cc @@ -143,9 +143,9 @@ class QuicStreamTestBase : public QuicTestWithParam<ParsedQuicVersion> { uint32_t initial_flow_control_window_bytes_; QuicTime::Delta zero_; ParsedQuicVersionVector supported_versions_; - const QuicStreamId kTestStreamId = - QuicUtils::GetHeadersStreamId(GetParam().transport_version) + - QuicUtils::StreamIdDelta(GetParam().transport_version); + QuicStreamId kTestStreamId = + GetNthClientInitiatedBidirectionalStreamId(GetParam().transport_version, + 1); }; // Non parameterized QuicStreamTest used for tests that do not diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_time_wait_list_manager.cc b/chromium/net/third_party/quiche/src/quic/core/quic_time_wait_list_manager.cc index cb29a40fbfe..f4a4f893ae7 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_time_wait_list_manager.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_time_wait_list_manager.cc @@ -206,17 +206,20 @@ void QuicTimeWaitListManager::SendVersionNegotiationPacket( QuicConnectionId server_connection_id, QuicConnectionId client_connection_id, bool ietf_quic, + bool use_length_prefix, const ParsedQuicVersionVector& supported_versions, const QuicSocketAddress& self_address, const QuicSocketAddress& peer_address, std::unique_ptr<QuicPerPacketContext> packet_context) { std::unique_ptr<QuicEncryptedPacket> version_packet = - QuicFramer::BuildVersionNegotiationPacket(server_connection_id, - client_connection_id, ietf_quic, - supported_versions); - QUIC_DVLOG(2) << "Dispatcher sending version negotiation packet: {" + QuicFramer::BuildVersionNegotiationPacket( + server_connection_id, client_connection_id, ietf_quic, + use_length_prefix, supported_versions); + QUIC_DVLOG(2) << "Dispatcher sending version negotiation packet {" << ParsedQuicVersionVectorToString(supported_versions) << "}, " - << (ietf_quic ? "" : "!") << "ietf_quic:" << std::endl + << (ietf_quic ? "" : "!") << "ietf_quic, " + << (use_length_prefix ? "" : "!") + << "use_length_prefix:" << std::endl << QuicTextUtils::HexDump(QuicStringPiece( version_packet->data(), version_packet->length())); SendOrQueuePacket(QuicMakeUnique<QueuedPacket>(self_address, peer_address, diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_time_wait_list_manager.h b/chromium/net/third_party/quiche/src/quic/core/quic_time_wait_list_manager.h index c53632c8e35..9a807c0b6ae 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_time_wait_list_manager.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_time_wait_list_manager.h @@ -125,6 +125,7 @@ class QuicTimeWaitListManager : public QuicBlockedWriterInterface { QuicConnectionId server_connection_id, QuicConnectionId client_connection_id, bool ietf_quic, + bool use_length_prefix, const ParsedQuicVersionVector& supported_versions, const QuicSocketAddress& self_address, const QuicSocketAddress& peer_address, diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_time_wait_list_manager_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_time_wait_list_manager_test.cc index 0d9e32ac62c..4caae880736 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_time_wait_list_manager_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_time_wait_list_manager_test.cc @@ -251,50 +251,67 @@ TEST_F(QuicTimeWaitListManagerTest, CheckStatelessConnectionIdInTimeWait) { TEST_F(QuicTimeWaitListManagerTest, SendVersionNegotiationPacket) { std::unique_ptr<QuicEncryptedPacket> packet( - QuicFramer::BuildVersionNegotiationPacket(connection_id_, - EmptyQuicConnectionId(), false, - AllSupportedVersions())); + QuicFramer::BuildVersionNegotiationPacket( + connection_id_, EmptyQuicConnectionId(), /*ietf_quic=*/false, + /*use_length_prefix=*/false, AllSupportedVersions())); EXPECT_CALL(writer_, WritePacket(_, packet->length(), self_address_.host(), peer_address_, _)) .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 1))); time_wait_list_manager_.SendVersionNegotiationPacket( - connection_id_, EmptyQuicConnectionId(), false, AllSupportedVersions(), - self_address_, peer_address_, QuicMakeUnique<QuicPerPacketContext>()); + connection_id_, EmptyQuicConnectionId(), /*ietf_quic=*/false, + /*use_length_prefix=*/false, AllSupportedVersions(), self_address_, + peer_address_, QuicMakeUnique<QuicPerPacketContext>()); + EXPECT_EQ(0u, time_wait_list_manager_.num_connections()); +} + +TEST_F(QuicTimeWaitListManagerTest, + SendIetfVersionNegotiationPacketWithoutLengthPrefix) { + std::unique_ptr<QuicEncryptedPacket> packet( + QuicFramer::BuildVersionNegotiationPacket( + connection_id_, EmptyQuicConnectionId(), /*ietf_quic=*/true, + /*use_length_prefix=*/false, AllSupportedVersions())); + EXPECT_CALL(writer_, WritePacket(_, packet->length(), self_address_.host(), + peer_address_, _)) + .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 1))); + + time_wait_list_manager_.SendVersionNegotiationPacket( + connection_id_, EmptyQuicConnectionId(), /*ietf_quic=*/true, + /*use_length_prefix=*/false, AllSupportedVersions(), self_address_, + peer_address_, QuicMakeUnique<QuicPerPacketContext>()); EXPECT_EQ(0u, time_wait_list_manager_.num_connections()); } TEST_F(QuicTimeWaitListManagerTest, SendIetfVersionNegotiationPacket) { std::unique_ptr<QuicEncryptedPacket> packet( - QuicFramer::BuildVersionNegotiationPacket(connection_id_, - EmptyQuicConnectionId(), true, - AllSupportedVersions())); + QuicFramer::BuildVersionNegotiationPacket( + connection_id_, EmptyQuicConnectionId(), /*ietf_quic=*/true, + /*use_length_prefix=*/true, AllSupportedVersions())); EXPECT_CALL(writer_, WritePacket(_, packet->length(), self_address_.host(), peer_address_, _)) .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 1))); time_wait_list_manager_.SendVersionNegotiationPacket( - connection_id_, EmptyQuicConnectionId(), true, AllSupportedVersions(), - self_address_, peer_address_, QuicMakeUnique<QuicPerPacketContext>()); + connection_id_, EmptyQuicConnectionId(), /*ietf_quic=*/true, + /*use_length_prefix=*/true, AllSupportedVersions(), self_address_, + peer_address_, QuicMakeUnique<QuicPerPacketContext>()); EXPECT_EQ(0u, time_wait_list_manager_.num_connections()); } TEST_F(QuicTimeWaitListManagerTest, SendIetfVersionNegotiationPacketWithClientConnectionId) { - // Client connection IDs cannot be used unless this flag is true. - SetQuicRestartFlag(quic_do_not_override_connection_id, true); - std::unique_ptr<QuicEncryptedPacket> packet( - QuicFramer::BuildVersionNegotiationPacket(connection_id_, - TestConnectionId(0x33), true, - AllSupportedVersions())); + QuicFramer::BuildVersionNegotiationPacket( + connection_id_, TestConnectionId(0x33), /*ietf_quic=*/true, + /*use_length_prefix=*/true, AllSupportedVersions())); EXPECT_CALL(writer_, WritePacket(_, packet->length(), self_address_.host(), peer_address_, _)) .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 1))); time_wait_list_manager_.SendVersionNegotiationPacket( - connection_id_, TestConnectionId(0x33), true, AllSupportedVersions(), - self_address_, peer_address_, QuicMakeUnique<QuicPerPacketContext>()); + connection_id_, TestConnectionId(0x33), /*ietf_quic=*/true, + /*use_length_prefix=*/true, AllSupportedVersions(), self_address_, + peer_address_, QuicMakeUnique<QuicPerPacketContext>()); EXPECT_EQ(0u, time_wait_list_manager_.num_connections()); } diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_types.cc b/chromium/net/third_party/quiche/src/quic/core/quic_types.cc index 102b60ecf63..46f809de3b4 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_types.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_types.cc @@ -4,6 +4,10 @@ #include "net/third_party/quiche/src/quic/core/quic_types.h" +#include <cstdint> + +#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h" + namespace quic { QuicConsumedData::QuicConsumedData(size_t bytes_consumed, bool fin_consumed) @@ -75,52 +79,396 @@ std::ostream& operator<<(std::ostream& os, const WriteResult& s) { MessageResult::MessageResult(MessageStatus status, QuicMessageId message_id) : status(status), message_id(message_id) {} -std::ostream& operator<<(std::ostream& os, - const QuicIetfTransportErrorCodes& c) { +#define RETURN_STRING_LITERAL(x) \ + case x: \ + return #x; + +std::string QuicIetfTransportErrorCodeString(QuicIetfTransportErrorCodes c) { if (static_cast<uint16_t>(c) >= 0xff00u) { - os << "Private value: " << c; - return os; + return QuicStrCat("Private value: ", static_cast<uint16_t>(c)); } switch (c) { - case NO_IETF_QUIC_ERROR: - os << "NO_IETF_QUIC_ERROR"; - break; - case INTERNAL_ERROR: - os << "INTERNAL_ERROR"; - break; - case SERVER_BUSY_ERROR: - os << "SERVER_BUSY_ERROR"; - break; - case FLOW_CONTROL_ERROR: - os << "FLOW_CONTROL_ERROR"; - break; - case STREAM_LIMIT_ERROR: - os << "STREAM_LIMIT_ERROR"; - break; - case STREAM_STATE_ERROR: - os << "STREAM_STATE_ERROR"; - break; - case FINAL_SIZE_ERROR: - os << "FINAL_SIZE_ERROR"; - break; - case FRAME_ENCODING_ERROR: - os << "FRAME_ENCODING_ERROR"; - break; - case TRANSPORT_PARAMETER_ERROR: - os << "TRANSPORT_PARAMETER_ERROR"; - break; - case VERSION_NEGOTIATION_ERROR: - os << "VERSION_NEGOTIATION_ERROR"; - break; - case PROTOCOL_VIOLATION: - os << "PROTOCOL_VIOLATION"; - break; - case INVALID_MIGRATION: - os << "INVALID_MIGRATION"; + RETURN_STRING_LITERAL(NO_IETF_QUIC_ERROR); + RETURN_STRING_LITERAL(INTERNAL_ERROR); + RETURN_STRING_LITERAL(SERVER_BUSY_ERROR); + RETURN_STRING_LITERAL(FLOW_CONTROL_ERROR); + RETURN_STRING_LITERAL(STREAM_LIMIT_ERROR); + RETURN_STRING_LITERAL(STREAM_STATE_ERROR); + RETURN_STRING_LITERAL(FINAL_SIZE_ERROR); + RETURN_STRING_LITERAL(FRAME_ENCODING_ERROR); + RETURN_STRING_LITERAL(TRANSPORT_PARAMETER_ERROR); + RETURN_STRING_LITERAL(VERSION_NEGOTIATION_ERROR); + RETURN_STRING_LITERAL(PROTOCOL_VIOLATION); + RETURN_STRING_LITERAL(INVALID_MIGRATION); + default: + return QuicStrCat("Unknown Transport Error Code Value: ", + static_cast<uint16_t>(c)); + } +} + +std::ostream& operator<<(std::ostream& os, + const QuicIetfTransportErrorCodes& c) { + os << QuicIetfTransportErrorCodeString(c); + return os; +} + +QuicErrorCodeToIetfMapping QuicErrorCodeToTransportErrorCode( + QuicErrorCode error) { + switch (error) { + // TODO(fkastenholz): Currently, all QuicError codes will map + // to application error codes and the original Google QUIC error + // code. This will change over time as we go through all calls to + // CloseConnection() and see whether the call is a Transport or an + // Application close and what the translated code should be. + case QUIC_NO_ERROR: + return {true, {static_cast<uint64_t>(QUIC_NO_ERROR)}}; + case QUIC_INTERNAL_ERROR: + return {true, {static_cast<uint64_t>(QUIC_INTERNAL_ERROR)}}; + case QUIC_STREAM_DATA_AFTER_TERMINATION: + return {true, + {static_cast<uint64_t>(QUIC_STREAM_DATA_AFTER_TERMINATION)}}; + case QUIC_INVALID_PACKET_HEADER: + return {true, {static_cast<uint64_t>(QUIC_INVALID_PACKET_HEADER)}}; + case QUIC_INVALID_FRAME_DATA: + return {true, {static_cast<uint64_t>(QUIC_INVALID_FRAME_DATA)}}; + case QUIC_MISSING_PAYLOAD: + return {true, {static_cast<uint64_t>(QUIC_MISSING_PAYLOAD)}}; + case QUIC_INVALID_FEC_DATA: + return {true, {static_cast<uint64_t>(QUIC_INVALID_FEC_DATA)}}; + case QUIC_INVALID_STREAM_DATA: + return {true, {static_cast<uint64_t>(QUIC_INVALID_STREAM_DATA)}}; + case QUIC_OVERLAPPING_STREAM_DATA: + return {true, {static_cast<uint64_t>(QUIC_OVERLAPPING_STREAM_DATA)}}; + case QUIC_UNENCRYPTED_STREAM_DATA: + return {true, {static_cast<uint64_t>(QUIC_UNENCRYPTED_STREAM_DATA)}}; + case QUIC_ATTEMPT_TO_SEND_UNENCRYPTED_STREAM_DATA: + return {true, + {static_cast<uint64_t>( + QUIC_ATTEMPT_TO_SEND_UNENCRYPTED_STREAM_DATA)}}; + case QUIC_MAYBE_CORRUPTED_MEMORY: + return {true, {static_cast<uint64_t>(QUIC_MAYBE_CORRUPTED_MEMORY)}}; + case QUIC_UNENCRYPTED_FEC_DATA: + return {true, {static_cast<uint64_t>(QUIC_UNENCRYPTED_FEC_DATA)}}; + case QUIC_INVALID_RST_STREAM_DATA: + return {true, {static_cast<uint64_t>(QUIC_INVALID_RST_STREAM_DATA)}}; + case QUIC_INVALID_CONNECTION_CLOSE_DATA: + return {true, + {static_cast<uint64_t>(QUIC_INVALID_CONNECTION_CLOSE_DATA)}}; + case QUIC_INVALID_GOAWAY_DATA: + return {true, {static_cast<uint64_t>(QUIC_INVALID_GOAWAY_DATA)}}; + case QUIC_INVALID_WINDOW_UPDATE_DATA: + return {true, {static_cast<uint64_t>(QUIC_INVALID_WINDOW_UPDATE_DATA)}}; + case QUIC_INVALID_BLOCKED_DATA: + return {true, {static_cast<uint64_t>(QUIC_INVALID_BLOCKED_DATA)}}; + case QUIC_INVALID_STOP_WAITING_DATA: + return {true, {static_cast<uint64_t>(QUIC_INVALID_STOP_WAITING_DATA)}}; + case QUIC_INVALID_PATH_CLOSE_DATA: + return {true, {static_cast<uint64_t>(QUIC_INVALID_PATH_CLOSE_DATA)}}; + case QUIC_INVALID_ACK_DATA: + return {true, {static_cast<uint64_t>(QUIC_INVALID_ACK_DATA)}}; + case QUIC_INVALID_MESSAGE_DATA: + return {true, {static_cast<uint64_t>(QUIC_INVALID_MESSAGE_DATA)}}; + case QUIC_INVALID_VERSION_NEGOTIATION_PACKET: + return {true, + {static_cast<uint64_t>(QUIC_INVALID_VERSION_NEGOTIATION_PACKET)}}; + case QUIC_INVALID_PUBLIC_RST_PACKET: + return {true, {static_cast<uint64_t>(QUIC_INVALID_PUBLIC_RST_PACKET)}}; + case QUIC_DECRYPTION_FAILURE: + return {true, {static_cast<uint64_t>(QUIC_DECRYPTION_FAILURE)}}; + case QUIC_ENCRYPTION_FAILURE: + return {true, {static_cast<uint64_t>(QUIC_ENCRYPTION_FAILURE)}}; + case QUIC_PACKET_TOO_LARGE: + return {true, {static_cast<uint64_t>(QUIC_PACKET_TOO_LARGE)}}; + case QUIC_PEER_GOING_AWAY: + return {true, {static_cast<uint64_t>(QUIC_PEER_GOING_AWAY)}}; + case QUIC_INVALID_STREAM_ID: + return {true, {static_cast<uint64_t>(QUIC_INVALID_STREAM_ID)}}; + case QUIC_INVALID_PRIORITY: + return {true, {static_cast<uint64_t>(QUIC_INVALID_PRIORITY)}}; + case QUIC_TOO_MANY_OPEN_STREAMS: + return {true, {static_cast<uint64_t>(QUIC_TOO_MANY_OPEN_STREAMS)}}; + case QUIC_TOO_MANY_AVAILABLE_STREAMS: + return {true, {static_cast<uint64_t>(QUIC_TOO_MANY_AVAILABLE_STREAMS)}}; + case QUIC_PUBLIC_RESET: + return {true, {static_cast<uint64_t>(QUIC_PUBLIC_RESET)}}; + case QUIC_INVALID_VERSION: + return {true, {static_cast<uint64_t>(QUIC_INVALID_VERSION)}}; + case QUIC_INVALID_HEADER_ID: + return {true, {static_cast<uint64_t>(QUIC_INVALID_HEADER_ID)}}; + case QUIC_INVALID_NEGOTIATED_VALUE: + return {true, {static_cast<uint64_t>(QUIC_INVALID_NEGOTIATED_VALUE)}}; + case QUIC_DECOMPRESSION_FAILURE: + return {true, {static_cast<uint64_t>(QUIC_DECOMPRESSION_FAILURE)}}; + case QUIC_NETWORK_IDLE_TIMEOUT: + return {true, {static_cast<uint64_t>(QUIC_NETWORK_IDLE_TIMEOUT)}}; + case QUIC_HANDSHAKE_TIMEOUT: + return {true, {static_cast<uint64_t>(QUIC_HANDSHAKE_TIMEOUT)}}; + case QUIC_ERROR_MIGRATING_ADDRESS: + return {true, {static_cast<uint64_t>(QUIC_ERROR_MIGRATING_ADDRESS)}}; + case QUIC_ERROR_MIGRATING_PORT: + return {true, {static_cast<uint64_t>(QUIC_ERROR_MIGRATING_PORT)}}; + case QUIC_PACKET_WRITE_ERROR: + return {true, {static_cast<uint64_t>(QUIC_PACKET_WRITE_ERROR)}}; + case QUIC_PACKET_READ_ERROR: + return {true, {static_cast<uint64_t>(QUIC_PACKET_READ_ERROR)}}; + case QUIC_EMPTY_STREAM_FRAME_NO_FIN: + return {true, {static_cast<uint64_t>(QUIC_EMPTY_STREAM_FRAME_NO_FIN)}}; + case QUIC_INVALID_HEADERS_STREAM_DATA: + return {true, {static_cast<uint64_t>(QUIC_INVALID_HEADERS_STREAM_DATA)}}; + case QUIC_HEADERS_STREAM_DATA_DECOMPRESS_FAILURE: + return { + true, + {static_cast<uint64_t>(QUIC_HEADERS_STREAM_DATA_DECOMPRESS_FAILURE)}}; + case QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA: + return { + true, + {static_cast<uint64_t>(QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA)}}; + case QUIC_FLOW_CONTROL_SENT_TOO_MUCH_DATA: + return {true, + {static_cast<uint64_t>(QUIC_FLOW_CONTROL_SENT_TOO_MUCH_DATA)}}; + case QUIC_FLOW_CONTROL_INVALID_WINDOW: + return {true, {static_cast<uint64_t>(QUIC_FLOW_CONTROL_INVALID_WINDOW)}}; + case QUIC_CONNECTION_IP_POOLED: + return {true, {static_cast<uint64_t>(QUIC_CONNECTION_IP_POOLED)}}; + case QUIC_TOO_MANY_OUTSTANDING_SENT_PACKETS: + return {true, + {static_cast<uint64_t>(QUIC_TOO_MANY_OUTSTANDING_SENT_PACKETS)}}; + case QUIC_TOO_MANY_OUTSTANDING_RECEIVED_PACKETS: + return { + true, + {static_cast<uint64_t>(QUIC_TOO_MANY_OUTSTANDING_RECEIVED_PACKETS)}}; + case QUIC_CONNECTION_CANCELLED: + return {true, {static_cast<uint64_t>(QUIC_CONNECTION_CANCELLED)}}; + case QUIC_BAD_PACKET_LOSS_RATE: + return {true, {static_cast<uint64_t>(QUIC_BAD_PACKET_LOSS_RATE)}}; + case QUIC_PUBLIC_RESETS_POST_HANDSHAKE: + return {true, {static_cast<uint64_t>(QUIC_PUBLIC_RESETS_POST_HANDSHAKE)}}; + case QUIC_FAILED_TO_SERIALIZE_PACKET: + return {true, {static_cast<uint64_t>(QUIC_FAILED_TO_SERIALIZE_PACKET)}}; + case QUIC_TOO_MANY_RTOS: + return {true, {static_cast<uint64_t>(QUIC_TOO_MANY_RTOS)}}; + case QUIC_HANDSHAKE_FAILED: + return {true, {static_cast<uint64_t>(QUIC_HANDSHAKE_FAILED)}}; + case QUIC_CRYPTO_TAGS_OUT_OF_ORDER: + return {true, {static_cast<uint64_t>(QUIC_CRYPTO_TAGS_OUT_OF_ORDER)}}; + case QUIC_CRYPTO_TOO_MANY_ENTRIES: + return {true, {static_cast<uint64_t>(QUIC_CRYPTO_TOO_MANY_ENTRIES)}}; + case QUIC_CRYPTO_INVALID_VALUE_LENGTH: + return {true, {static_cast<uint64_t>(QUIC_CRYPTO_INVALID_VALUE_LENGTH)}}; + case QUIC_CRYPTO_MESSAGE_AFTER_HANDSHAKE_COMPLETE: + return {true, + {static_cast<uint64_t>( + QUIC_CRYPTO_MESSAGE_AFTER_HANDSHAKE_COMPLETE)}}; + case QUIC_INVALID_CRYPTO_MESSAGE_TYPE: + return {true, {static_cast<uint64_t>(QUIC_INVALID_CRYPTO_MESSAGE_TYPE)}}; + case QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER: + return {true, + {static_cast<uint64_t>(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER)}}; + case QUIC_INVALID_CHANNEL_ID_SIGNATURE: + return {true, {static_cast<uint64_t>(QUIC_INVALID_CHANNEL_ID_SIGNATURE)}}; + case QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND: + return {true, + {static_cast<uint64_t>(QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND)}}; + case QUIC_CRYPTO_MESSAGE_PARAMETER_NO_OVERLAP: + return { + true, + {static_cast<uint64_t>(QUIC_CRYPTO_MESSAGE_PARAMETER_NO_OVERLAP)}}; + case QUIC_CRYPTO_MESSAGE_INDEX_NOT_FOUND: + return {true, + {static_cast<uint64_t>(QUIC_CRYPTO_MESSAGE_INDEX_NOT_FOUND)}}; + case QUIC_UNSUPPORTED_PROOF_DEMAND: + return {true, {static_cast<uint64_t>(QUIC_UNSUPPORTED_PROOF_DEMAND)}}; + case QUIC_CRYPTO_INTERNAL_ERROR: + return {true, {static_cast<uint64_t>(QUIC_CRYPTO_INTERNAL_ERROR)}}; + case QUIC_CRYPTO_VERSION_NOT_SUPPORTED: + return {true, {static_cast<uint64_t>(QUIC_CRYPTO_VERSION_NOT_SUPPORTED)}}; + case QUIC_CRYPTO_NO_SUPPORT: + return {true, {static_cast<uint64_t>(QUIC_CRYPTO_NO_SUPPORT)}}; + case QUIC_CRYPTO_TOO_MANY_REJECTS: + return {true, {static_cast<uint64_t>(QUIC_CRYPTO_TOO_MANY_REJECTS)}}; + case QUIC_PROOF_INVALID: + return {true, {static_cast<uint64_t>(QUIC_PROOF_INVALID)}}; + case QUIC_CRYPTO_DUPLICATE_TAG: + return {true, {static_cast<uint64_t>(QUIC_CRYPTO_DUPLICATE_TAG)}}; + case QUIC_CRYPTO_ENCRYPTION_LEVEL_INCORRECT: + return {true, + {static_cast<uint64_t>(QUIC_CRYPTO_ENCRYPTION_LEVEL_INCORRECT)}}; + case QUIC_CRYPTO_SERVER_CONFIG_EXPIRED: + return {true, {static_cast<uint64_t>(QUIC_CRYPTO_SERVER_CONFIG_EXPIRED)}}; + case QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED: + return {true, + {static_cast<uint64_t>(QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED)}}; + case QUIC_CRYPTO_MESSAGE_WHILE_VALIDATING_CLIENT_HELLO: + return {true, + {static_cast<uint64_t>( + QUIC_CRYPTO_MESSAGE_WHILE_VALIDATING_CLIENT_HELLO)}}; + case QUIC_CRYPTO_UPDATE_BEFORE_HANDSHAKE_COMPLETE: + return {true, + {static_cast<uint64_t>( + QUIC_CRYPTO_UPDATE_BEFORE_HANDSHAKE_COMPLETE)}}; + case QUIC_CRYPTO_CHLO_TOO_LARGE: + return {true, {static_cast<uint64_t>(QUIC_CRYPTO_CHLO_TOO_LARGE)}}; + case QUIC_VERSION_NEGOTIATION_MISMATCH: + return {true, {static_cast<uint64_t>(QUIC_VERSION_NEGOTIATION_MISMATCH)}}; + case QUIC_BAD_MULTIPATH_FLAG: + return {true, {static_cast<uint64_t>(QUIC_BAD_MULTIPATH_FLAG)}}; + case QUIC_MULTIPATH_PATH_DOES_NOT_EXIST: + return {true, + {static_cast<uint64_t>(QUIC_MULTIPATH_PATH_DOES_NOT_EXIST)}}; + case QUIC_MULTIPATH_PATH_NOT_ACTIVE: + return {true, {static_cast<uint64_t>(QUIC_MULTIPATH_PATH_NOT_ACTIVE)}}; + case QUIC_IP_ADDRESS_CHANGED: + return {true, {static_cast<uint64_t>(QUIC_IP_ADDRESS_CHANGED)}}; + case QUIC_CONNECTION_MIGRATION_NO_MIGRATABLE_STREAMS: + return {true, + {static_cast<uint64_t>( + QUIC_CONNECTION_MIGRATION_NO_MIGRATABLE_STREAMS)}}; + case QUIC_CONNECTION_MIGRATION_TOO_MANY_CHANGES: + return { + true, + {static_cast<uint64_t>(QUIC_CONNECTION_MIGRATION_TOO_MANY_CHANGES)}}; + case QUIC_CONNECTION_MIGRATION_NO_NEW_NETWORK: + return { + true, + {static_cast<uint64_t>(QUIC_CONNECTION_MIGRATION_NO_NEW_NETWORK)}}; + case QUIC_CONNECTION_MIGRATION_NON_MIGRATABLE_STREAM: + return {true, + {static_cast<uint64_t>( + QUIC_CONNECTION_MIGRATION_NON_MIGRATABLE_STREAM)}}; + case QUIC_CONNECTION_MIGRATION_DISABLED_BY_CONFIG: + return {true, + {static_cast<uint64_t>( + QUIC_CONNECTION_MIGRATION_DISABLED_BY_CONFIG)}}; + case QUIC_CONNECTION_MIGRATION_INTERNAL_ERROR: + return { + true, + {static_cast<uint64_t>(QUIC_CONNECTION_MIGRATION_INTERNAL_ERROR)}}; + case QUIC_CONNECTION_MIGRATION_HANDSHAKE_UNCONFIRMED: + return {true, + {static_cast<uint64_t>( + QUIC_CONNECTION_MIGRATION_HANDSHAKE_UNCONFIRMED)}}; + case QUIC_TOO_MANY_STREAM_DATA_INTERVALS: + return {true, + {static_cast<uint64_t>(QUIC_TOO_MANY_STREAM_DATA_INTERVALS)}}; + case QUIC_STREAM_SEQUENCER_INVALID_STATE: + return {true, + {static_cast<uint64_t>(QUIC_STREAM_SEQUENCER_INVALID_STATE)}}; + case QUIC_TOO_MANY_SESSIONS_ON_SERVER: + return {true, {static_cast<uint64_t>(QUIC_TOO_MANY_SESSIONS_ON_SERVER)}}; + case QUIC_STREAM_LENGTH_OVERFLOW: + return {true, {static_cast<uint64_t>(QUIC_STREAM_LENGTH_OVERFLOW)}}; + case QUIC_INVALID_MAX_DATA_FRAME_DATA: + return {true, {static_cast<uint64_t>(QUIC_INVALID_MAX_DATA_FRAME_DATA)}}; + case QUIC_INVALID_MAX_STREAM_DATA_FRAME_DATA: + return {true, + {static_cast<uint64_t>(QUIC_INVALID_MAX_STREAM_DATA_FRAME_DATA)}}; + case QUIC_MAX_STREAMS_DATA: + return {true, {static_cast<uint64_t>(QUIC_MAX_STREAMS_DATA)}}; + case QUIC_STREAMS_BLOCKED_DATA: + return {true, {static_cast<uint64_t>(QUIC_STREAMS_BLOCKED_DATA)}}; + case QUIC_INVALID_STREAM_BLOCKED_DATA: + return {true, {static_cast<uint64_t>(QUIC_INVALID_STREAM_BLOCKED_DATA)}}; + case QUIC_INVALID_NEW_CONNECTION_ID_DATA: + return {true, + {static_cast<uint64_t>(QUIC_INVALID_NEW_CONNECTION_ID_DATA)}}; + case QUIC_INVALID_STOP_SENDING_FRAME_DATA: + return {true, + {static_cast<uint64_t>(QUIC_INVALID_STOP_SENDING_FRAME_DATA)}}; + case QUIC_INVALID_PATH_CHALLENGE_DATA: + return {true, {static_cast<uint64_t>(QUIC_INVALID_PATH_CHALLENGE_DATA)}}; + case QUIC_INVALID_PATH_RESPONSE_DATA: + return {true, {static_cast<uint64_t>(QUIC_INVALID_PATH_RESPONSE_DATA)}}; + case IETF_QUIC_PROTOCOL_VIOLATION: + return {true, {static_cast<uint64_t>(IETF_QUIC_PROTOCOL_VIOLATION)}}; + case QUIC_INVALID_NEW_TOKEN: + return {true, {static_cast<uint64_t>(QUIC_INVALID_NEW_TOKEN)}}; + case QUIC_DATA_RECEIVED_ON_WRITE_UNIDIRECTIONAL_STREAM: + return {true, + {static_cast<uint64_t>( + QUIC_DATA_RECEIVED_ON_WRITE_UNIDIRECTIONAL_STREAM)}}; + case QUIC_TRY_TO_WRITE_DATA_ON_READ_UNIDIRECTIONAL_STREAM: + return {true, + {static_cast<uint64_t>( + QUIC_TRY_TO_WRITE_DATA_ON_READ_UNIDIRECTIONAL_STREAM)}}; + case QUIC_INVALID_RETIRE_CONNECTION_ID_DATA: + return {true, + {static_cast<uint64_t>(QUIC_INVALID_RETIRE_CONNECTION_ID_DATA)}}; + case QUIC_STREAMS_BLOCKED_ERROR: + return {true, {static_cast<uint64_t>(QUIC_STREAMS_BLOCKED_ERROR)}}; + case QUIC_MAX_STREAMS_ERROR: + return {true, {static_cast<uint64_t>(QUIC_MAX_STREAMS_ERROR)}}; + case QUIC_HTTP_DECODER_ERROR: + return {true, {static_cast<uint64_t>(QUIC_HTTP_DECODER_ERROR)}}; + case QUIC_STALE_CONNECTION_CANCELLED: + return {true, {static_cast<uint64_t>(QUIC_STALE_CONNECTION_CANCELLED)}}; + case QUIC_IETF_GQUIC_ERROR_MISSING: + return {true, {static_cast<uint64_t>(QUIC_IETF_GQUIC_ERROR_MISSING)}}; + case QUIC_WINDOW_UPDATE_RECEIVED_ON_READ_UNIDIRECTIONAL_STREAM: + return {true, + {static_cast<uint64_t>( + QUIC_WINDOW_UPDATE_RECEIVED_ON_READ_UNIDIRECTIONAL_STREAM)}}; + case QUIC_TOO_MANY_BUFFERED_CONTROL_FRAMES: + return {true, + {static_cast<uint64_t>(QUIC_TOO_MANY_BUFFERED_CONTROL_FRAMES)}}; + case QUIC_LAST_ERROR: + return {false, {static_cast<uint64_t>(QUIC_LAST_ERROR)}}; + } + // If it's an unknown code, indicate it's an application error code. + return {false, {NO_IETF_QUIC_ERROR}}; +} + +std::string QuicIetfFrameTypeString(QuicIetfFrameType t) { + if (IS_IETF_STREAM_FRAME(t)) { + return "IETF_STREAM"; + } + + switch (t) { + RETURN_STRING_LITERAL(IETF_PADDING); + RETURN_STRING_LITERAL(IETF_PING); + RETURN_STRING_LITERAL(IETF_ACK); + RETURN_STRING_LITERAL(IETF_ACK_ECN); + RETURN_STRING_LITERAL(IETF_RST_STREAM); + RETURN_STRING_LITERAL(IETF_STOP_SENDING); + RETURN_STRING_LITERAL(IETF_CRYPTO); + RETURN_STRING_LITERAL(IETF_NEW_TOKEN); + RETURN_STRING_LITERAL(IETF_MAX_DATA); + RETURN_STRING_LITERAL(IETF_MAX_STREAM_DATA); + RETURN_STRING_LITERAL(IETF_MAX_STREAMS_BIDIRECTIONAL); + RETURN_STRING_LITERAL(IETF_MAX_STREAMS_UNIDIRECTIONAL); + RETURN_STRING_LITERAL(IETF_BLOCKED); + RETURN_STRING_LITERAL(IETF_STREAM_BLOCKED); + RETURN_STRING_LITERAL(IETF_STREAMS_BLOCKED_BIDIRECTIONAL); + RETURN_STRING_LITERAL(IETF_STREAMS_BLOCKED_UNIDIRECTIONAL); + RETURN_STRING_LITERAL(IETF_NEW_CONNECTION_ID); + RETURN_STRING_LITERAL(IETF_RETIRE_CONNECTION_ID); + RETURN_STRING_LITERAL(IETF_PATH_CHALLENGE); + RETURN_STRING_LITERAL(IETF_PATH_RESPONSE); + RETURN_STRING_LITERAL(IETF_CONNECTION_CLOSE); + RETURN_STRING_LITERAL(IETF_APPLICATION_CLOSE); + RETURN_STRING_LITERAL(IETF_EXTENSION_MESSAGE_NO_LENGTH); + RETURN_STRING_LITERAL(IETF_EXTENSION_MESSAGE); + default: + return QuicStrCat("Private value (", t, ")"); + } +} +std::ostream& operator<<(std::ostream& os, const QuicIetfFrameType& c) { + os << QuicIetfFrameTypeString(c); + return os; +} + +std::string QuicConnectionCloseTypeString(QuicConnectionCloseType type) { + switch (type) { + RETURN_STRING_LITERAL(GOOGLE_QUIC_CONNECTION_CLOSE); + RETURN_STRING_LITERAL(IETF_QUIC_TRANSPORT_CONNECTION_CLOSE); + RETURN_STRING_LITERAL(IETF_QUIC_APPLICATION_CONNECTION_CLOSE); + default: + return QuicStrCat("Unknown: ", static_cast<int>(type)); break; - // No default -- compiler will catch any adds/changes then. } +} +std::ostream& operator<<(std::ostream& os, const QuicConnectionCloseType type) { + os << QuicConnectionCloseTypeString(type); return os; } diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_types.h b/chromium/net/third_party/quiche/src/quic/core/quic_types.h index c3f70e739d4..2d893927120 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_types.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_types.h @@ -267,6 +267,10 @@ enum QuicIetfFrameType : uint8_t { IETF_EXTENSION_MESSAGE_NO_LENGTH = 0x20, IETF_EXTENSION_MESSAGE = 0x21, }; +QUIC_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os, + const QuicIetfFrameType& c); +QUIC_EXPORT_PRIVATE std::string QuicIetfFrameTypeString(QuicIetfFrameType t); + // Masks for the bits that indicate the frame is a Stream frame vs the // bits used as flags. #define IETF_STREAM_FRAME_TYPE_MASK 0xfffffffffffffff8 @@ -298,7 +302,7 @@ const QuicVariableLengthIntegerLength kQuicDefaultLongHeaderLengthLength = enum QuicPacketNumberLength : uint8_t { PACKET_1BYTE_PACKET_NUMBER = 1, PACKET_2BYTE_PACKET_NUMBER = 2, - PACKET_3BYTE_PACKET_NUMBER = 3, // Used in version > QUIC_VERSION_44. + PACKET_3BYTE_PACKET_NUMBER = 3, // Used in versions 45+. PACKET_4BYTE_PACKET_NUMBER = 4, IETF_MAX_PACKET_NUMBER_LENGTH = 4, // TODO(rch): Remove this when we remove QUIC_VERSION_39. @@ -519,10 +523,28 @@ enum QuicIetfTransportErrorCodes : uint16_t { PROTOCOL_VIOLATION = 0xA, INVALID_MIGRATION = 0xC, }; +QUIC_EXPORT_PRIVATE std::string QuicIetfTransportErrorCodeString( + QuicIetfTransportErrorCodes c); + QUIC_EXPORT_PRIVATE std::ostream& operator<<( std::ostream& os, const QuicIetfTransportErrorCodes& c); +// Returns the mapping of the QuicErrorCode to an IETF TransportErrorCode. If +// first element of the pair is false, it means that an IETF Application Close +// should be done instead. + +struct QuicErrorCodeToIetfMapping { + bool is_transport_close_; + union { + uint64_t application_error_code_; + QuicIetfTransportErrorCodes transport_error_code_; + }; +}; + +QUIC_EXPORT_PRIVATE QuicErrorCodeToIetfMapping +QuicErrorCodeToTransportErrorCode(QuicErrorCode error); + // Please note, this value cannot used directly for packet serialization. enum QuicLongHeaderType : uint8_t { VERSION_NEGOTIATION, @@ -624,6 +646,20 @@ enum AckResult { PACKETS_ACKED_IN_WRONG_PACKET_NUMBER_SPACE, }; +// There are three different forms of CONNECTION_CLOSE. +typedef enum QuicConnectionCloseType { + GOOGLE_QUIC_CONNECTION_CLOSE = 0, + IETF_QUIC_TRANSPORT_CONNECTION_CLOSE = 1, + IETF_QUIC_APPLICATION_CONNECTION_CLOSE = 2 +} QuicConnectionCloseType; + +QUIC_EXPORT_PRIVATE std::ostream& operator<<( + std::ostream& os, + const QuicConnectionCloseType type); + +QUIC_EXPORT_PRIVATE std::string QuicConnectionCloseTypeString( + QuicConnectionCloseType type); + } // namespace quic #endif // QUICHE_QUIC_CORE_QUIC_TYPES_H_ diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_types_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_types_test.cc new file mode 100644 index 00000000000..8b4df895224 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/core/quic_types_test.cc @@ -0,0 +1,40 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/third_party/quiche/src/quic/core/quic_types.h" + +#include <cstdint> + +#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" + +namespace quic { +namespace test { +namespace { + +class QuicUtilsTest : public QuicTest {}; + +TEST_F(QuicUtilsTest, QuicIetfTransportErrorCodeString) { + // QuicIetfTransportErrorCode out of bound. + for (quic::QuicErrorCode error = quic::QUIC_ENCRYPTION_FAILURE; + error < quic::QUIC_LAST_ERROR; + error = static_cast<quic::QuicErrorCode>(error + 1)) { + QuicErrorCodeToIetfMapping mapping = + QuicErrorCodeToTransportErrorCode(error); + if (mapping.is_transport_close_) { + EXPECT_EQ( + QuicIetfTransportErrorCodeString(mapping.transport_error_code_), + QuicStrCat("Unknown Transport Error Code Value: ", + static_cast<uint16_t>(mapping.transport_error_code_))); + } else { + // Some QuicErrorCodes are no longer valid. + EXPECT_EQ(QuicIetfTransportErrorCodeString(mapping.transport_error_code_), + "NO_IETF_QUIC_ERROR"); + } + } +} + +} // namespace +} // namespace test +} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_unacked_packet_map_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_unacked_packet_map_test.cc index 807a95918ab..5dda2ae9de3 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_unacked_packet_map_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_unacked_packet_map_test.cc @@ -68,8 +68,9 @@ class QuicUnackedPacketMapTest : public QuicTestWithParam<TestParams> { SerializedPacket CreateRetransmittablePacket(uint64_t packet_number) { return CreateRetransmittablePacketForStream( - packet_number, QuicUtils::GetHeadersStreamId( - CurrentSupportedVersions()[0].transport_version)); + packet_number, QuicUtils::GetFirstBidirectionalStreamId( + CurrentSupportedVersions()[0].transport_version, + Perspective::IS_CLIENT)); } SerializedPacket CreateRetransmittablePacketForStream( @@ -174,8 +175,9 @@ class QuicUnackedPacketMapTest : public QuicTestWithParam<TestParams> { } QuicTransmissionInfo* info = unacked_packets_.GetMutableTransmissionInfo( QuicPacketNumber(old_packet_number)); - QuicStreamId stream_id = QuicUtils::GetHeadersStreamId( - CurrentSupportedVersions()[0].transport_version); + QuicStreamId stream_id = QuicUtils::GetFirstBidirectionalStreamId( + CurrentSupportedVersions()[0].transport_version, + Perspective::IS_CLIENT); for (const auto& frame : info->retransmittable_frames) { if (frame.type == STREAM_FRAME) { stream_id = frame.stream_frame.stream_id; diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_utils.cc b/chromium/net/third_party/quiche/src/quic/core/quic_utils.cc index 40c50afc524..da2cb1a699c 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_utils.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_utils.cc @@ -11,6 +11,7 @@ #include "net/third_party/quiche/src/quic/core/quic_connection_id.h" #include "net/third_party/quiche/src/quic/core/quic_constants.h" #include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/core/quic_versions.h" #include "net/third_party/quiche/src/quic/platform/api/quic_aligned.h" #include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" #include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" @@ -400,11 +401,7 @@ bool QuicUtils::IsCryptoStreamId(QuicTransportVersion version, // static QuicStreamId QuicUtils::GetHeadersStreamId(QuicTransportVersion version) { - if (version == QUIC_VERSION_99) { - // TODO(b/130659182) Turn this into a QUIC_BUG once we've fully removed - // the headers stream in those versions. - return GetQuicFlag(FLAGS_quic_headers_stream_id_in_v99); - } + DCHECK(!VersionUsesQpack(version)); return GetFirstBidirectionalStreamId(version, Perspective::IS_CLIENT); } @@ -488,6 +485,15 @@ QuicStreamId QuicUtils::GetFirstUnidirectionalStreamId( } // static +QuicConnectionId QuicUtils::CreateReplacementConnectionId( + QuicConnectionId connection_id) { + const uint64_t connection_id_hash = FNV1a_64_Hash( + QuicStringPiece(connection_id.data(), connection_id.length())); + return QuicConnectionId(reinterpret_cast<const char*>(&connection_id_hash), + sizeof(connection_id_hash)); +} + +// static QuicConnectionId QuicUtils::CreateRandomConnectionId() { return CreateRandomConnectionId(kQuicDefaultConnectionIdLength, QuicRandom::GetInstance()); @@ -508,18 +514,12 @@ QuicConnectionId QuicUtils::CreateRandomConnectionId( QuicConnectionId QuicUtils::CreateRandomConnectionId( uint8_t connection_id_length, QuicRandom* random) { - if (connection_id_length == 0) { - return EmptyQuicConnectionId(); - } - if (connection_id_length > kQuicMaxConnectionIdLength) { - QUIC_BUG << "Tried to CreateRandomConnectionId of invalid length " - << static_cast<int>(connection_id_length); - connection_id_length = kQuicMaxConnectionIdLength; + QuicConnectionId connection_id; + connection_id.set_length(connection_id_length); + if (connection_id.length() > 0) { + random->RandBytes(connection_id.mutable_data(), connection_id.length()); } - char connection_id_bytes[kQuicMaxConnectionIdLength]; - random->RandBytes(connection_id_bytes, connection_id_length); - return QuicConnectionId(static_cast<char*>(connection_id_bytes), - connection_id_length); + return connection_id; } // static @@ -543,27 +543,57 @@ QuicConnectionId QuicUtils::CreateZeroConnectionId( } // static -bool QuicUtils::IsConnectionIdValidForVersion(QuicConnectionId connection_id, - QuicTransportVersion version) { - if (VariableLengthConnectionIdAllowedForVersion(version)) { - return true; +bool QuicUtils::IsConnectionIdLengthValidForVersion( + size_t connection_id_length, + QuicTransportVersion transport_version) { + // No version of QUIC can support lengths that do not fit in an uint8_t. + if (connection_id_length > + static_cast<size_t>(std::numeric_limits<uint8_t>::max())) { + return false; + } + const uint8_t connection_id_length8 = + static_cast<uint8_t>(connection_id_length); + // Versions that do not support variable lengths only support length 8. + if (!VariableLengthConnectionIdAllowedForVersion(transport_version)) { + return connection_id_length8 == kQuicDefaultConnectionIdLength; + } + // Versions that do support variable length but do not have length-prefixed + // connection IDs use the 4-bit connection ID length encoding which can + // only encode values 0 and 4-18. + if (!VersionHasLengthPrefixedConnectionIds(transport_version)) { + return connection_id_length8 == 0 || + (connection_id_length8 >= 4 && + connection_id_length8 <= kQuicMaxConnectionId4BitLength); } - return connection_id.length() == kQuicDefaultConnectionIdLength; + return connection_id_length8 <= kQuicMaxConnectionIdWithLengthPrefixLength; +} + +// static +bool QuicUtils::IsConnectionIdValidForVersion( + QuicConnectionId connection_id, + QuicTransportVersion transport_version) { + return IsConnectionIdLengthValidForVersion(connection_id.length(), + transport_version); } QuicUint128 QuicUtils::GenerateStatelessResetToken( QuicConnectionId connection_id) { - uint64_t data_bytes[3] = {0, 0, 0}; - static_assert(sizeof(data_bytes) >= kQuicMaxConnectionIdLength, - "kQuicMaxConnectionIdLength changed"); - memcpy(data_bytes, connection_id.data(), connection_id.length()); - // This is designed so that the common case of 64bit connection IDs - // produces a stateless reset token that is equal to the connection ID - // interpreted as a 64bit unsigned integer, to facilitate debugging. - return MakeQuicUint128( - QuicEndian::NetToHost64(sizeof(uint64_t) ^ connection_id.length() ^ - data_bytes[1] ^ data_bytes[2]), - QuicEndian::NetToHost64(data_bytes[0])); + if (!GetQuicRestartFlag(quic_use_hashed_stateless_reset_tokens)) { + uint64_t data_bytes[3] = {0, 0, 0}; + static_assert(sizeof(data_bytes) >= kQuicMaxConnectionIdAllVersionsLength, + "kQuicMaxConnectionIdAllVersionsLength changed"); + memcpy(data_bytes, connection_id.data(), connection_id.length()); + // This is designed so that the common case of 64bit connection IDs + // produces a stateless reset token that is equal to the connection ID + // interpreted as a 64bit unsigned integer, to facilitate debugging. + return MakeQuicUint128( + QuicEndian::NetToHost64(sizeof(uint64_t) ^ connection_id.length() ^ + data_bytes[1] ^ data_bytes[2]), + QuicEndian::NetToHost64(data_bytes[0])); + } + QUIC_RESTART_FLAG_COUNT(quic_use_hashed_stateless_reset_tokens); + return FNV1a_128_Hash( + QuicStringPiece(connection_id.data(), connection_id.length())); } // Returns the maximum value that a stream count may have, taking into account diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_utils.h b/chromium/net/third_party/quiche/src/quic/core/quic_utils.h index 294f52fa846..de855a0cad8 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_utils.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_utils.h @@ -11,6 +11,7 @@ #include "net/third_party/quiche/src/quic/core/crypto/quic_random.h" #include "net/third_party/quiche/src/quic/core/frames/quic_frame.h" +#include "net/third_party/quiche/src/quic/core/quic_connection_id.h" #include "net/third_party/quiche/src/quic/core/quic_error_codes.h" #include "net/third_party/quiche/src/quic/core/quic_types.h" #include "net/third_party/quiche/src/quic/core/quic_versions.h" @@ -109,7 +110,7 @@ class QUIC_EXPORT_PRIVATE QuicUtils { TransmissionType retransmission_type); // Returns true if header with |first_byte| is considered as an IETF QUIC - // packet header. + // packet header. This only works on the server. static bool IsIetfPacketHeader(uint8_t first_byte); // Returns true if header with |first_byte| is considered as an IETF QUIC @@ -162,6 +163,12 @@ class QUIC_EXPORT_PRIVATE QuicUtils { QuicTransportVersion version, Perspective perspective); + // Generates a 64bit connection ID derived from the input connection ID. + // This is guaranteed to be deterministic (calling this method with two + // connection IDs that are equal is guaranteed to produce the same result). + static QuicConnectionId CreateReplacementConnectionId( + QuicConnectionId connection_id); + // Generates a random 64bit connection ID. static QuicConnectionId CreateRandomConnectionId(); @@ -181,9 +188,15 @@ class QUIC_EXPORT_PRIVATE QuicUtils { static bool VariableLengthConnectionIdAllowedForVersion( QuicTransportVersion version); + // Returns true if the connection ID length is valid for this QUIC version. + static bool IsConnectionIdLengthValidForVersion( + size_t connection_id_length, + QuicTransportVersion transport_version); + // Returns true if the connection ID is valid for this QUIC version. - static bool IsConnectionIdValidForVersion(QuicConnectionId connection_id, - QuicTransportVersion version); + static bool IsConnectionIdValidForVersion( + QuicConnectionId connection_id, + QuicTransportVersion transport_version); // Returns a connection ID suitable for QUIC use-cases that do not need the // connection ID for multiplexing. If the version allows variable lengths, diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_utils_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_utils_test.cc index d0ce7e80a6f..a2bc4030744 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_utils_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_utils_test.cc @@ -165,6 +165,47 @@ TEST_F(QuicUtilsTest, IsIetfPacketHeader) { EXPECT_FALSE(QuicUtils::IsIetfPacketShortHeader(first_byte)); } +TEST_F(QuicUtilsTest, ReplacementConnectionIdIsDeterministic) { + // Verify that two equal connection IDs get the same replacement. + QuicConnectionId connection_id64a = TestConnectionId(33); + QuicConnectionId connection_id64b = TestConnectionId(33); + EXPECT_EQ(connection_id64a, connection_id64b); + EXPECT_EQ(QuicUtils::CreateReplacementConnectionId(connection_id64a), + QuicUtils::CreateReplacementConnectionId(connection_id64b)); + QuicConnectionId connection_id72a = TestConnectionIdNineBytesLong(42); + QuicConnectionId connection_id72b = TestConnectionIdNineBytesLong(42); + EXPECT_EQ(connection_id72a, connection_id72b); + EXPECT_EQ(QuicUtils::CreateReplacementConnectionId(connection_id72a), + QuicUtils::CreateReplacementConnectionId(connection_id72b)); +} + +TEST_F(QuicUtilsTest, ReplacementConnectionIdLengthIsCorrect) { + // Verify that all lengths get replaced by kQuicDefaultConnectionIdLength. + const char connection_id_bytes[kQuicMaxConnectionIdAllVersionsLength] = {}; + for (uint8_t i = 0; i < sizeof(connection_id_bytes) - 1; ++i) { + QuicConnectionId connection_id(connection_id_bytes, i); + QuicConnectionId replacement_connection_id = + QuicUtils::CreateReplacementConnectionId(connection_id); + EXPECT_EQ(kQuicDefaultConnectionIdLength, + replacement_connection_id.length()); + } +} + +TEST_F(QuicUtilsTest, ReplacementConnectionIdHasEntropy) { + // Make sure all these test connection IDs have different replacements. + for (uint64_t i = 0; i < 256; ++i) { + QuicConnectionId connection_id_i = TestConnectionId(i); + EXPECT_NE(connection_id_i, + QuicUtils::CreateReplacementConnectionId(connection_id_i)); + for (uint64_t j = i + 1; j <= 256; ++j) { + QuicConnectionId connection_id_j = TestConnectionId(j); + EXPECT_NE(connection_id_i, connection_id_j); + EXPECT_NE(QuicUtils::CreateReplacementConnectionId(connection_id_i), + QuicUtils::CreateReplacementConnectionId(connection_id_j)); + } + } +} + TEST_F(QuicUtilsTest, RandomConnectionId) { MockRandom random(33); QuicConnectionId connection_id = QuicUtils::CreateRandomConnectionId(&random); @@ -225,7 +266,9 @@ TEST_F(QuicUtilsTest, StatelessResetToken) { QuicUint128 token2 = QuicUtils::GenerateStatelessResetToken(connection_id2); EXPECT_EQ(token1a, token1b); EXPECT_NE(token1a, token2); - EXPECT_EQ(token1a, MakeQuicUint128(0, 1)); + if (!GetQuicRestartFlag(quic_use_hashed_stateless_reset_tokens)) { + EXPECT_EQ(token1a, MakeQuicUint128(0, 1)); + } } } // namespace diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_version_manager.cc b/chromium/net/third_party/quiche/src/quic/core/quic_version_manager.cc index 49aee67764b..6b2f60eb6ee 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_version_manager.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_version_manager.cc @@ -15,9 +15,8 @@ namespace quic { QuicVersionManager::QuicVersionManager( ParsedQuicVersionVector supported_versions) : enable_version_99_(GetQuicReloadableFlag(quic_enable_version_99)), - enable_version_48_(GetQuicReloadableFlag(quic_enable_version_48)), + enable_version_48_(GetQuicReloadableFlag(quic_enable_version_48_2)), enable_version_47_(GetQuicReloadableFlag(quic_enable_version_47)), - disable_version_44_(GetQuicReloadableFlag(quic_disable_version_44)), disable_version_39_(GetQuicReloadableFlag(quic_disable_version_39)), enable_tls_(GetQuicFlag(FLAGS_quic_supports_tls_handshake)), allowed_supported_versions_(std::move(supported_versions)) { @@ -39,15 +38,13 @@ const ParsedQuicVersionVector& QuicVersionManager::GetSupportedVersions() { void QuicVersionManager::MaybeRefilterSupportedVersions() { if (enable_version_99_ != GetQuicReloadableFlag(quic_enable_version_99) || - enable_version_48_ != GetQuicReloadableFlag(quic_enable_version_48) || + enable_version_48_ != GetQuicReloadableFlag(quic_enable_version_48_2) || enable_version_47_ != GetQuicReloadableFlag(quic_enable_version_47) || - disable_version_44_ != GetQuicReloadableFlag(quic_disable_version_44) || disable_version_39_ != GetQuicReloadableFlag(quic_disable_version_39) || enable_tls_ != GetQuicFlag(FLAGS_quic_supports_tls_handshake)) { enable_version_99_ = GetQuicReloadableFlag(quic_enable_version_99); - enable_version_48_ = GetQuicReloadableFlag(quic_enable_version_48); + enable_version_48_ = GetQuicReloadableFlag(quic_enable_version_48_2); enable_version_47_ = GetQuicReloadableFlag(quic_enable_version_47); - disable_version_44_ = GetQuicReloadableFlag(quic_disable_version_44); disable_version_39_ = GetQuicReloadableFlag(quic_disable_version_39); enable_tls_ = GetQuicFlag(FLAGS_quic_supports_tls_handshake); RefilterSupportedVersions(); diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_version_manager.h b/chromium/net/third_party/quiche/src/quic/core/quic_version_manager.h index a6b7bcbdd93..a0c2d6f2716 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_version_manager.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_version_manager.h @@ -33,19 +33,17 @@ class QUIC_EXPORT_PRIVATE QuicVersionManager { // Refilters filtered_supported_versions_. virtual void RefilterSupportedVersions(); - const QuicTransportVersionVector& filtered_supported_versions() const { + const QuicTransportVersionVector& filtered_transport_versions() const { return filtered_transport_versions_; } private: // quic_enable_version_99 flag bool enable_version_99_; - // quic_enable_version_48 flag + // quic_enable_version_48_2 flag bool enable_version_48_; // quic_enable_version_47 flag bool enable_version_47_; - // quic_disable_version_44 flag - bool disable_version_44_; // quic_disable_version_39 flag bool disable_version_39_; // quic_supports_tls_handshake flag diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_version_manager_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_version_manager_test.cc index 886c0d3d4ef..7afd55d3033 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_version_manager_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_version_manager_test.cc @@ -16,12 +16,11 @@ namespace { class QuicVersionManagerTest : public QuicTest {}; TEST_F(QuicVersionManagerTest, QuicVersionManager) { - static_assert(QUIC_ARRAYSIZE(kSupportedTransportVersions) == 7u, + static_assert(QUIC_ARRAYSIZE(kSupportedTransportVersions) == 6u, "Supported versions out of sync"); SetQuicReloadableFlag(quic_enable_version_99, false); - SetQuicReloadableFlag(quic_enable_version_48, false); + SetQuicReloadableFlag(quic_enable_version_48_2, false); SetQuicReloadableFlag(quic_enable_version_47, false); - SetQuicReloadableFlag(quic_disable_version_44, true); SetQuicReloadableFlag(quic_disable_version_39, true); QuicVersionManager manager(AllSupportedVersions()); @@ -36,29 +35,23 @@ TEST_F(QuicVersionManagerTest, QuicVersionManager) { {QUIC_VERSION_46, QUIC_VERSION_43, QUIC_VERSION_39}), manager.GetSupportedTransportVersions()); - SetQuicReloadableFlag(quic_disable_version_44, false); - EXPECT_EQ(QuicTransportVersionVector({QUIC_VERSION_46, QUIC_VERSION_44, - QUIC_VERSION_43, QUIC_VERSION_39}), - manager.GetSupportedTransportVersions()); - SetQuicReloadableFlag(quic_enable_version_47, true); EXPECT_EQ(QuicTransportVersionVector({QUIC_VERSION_47, QUIC_VERSION_46, - QUIC_VERSION_44, QUIC_VERSION_43, - QUIC_VERSION_39}), + QUIC_VERSION_43, QUIC_VERSION_39}), manager.GetSupportedTransportVersions()); - SetQuicReloadableFlag(quic_enable_version_48, true); + SetQuicReloadableFlag(quic_enable_version_48_2, true); EXPECT_EQ(QuicTransportVersionVector({QUIC_VERSION_48, QUIC_VERSION_47, - QUIC_VERSION_46, QUIC_VERSION_44, - QUIC_VERSION_43, QUIC_VERSION_39}), + QUIC_VERSION_46, QUIC_VERSION_43, + QUIC_VERSION_39}), manager.GetSupportedTransportVersions()); SetQuicReloadableFlag(quic_enable_version_99, true); - EXPECT_EQ( - QuicTransportVersionVector( - {QUIC_VERSION_99, QUIC_VERSION_48, QUIC_VERSION_47, QUIC_VERSION_46, - QUIC_VERSION_44, QUIC_VERSION_43, QUIC_VERSION_39}), - manager.GetSupportedTransportVersions()); + SetQuicReloadableFlag(quic_use_parse_public_header, true); + EXPECT_EQ(QuicTransportVersionVector({QUIC_VERSION_99, QUIC_VERSION_48, + QUIC_VERSION_47, QUIC_VERSION_46, + QUIC_VERSION_43, QUIC_VERSION_39}), + manager.GetSupportedTransportVersions()); // Ensure that all versions are now supported. EXPECT_EQ(FilterSupportedTransportVersions(AllSupportedTransportVersions()), diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_versions.cc b/chromium/net/third_party/quiche/src/quic/core/quic_versions.cc index 950dd1d1cd0..8281b688198 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_versions.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_versions.cc @@ -72,23 +72,21 @@ bool ParsedQuicVersion::SendsVariableLengthPacketNumberInLongHeader() const { } bool ParsedQuicVersion::SupportsClientConnectionIds() const { - if (!GetQuicRestartFlag(quic_do_not_override_connection_id)) { - // Do not enable this feature in a production version until this flag has - // been deprecated. - return false; - } return transport_version >= QUIC_VERSION_99; } -bool ParsedQuicVersion::DoesNotHaveHeadersStream() const { - return VersionLacksHeadersStream(transport_version); +bool ParsedQuicVersion::HasLengthPrefixedConnectionIds() const { + return VersionHasLengthPrefixedConnectionIds(transport_version); } -bool VersionLacksHeadersStream(QuicTransportVersion transport_version) { - if (GetQuicFlag(FLAGS_quic_headers_stream_id_in_v99) == 0) { - return false; - } - return transport_version == QUIC_VERSION_99; +bool ParsedQuicVersion::SupportsAntiAmplificationLimit() const { + return transport_version == QUIC_VERSION_99 && + handshake_protocol == PROTOCOL_TLS1_3; +} + +bool VersionHasLengthPrefixedConnectionIds( + QuicTransportVersion transport_version) { + return transport_version >= QUIC_VERSION_99; } std::ostream& operator<<(std::ostream& os, const ParsedQuicVersion& version) { @@ -115,8 +113,6 @@ QuicVersionLabel CreateQuicVersionLabel(ParsedQuicVersion parsed_version) { return MakeVersionLabel(proto, '0', '3', '9'); case QUIC_VERSION_43: return MakeVersionLabel(proto, '0', '4', '3'); - case QUIC_VERSION_44: - return MakeVersionLabel(proto, '0', '4', '4'); case QUIC_VERSION_46: return MakeVersionLabel(proto, '0', '4', '6'); case QUIC_VERSION_47: @@ -124,10 +120,8 @@ QuicVersionLabel CreateQuicVersionLabel(ParsedQuicVersion parsed_version) { case QUIC_VERSION_48: return MakeVersionLabel(proto, '0', '4', '8'); case QUIC_VERSION_99: - if (parsed_version.handshake_protocol == PROTOCOL_TLS1_3 && - GetQuicFlag(FLAGS_quic_ietf_draft_version) != 0) { - return MakeVersionLabel(0xff, 0x00, 0x00, - GetQuicFlag(FLAGS_quic_ietf_draft_version)); + if (parsed_version.handshake_protocol == PROTOCOL_TLS1_3) { + return MakeVersionLabel(0xff, 0x00, 0x00, kQuicIetfDraftVersion); } return MakeVersionLabel(proto, '0', '9', '9'); case QUIC_VERSION_RESERVED_FOR_NEGOTIATION: @@ -262,21 +256,18 @@ ParsedQuicVersionVector FilterSupportedVersions( continue; } if (version.transport_version == QUIC_VERSION_99) { - if (GetQuicReloadableFlag(quic_enable_version_99)) { + if (GetQuicReloadableFlag(quic_enable_version_99) && + GetQuicReloadableFlag(quic_use_parse_public_header)) { filtered_versions.push_back(version); } } else if (version.transport_version == QUIC_VERSION_48) { - if (GetQuicReloadableFlag(quic_enable_version_48)) { + if (GetQuicReloadableFlag(quic_enable_version_48_2)) { filtered_versions.push_back(version); } } else if (version.transport_version == QUIC_VERSION_47) { if (GetQuicReloadableFlag(quic_enable_version_47)) { filtered_versions.push_back(version); } - } else if (version.transport_version == QUIC_VERSION_44) { - if (!GetQuicReloadableFlag(quic_disable_version_44)) { - filtered_versions.push_back(version); - } } else if (version.transport_version == QUIC_VERSION_39) { if (!GetQuicReloadableFlag(quic_disable_version_39)) { filtered_versions.push_back(version); @@ -371,7 +362,6 @@ std::string QuicVersionToString(QuicTransportVersion transport_version) { switch (transport_version) { RETURN_STRING_LITERAL(QUIC_VERSION_39); RETURN_STRING_LITERAL(QUIC_VERSION_43); - RETURN_STRING_LITERAL(QUIC_VERSION_44); RETURN_STRING_LITERAL(QUIC_VERSION_46); RETURN_STRING_LITERAL(QUIC_VERSION_47); RETURN_STRING_LITERAL(QUIC_VERSION_48); @@ -418,6 +408,34 @@ std::string ParsedQuicVersionVectorToString( return result; } +bool QuicVersionLabelUses4BitConnectionIdLength( + QuicVersionLabel version_label) { + // As we deprecate old versions, we still need the ability to send valid + // version negotiation packets for those versions. This function keeps track + // of the versions that ever supported the 4bit connection ID length encoding + // that we know about. Google QUIC 43 and earlier used a different encoding, + // and Google QUIC 49 will start using the new length prefixed encoding. + // Similarly, only IETF drafts 11 to 21 used this encoding. + + // Check Q044, Q045, Q046, Q047 and Q048. + for (uint8_t c = '4'; c <= '8'; ++c) { + if (version_label == MakeVersionLabel('Q', '0', '4', c)) { + return true; + } + } + // Check T048. + if (version_label == MakeVersionLabel('T', '0', '4', '8')) { + return true; + } + // Check IETF draft versions in [11,21]. + for (uint8_t draft_number = 11; draft_number <= 21; ++draft_number) { + if (version_label == MakeVersionLabel(0xff, 0x00, 0x00, draft_number)) { + return true; + } + } + return false; +} + ParsedQuicVersion UnsupportedQuicVersion() { return ParsedQuicVersion(PROTOCOL_UNSUPPORTED, QUIC_VERSION_UNSUPPORTED); } @@ -429,46 +447,32 @@ ParsedQuicVersion QuicVersionReservedForNegotiation() { std::string AlpnForVersion(ParsedQuicVersion parsed_version) { if (parsed_version.handshake_protocol == PROTOCOL_TLS1_3 && - parsed_version.transport_version == QUIC_VERSION_99 && - GetQuicFlag(FLAGS_quic_ietf_draft_version) != 0) { - return "h3-" + QuicTextUtils::Uint64ToString( - GetQuicFlag(FLAGS_quic_ietf_draft_version)); + parsed_version.transport_version == QUIC_VERSION_99) { + return "h3-" + QuicTextUtils::Uint64ToString(kQuicIetfDraftVersion); } - return "h3-google-" + ParsedQuicVersionToString(parsed_version); + return "h3-" + ParsedQuicVersionToString(parsed_version); } -void QuicVersionInitializeSupportForIetfDraft(int32_t draft_version) { - if (draft_version < 0 || draft_version >= 256) { - QUIC_LOG(FATAL) << "Invalid IETF draft version " << draft_version; - return; - } - - SetQuicFlag(FLAGS_quic_ietf_draft_version, draft_version); - - if (draft_version == 0) { - return; - } - +void QuicVersionInitializeSupportForIetfDraft() { // Enable necessary flags. SetQuicFlag(FLAGS_quic_supports_tls_handshake, true); - SetQuicFlag(FLAGS_quic_headers_stream_id_in_v99, 60); SetQuicReloadableFlag(quic_simplify_stop_waiting, true); - SetQuicRestartFlag(quic_do_not_override_connection_id, true); - SetQuicRestartFlag(quic_use_allocated_connection_ids, true); + SetQuicReloadableFlag(quic_use_parse_public_header, true); SetQuicRestartFlag(quic_dispatcher_hands_chlo_extractor_one_version, true); + SetQuicRestartFlag(quic_use_hashed_stateless_reset_tokens, true); } void QuicEnableVersion(ParsedQuicVersion parsed_version) { if (parsed_version.handshake_protocol == PROTOCOL_TLS1_3) { SetQuicFlag(FLAGS_quic_supports_tls_handshake, true); } - static_assert(QUIC_ARRAYSIZE(kSupportedTransportVersions) == 7u, + static_assert(QUIC_ARRAYSIZE(kSupportedTransportVersions) == 6u, "Supported versions out of sync"); if (parsed_version.transport_version == QUIC_VERSION_99) { SetQuicReloadableFlag(quic_enable_version_99, true); } if (parsed_version.transport_version == QUIC_VERSION_48) { - SetQuicReloadableFlag(quic_enable_version_48, true); + SetQuicReloadableFlag(quic_enable_version_48_2, true); } if (parsed_version.transport_version == QUIC_VERSION_47) { SetQuicReloadableFlag(quic_enable_version_47, true); diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_versions.h b/chromium/net/third_party/quiche/src/quic/core/quic_versions.h index 72bb32ca4ba..5b2a6ed35bd 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_versions.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_versions.h @@ -97,7 +97,7 @@ enum QuicTransportVersion { QUIC_VERSION_43 = 43, // PRIORITY frames are sent by client and accepted by // server. - QUIC_VERSION_44 = 44, // Use IETF header format. + // Version 44 used IETF header format from draft-ietf-quic-invariants-05. // Version 45 added MESSAGE frame. @@ -116,6 +116,9 @@ enum QuicTransportVersion { QUIC_VERSION_RESERVED_FOR_NEGOTIATION = 999, }; +// IETF draft version most closely approximated by TLS + v99. +static const int kQuicIetfDraftVersion = 22; + // The crypto handshake protocols that can be used with QUIC. enum HandshakeProtocol { PROTOCOL_UNSUPPORTED, @@ -173,8 +176,15 @@ struct QUIC_EXPORT_PRIVATE ParsedQuicVersion { // Returns whether this version supports client connection ID. bool SupportsClientConnectionIds() const; - // Returns whether this version does not have the Google QUIC headers stream. - bool DoesNotHaveHeadersStream() const; + // Returns whether this version supports long header 8-bit encoded + // connection ID lengths as described in draft-ietf-quic-invariants-06 and + // draft-ietf-quic-transport-22. + bool HasLengthPrefixedConnectionIds() const; + + // Returns whether this version supports IETF style anti-amplification limit, + // i.e., server will send no more than FLAGS_quic_anti_amplification_factor + // times received bytes until address can be validated. + bool SupportsAntiAmplificationLimit() const; }; QUIC_EXPORT_PRIVATE ParsedQuicVersion UnsupportedQuicVersion(); @@ -198,8 +208,8 @@ using QuicVersionLabelVector = std::vector<QuicVersionLabel>; // // See go/new-quic-version for more details on how to roll out new versions. static const QuicTransportVersion kSupportedTransportVersions[] = { - QUIC_VERSION_99, QUIC_VERSION_48, QUIC_VERSION_47, QUIC_VERSION_46, - QUIC_VERSION_44, QUIC_VERSION_43, QUIC_VERSION_39, + QUIC_VERSION_99, QUIC_VERSION_48, QUIC_VERSION_47, + QUIC_VERSION_46, QUIC_VERSION_43, QUIC_VERSION_39, }; // This vector contains all crypto handshake protocols that are supported. @@ -355,7 +365,7 @@ QUIC_EXPORT_PRIVATE inline bool VersionHasIetfInvariantHeader( // Returns true if |transport_version| supports MESSAGE frames. QUIC_EXPORT_PRIVATE inline bool VersionSupportsMessageFrames( QuicTransportVersion transport_version) { - return transport_version > QUIC_VERSION_44; + return transport_version >= QUIC_VERSION_46; } // Returns true if QuicSpdyStream encodes body using HTTP/3 specification and @@ -375,19 +385,14 @@ QUIC_EXPORT_PRIVATE inline bool VersionHasStreamType( // * QuicSpdySession instantiates a QPACK encoder and decoder; // * HEADERS frames (containing headers or trailers) are sent on // request/response streams, compressed with QPACK; -// * trailers must not contain :final-offset key. +// * trailers must not contain :final-offset key, +// * PUSH_PROMISE and PRIORITY frames are sent on the request stream, +// * there is no headers stream. // If false: // * HEADERS frames (containing headers or trailers) are sent on the headers // stream, compressed with HPACK; -// * trailers must contain :final-offset key. -// -// TODO(123528590): Implement the following features and gate them on this -// function as well, optionally renaming this function as appropriate: -// * send PUSH_PROMISE frames on the request/response stream instead of the -// headers stream; -// * send PRIORITY frames on the request/response stream instead of the headers -// stream; -// * do not instantiate the headers stream object. +// * trailers must contain :final-offset key, +// * PUSH_PROMISE and PRIORITY frames are sent on the headers stream. QUIC_EXPORT_PRIVATE inline bool VersionUsesQpack( QuicTransportVersion transport_version) { const bool uses_qpack = (transport_version == QUIC_VERSION_99); @@ -414,11 +419,6 @@ QUIC_EXPORT_PRIVATE inline bool QuicVersionUsesCryptoFrames( return transport_version >= QUIC_VERSION_48; } -// Returns whether |transport_version| does not have the -// Google QUIC headers stream. -QUIC_EXPORT_PRIVATE bool VersionLacksHeadersStream( - QuicTransportVersion transport_version); - // Returns whether |transport_version| makes use of IETF QUIC // frames or not. QUIC_EXPORT_PRIVATE inline bool VersionHasIetfQuicFrames( @@ -426,14 +426,25 @@ QUIC_EXPORT_PRIVATE inline bool VersionHasIetfQuicFrames( return transport_version >= QUIC_VERSION_99; } +// Returns whether this version supports long header 8-bit encoded +// connection ID lengths as described in draft-ietf-quic-invariants-06 and +// draft-ietf-quic-transport-22. +QUIC_EXPORT_PRIVATE bool VersionHasLengthPrefixedConnectionIds( + QuicTransportVersion transport_version); + +// Returns whether this version label supports long header 4-bit encoded +// connection ID lengths as described in draft-ietf-quic-invariants-05 and +// draft-ietf-quic-transport-21. +QUIC_EXPORT_PRIVATE bool QuicVersionLabelUses4BitConnectionIdLength( + QuicVersionLabel version_label); + // Returns the ALPN string to use in TLS for this version of QUIC. QUIC_EXPORT_PRIVATE std::string AlpnForVersion( ParsedQuicVersion parsed_version); -// Initializes support for the provided IETF draft version by setting flags -// and the version label. -QUIC_EXPORT_PRIVATE void QuicVersionInitializeSupportForIetfDraft( - int32_t draft_version); +// Initializes support for the provided IETF draft version by setting the +// correct flags. +QUIC_EXPORT_PRIVATE void QuicVersionInitializeSupportForIetfDraft(); // Enables the flags required to support this version of QUIC. QUIC_EXPORT_PRIVATE void QuicEnableVersion(ParsedQuicVersion parsed_version); diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_versions_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_versions_test.cc index 016ae5f5ca6..f5b0dd2a68a 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_versions_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_versions_test.cc @@ -116,8 +116,6 @@ TEST_F(QuicVersionsTest, ParseQuicVersionLabel) { ParseQuicVersionLabel(MakeVersionLabel('Q', '0', '3', '9'))); EXPECT_EQ(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_43), ParseQuicVersionLabel(MakeVersionLabel('Q', '0', '4', '3'))); - EXPECT_EQ(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_44), - ParseQuicVersionLabel(MakeVersionLabel('Q', '0', '4', '4'))); EXPECT_EQ(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_46), ParseQuicVersionLabel(MakeVersionLabel('Q', '0', '4', '6'))); EXPECT_EQ(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_47), @@ -130,8 +128,6 @@ TEST_F(QuicVersionsTest, ParseQuicVersionLabel) { ParseQuicVersionLabel(MakeVersionLabel('T', '0', '3', '9'))); EXPECT_EQ(ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_43), ParseQuicVersionLabel(MakeVersionLabel('T', '0', '4', '3'))); - EXPECT_EQ(ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_44), - ParseQuicVersionLabel(MakeVersionLabel('T', '0', '4', '4'))); EXPECT_EQ(ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_46), ParseQuicVersionLabel(MakeVersionLabel('T', '0', '4', '6'))); EXPECT_EQ(ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_47), @@ -145,8 +141,6 @@ TEST_F(QuicVersionsTest, ParseQuicVersionString) { ParseQuicVersionString("Q039")); EXPECT_EQ(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_43), ParseQuicVersionString("Q043")); - EXPECT_EQ(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_44), - ParseQuicVersionString("Q044")); EXPECT_EQ(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_46), ParseQuicVersionString("Q046")); EXPECT_EQ(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_47), @@ -164,8 +158,6 @@ TEST_F(QuicVersionsTest, ParseQuicVersionString) { ParseQuicVersionString("T039")); EXPECT_EQ(ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_43), ParseQuicVersionString("T043")); - EXPECT_EQ(ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_44), - ParseQuicVersionString("T044")); EXPECT_EQ(ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_46), ParseQuicVersionString("T046")); EXPECT_EQ(ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_47), @@ -181,9 +173,6 @@ TEST_F(QuicVersionsTest, CreateQuicVersionLabel) { EXPECT_EQ(MakeVersionLabel('Q', '0', '4', '3'), CreateQuicVersionLabel( ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_43))); - EXPECT_EQ(MakeVersionLabel('Q', '0', '4', '4'), - CreateQuicVersionLabel( - ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_44))); EXPECT_EQ(MakeVersionLabel('Q', '0', '4', '6'), CreateQuicVersionLabel( ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_46))); @@ -201,9 +190,6 @@ TEST_F(QuicVersionsTest, CreateQuicVersionLabel) { EXPECT_EQ(MakeVersionLabel('T', '0', '4', '3'), CreateQuicVersionLabel( ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_43))); - EXPECT_EQ(MakeVersionLabel('T', '0', '4', '4'), - CreateQuicVersionLabel( - ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_44))); EXPECT_EQ(MakeVersionLabel('T', '0', '4', '6'), CreateQuicVersionLabel( ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_46))); @@ -308,17 +294,17 @@ TEST_F(QuicVersionsTest, AllSupportedTransportVersions) { TEST_F(QuicVersionsTest, FilterSupportedTransportVersionsAllVersions) { QuicTransportVersionVector all_versions = AllSupportedTransportVersions(); SetQuicReloadableFlag(quic_disable_version_39, false); - SetQuicReloadableFlag(quic_disable_version_44, false); SetQuicReloadableFlag(quic_enable_version_47, true); - SetQuicReloadableFlag(quic_enable_version_48, true); + SetQuicReloadableFlag(quic_enable_version_48_2, true); SetQuicReloadableFlag(quic_enable_version_99, true); + SetQuicReloadableFlag(quic_use_parse_public_header, true); ParsedQuicVersionVector parsed_versions; for (QuicTransportVersion version : all_versions) { parsed_versions.push_back(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, version)); } QuicTransportVersionVector expected_versions = { - QUIC_VERSION_99, QUIC_VERSION_48, QUIC_VERSION_47, QUIC_VERSION_46, - QUIC_VERSION_44, QUIC_VERSION_43, QUIC_VERSION_39}; + QUIC_VERSION_99, QUIC_VERSION_48, QUIC_VERSION_47, + QUIC_VERSION_46, QUIC_VERSION_43, QUIC_VERSION_39}; ParsedQuicVersionVector expected_parsed_versions; for (QuicTransportVersion version : expected_versions) { expected_parsed_versions.push_back( @@ -332,17 +318,16 @@ TEST_F(QuicVersionsTest, FilterSupportedTransportVersionsAllVersions) { TEST_F(QuicVersionsTest, FilterSupportedTransportVersionsNo99) { QuicTransportVersionVector all_versions = AllSupportedTransportVersions(); SetQuicReloadableFlag(quic_disable_version_39, false); - SetQuicReloadableFlag(quic_disable_version_44, false); SetQuicReloadableFlag(quic_enable_version_47, true); - SetQuicReloadableFlag(quic_enable_version_48, true); + SetQuicReloadableFlag(quic_enable_version_48_2, true); SetQuicReloadableFlag(quic_enable_version_99, false); ParsedQuicVersionVector parsed_versions; for (QuicTransportVersion version : all_versions) { parsed_versions.push_back(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, version)); } QuicTransportVersionVector expected_versions = { - QUIC_VERSION_48, QUIC_VERSION_47, QUIC_VERSION_46, - QUIC_VERSION_44, QUIC_VERSION_43, QUIC_VERSION_39}; + QUIC_VERSION_48, QUIC_VERSION_47, QUIC_VERSION_46, QUIC_VERSION_43, + QUIC_VERSION_39}; ParsedQuicVersionVector expected_parsed_versions; for (QuicTransportVersion version : expected_versions) { expected_parsed_versions.push_back( @@ -356,17 +341,15 @@ TEST_F(QuicVersionsTest, FilterSupportedTransportVersionsNo99) { TEST_F(QuicVersionsTest, FilterSupportedTransportVersionsNo48) { QuicTransportVersionVector all_versions = AllSupportedTransportVersions(); SetQuicReloadableFlag(quic_disable_version_39, false); - SetQuicReloadableFlag(quic_disable_version_44, false); SetQuicReloadableFlag(quic_enable_version_47, true); - SetQuicReloadableFlag(quic_enable_version_48, false); + SetQuicReloadableFlag(quic_enable_version_48_2, false); SetQuicReloadableFlag(quic_enable_version_99, false); ParsedQuicVersionVector parsed_versions; for (QuicTransportVersion version : all_versions) { parsed_versions.push_back(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, version)); } QuicTransportVersionVector expected_versions = { - QUIC_VERSION_47, QUIC_VERSION_46, QUIC_VERSION_44, QUIC_VERSION_43, - QUIC_VERSION_39}; + QUIC_VERSION_47, QUIC_VERSION_46, QUIC_VERSION_43, QUIC_VERSION_39}; ParsedQuicVersionVector expected_parsed_versions; for (QuicTransportVersion version : expected_versions) { expected_parsed_versions.push_back( @@ -380,32 +363,8 @@ TEST_F(QuicVersionsTest, FilterSupportedTransportVersionsNo48) { TEST_F(QuicVersionsTest, FilterSupportedTransportVersionsNo47) { QuicTransportVersionVector all_versions = AllSupportedTransportVersions(); SetQuicReloadableFlag(quic_disable_version_39, false); - SetQuicReloadableFlag(quic_disable_version_44, false); SetQuicReloadableFlag(quic_enable_version_47, false); - SetQuicReloadableFlag(quic_enable_version_48, false); - SetQuicReloadableFlag(quic_enable_version_99, false); - ParsedQuicVersionVector parsed_versions; - for (QuicTransportVersion version : all_versions) { - parsed_versions.push_back(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, version)); - } - QuicTransportVersionVector expected_versions = { - QUIC_VERSION_46, QUIC_VERSION_44, QUIC_VERSION_43, QUIC_VERSION_39}; - ParsedQuicVersionVector expected_parsed_versions; - for (QuicTransportVersion version : expected_versions) { - expected_parsed_versions.push_back( - ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, version)); - } - - ASSERT_EQ(expected_versions, FilterSupportedTransportVersions(all_versions)); - ASSERT_EQ(expected_parsed_versions, FilterSupportedVersions(parsed_versions)); -} - -TEST_F(QuicVersionsTest, FilterSupportedTransportVersionsNo44) { - QuicTransportVersionVector all_versions = AllSupportedTransportVersions(); - SetQuicReloadableFlag(quic_disable_version_39, false); - SetQuicReloadableFlag(quic_disable_version_44, true); - SetQuicReloadableFlag(quic_enable_version_47, false); - SetQuicReloadableFlag(quic_enable_version_48, false); + SetQuicReloadableFlag(quic_enable_version_48_2, false); SetQuicReloadableFlag(quic_enable_version_99, false); ParsedQuicVersionVector parsed_versions; for (QuicTransportVersion version : all_versions) { @@ -426,16 +385,15 @@ TEST_F(QuicVersionsTest, FilterSupportedTransportVersionsNo44) { TEST_F(QuicVersionsTest, FilterSupportedTransportVersionsNo39) { QuicTransportVersionVector all_versions = AllSupportedTransportVersions(); SetQuicReloadableFlag(quic_disable_version_39, true); - SetQuicReloadableFlag(quic_disable_version_44, false); SetQuicReloadableFlag(quic_enable_version_47, false); - SetQuicReloadableFlag(quic_enable_version_48, false); + SetQuicReloadableFlag(quic_enable_version_48_2, false); SetQuicReloadableFlag(quic_enable_version_99, false); ParsedQuicVersionVector parsed_versions; for (QuicTransportVersion version : all_versions) { parsed_versions.push_back(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, version)); } - QuicTransportVersionVector expected_versions = { - QUIC_VERSION_46, QUIC_VERSION_44, QUIC_VERSION_43}; + QuicTransportVersionVector expected_versions = {QUIC_VERSION_46, + QUIC_VERSION_43}; ParsedQuicVersionVector expected_parsed_versions; for (QuicTransportVersion version : expected_versions) { expected_parsed_versions.push_back( @@ -485,11 +443,10 @@ TEST_F(QuicVersionsTest, ParsedVersionsToTransportVersions) { // 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, CheckVersionNumbersForTypos) { - static_assert(QUIC_ARRAYSIZE(kSupportedTransportVersions) == 7u, + static_assert(QUIC_ARRAYSIZE(kSupportedTransportVersions) == 6u, "Supported versions out of sync"); EXPECT_EQ(QUIC_VERSION_39, 39); EXPECT_EQ(QUIC_VERSION_43, 43); - EXPECT_EQ(QUIC_VERSION_44, 44); EXPECT_EQ(QUIC_VERSION_46, 46); EXPECT_EQ(QUIC_VERSION_47, 47); EXPECT_EQ(QUIC_VERSION_48, 48); @@ -508,36 +465,11 @@ TEST_F(QuicVersionsTest, AlpnForVersion) { ParsedQuicVersion parsed_version_t099 = ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_99); - EXPECT_EQ("h3-google-Q047", AlpnForVersion(parsed_version_q047)); - EXPECT_EQ("h3-google-T047", AlpnForVersion(parsed_version_t047)); - EXPECT_EQ("h3-google-Q048", AlpnForVersion(parsed_version_q048)); - EXPECT_EQ("h3-google-T048", AlpnForVersion(parsed_version_t048)); - EXPECT_EQ("h3-google-T099", AlpnForVersion(parsed_version_t099)); -} - -TEST_F(QuicVersionsTest, InitializeSupportForIetfDraft) { - ParsedQuicVersion parsed_version_t099 = - ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_99); - EXPECT_EQ(MakeVersionLabel('T', '0', '9', '9'), - CreateQuicVersionLabel(parsed_version_t099)); - EXPECT_EQ("h3-google-T099", AlpnForVersion(parsed_version_t099)); - - QuicVersionInitializeSupportForIetfDraft(0); - EXPECT_EQ(MakeVersionLabel('T', '0', '9', '9'), - CreateQuicVersionLabel(parsed_version_t099)); - EXPECT_EQ("h3-google-T099", AlpnForVersion(parsed_version_t099)); - EXPECT_FALSE(GetQuicFlag(FLAGS_quic_supports_tls_handshake)); - - QuicVersionInitializeSupportForIetfDraft(18); - EXPECT_TRUE(GetQuicFlag(FLAGS_quic_supports_tls_handshake)); - EXPECT_EQ(MakeVersionLabel(0xff, 0, 0, 18), - CreateQuicVersionLabel(parsed_version_t099)); - EXPECT_EQ("h3-18", AlpnForVersion(parsed_version_t099)); - - QuicVersionInitializeSupportForIetfDraft(0); - EXPECT_EQ(MakeVersionLabel('T', '0', '9', '9'), - CreateQuicVersionLabel(parsed_version_t099)); - EXPECT_EQ("h3-google-T099", AlpnForVersion(parsed_version_t099)); + EXPECT_EQ("h3-Q047", AlpnForVersion(parsed_version_q047)); + EXPECT_EQ("h3-T047", AlpnForVersion(parsed_version_t047)); + EXPECT_EQ("h3-Q048", AlpnForVersion(parsed_version_q048)); + EXPECT_EQ("h3-T048", AlpnForVersion(parsed_version_t048)); + EXPECT_EQ("h3-22", AlpnForVersion(parsed_version_t099)); } TEST_F(QuicVersionsTest, QuicEnableVersion) { @@ -554,9 +486,8 @@ TEST_F(QuicVersionsTest, QuicEnableVersion) { ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_99); SetQuicFlag(FLAGS_quic_supports_tls_handshake, false); SetQuicReloadableFlag(quic_disable_version_39, false); - SetQuicReloadableFlag(quic_disable_version_44, false); SetQuicReloadableFlag(quic_enable_version_47, false); - SetQuicReloadableFlag(quic_enable_version_48, false); + SetQuicReloadableFlag(quic_enable_version_48_2, false); SetQuicReloadableFlag(quic_enable_version_99, false); { @@ -564,7 +495,7 @@ TEST_F(QuicVersionsTest, QuicEnableVersion) { QuicEnableVersion(parsed_version_q047); EXPECT_FALSE(GetQuicFlag(FLAGS_quic_supports_tls_handshake)); EXPECT_TRUE(GetQuicReloadableFlag(quic_enable_version_47)); - EXPECT_FALSE(GetQuicReloadableFlag(quic_enable_version_48)); + EXPECT_FALSE(GetQuicReloadableFlag(quic_enable_version_48_2)); EXPECT_FALSE(GetQuicReloadableFlag(quic_enable_version_99)); } @@ -573,7 +504,7 @@ TEST_F(QuicVersionsTest, QuicEnableVersion) { QuicEnableVersion(parsed_version_t047); EXPECT_TRUE(GetQuicFlag(FLAGS_quic_supports_tls_handshake)); EXPECT_TRUE(GetQuicReloadableFlag(quic_enable_version_47)); - EXPECT_FALSE(GetQuicReloadableFlag(quic_enable_version_48)); + EXPECT_FALSE(GetQuicReloadableFlag(quic_enable_version_48_2)); EXPECT_FALSE(GetQuicReloadableFlag(quic_enable_version_99)); } @@ -582,7 +513,7 @@ TEST_F(QuicVersionsTest, QuicEnableVersion) { QuicEnableVersion(parsed_version_q048); EXPECT_FALSE(GetQuicFlag(FLAGS_quic_supports_tls_handshake)); EXPECT_FALSE(GetQuicReloadableFlag(quic_enable_version_47)); - EXPECT_TRUE(GetQuicReloadableFlag(quic_enable_version_48)); + EXPECT_TRUE(GetQuicReloadableFlag(quic_enable_version_48_2)); EXPECT_FALSE(GetQuicReloadableFlag(quic_enable_version_99)); } @@ -591,7 +522,7 @@ TEST_F(QuicVersionsTest, QuicEnableVersion) { QuicEnableVersion(parsed_version_t048); EXPECT_TRUE(GetQuicFlag(FLAGS_quic_supports_tls_handshake)); EXPECT_FALSE(GetQuicReloadableFlag(quic_enable_version_47)); - EXPECT_TRUE(GetQuicReloadableFlag(quic_enable_version_48)); + EXPECT_TRUE(GetQuicReloadableFlag(quic_enable_version_48_2)); EXPECT_FALSE(GetQuicReloadableFlag(quic_enable_version_99)); } @@ -600,7 +531,7 @@ TEST_F(QuicVersionsTest, QuicEnableVersion) { QuicEnableVersion(parsed_version_t099); EXPECT_TRUE(GetQuicFlag(FLAGS_quic_supports_tls_handshake)); EXPECT_FALSE(GetQuicReloadableFlag(quic_enable_version_47)); - EXPECT_FALSE(GetQuicReloadableFlag(quic_enable_version_48)); + EXPECT_FALSE(GetQuicReloadableFlag(quic_enable_version_48_2)); EXPECT_TRUE(GetQuicReloadableFlag(quic_enable_version_99)); } } diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_write_blocked_list.cc b/chromium/net/third_party/quiche/src/quic/core/quic_write_blocked_list.cc index d7fe3c0e554..8e4bcf118b7 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_write_blocked_list.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_write_blocked_list.cc @@ -10,10 +10,13 @@ namespace quic { QuicWriteBlockedList::QuicWriteBlockedList(QuicTransportVersion version) - : priority_write_scheduler_(QuicVersionUsesCryptoFrames(version) - ? std::numeric_limits<QuicStreamId>::max() - : 0), - last_priority_popped_(0) { + : priority_write_scheduler_( + QuicMakeUnique<spdy::PriorityWriteScheduler<QuicStreamId>>( + QuicVersionUsesCryptoFrames(version) + ? std::numeric_limits<QuicStreamId>::max() + : 0)), + last_priority_popped_(0), + scheduler_type_(spdy::WriteSchedulerType::SPDY) { memset(batch_write_stream_id_, 0, sizeof(batch_write_stream_id_)); memset(bytes_left_for_batch_write_, 0, sizeof(bytes_left_for_batch_write_)); } diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_write_blocked_list.h b/chromium/net/third_party/quiche/src/quic/core/quic_write_blocked_list.h index eca7c1a29ab..72ebc051ca1 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_write_blocked_list.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_write_blocked_list.h @@ -9,9 +9,14 @@ #include <cstdint> #include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" #include "net/third_party/quiche/src/quic/platform/api/quic_containers.h" #include "net/third_party/quiche/src/quic/platform/api/quic_export.h" #include "net/third_party/quiche/src/quic/platform/api/quic_map_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/spdy/core/fifo_write_scheduler.h" +#include "net/third_party/quiche/src/spdy/core/http2_priority_write_scheduler.h" +#include "net/third_party/quiche/src/spdy/core/lifo_write_scheduler.h" #include "net/third_party/quiche/src/spdy/core/priority_write_scheduler.h" namespace quic { @@ -21,7 +26,7 @@ namespace quic { // Crypto stream > Headers stream > Data streams by requested priority. class QUIC_EXPORT_PRIVATE QuicWriteBlockedList { private: - typedef spdy::PriorityWriteScheduler<QuicStreamId> QuicPriorityWriteScheduler; + typedef spdy::WriteScheduler<QuicStreamId> QuicPriorityWriteScheduler; public: explicit QuicWriteBlockedList(QuicTransportVersion version); @@ -30,7 +35,7 @@ class QUIC_EXPORT_PRIVATE QuicWriteBlockedList { ~QuicWriteBlockedList(); bool HasWriteBlockedDataStreams() const { - return priority_write_scheduler_.HasReadyStreams(); + return priority_write_scheduler_->HasReadyStreams(); } bool HasWriteBlockedSpecialStream() const { @@ -43,7 +48,7 @@ class QUIC_EXPORT_PRIVATE QuicWriteBlockedList { size_t NumBlockedStreams() const { return NumBlockedSpecialStreams() + - priority_write_scheduler_.NumReadyStreams(); + priority_write_scheduler_->NumReadyStreams(); } bool ShouldYield(QuicStreamId id) const { @@ -58,11 +63,54 @@ class QUIC_EXPORT_PRIVATE QuicWriteBlockedList { } } - return priority_write_scheduler_.ShouldYield(id); + return priority_write_scheduler_->ShouldYield(id); } - // Pops the highest priorty stream, special casing crypto and headers streams. - // Latches the most recently popped data stream for batch writing purposes. + // Switches write scheduler. This can only be called before any stream is + // registered. + bool SwitchWriteScheduler(spdy::WriteSchedulerType type, + QuicTransportVersion version) { + if (scheduler_type_ == type) { + return true; + } + if (priority_write_scheduler_->NumRegisteredStreams() != 0) { + QUIC_BUG << "Cannot switch scheduler with registered streams"; + return false; + } + QUIC_DVLOG(1) << "Switching to scheduler type: " + << spdy::WriteSchedulerTypeToString(type); + switch (type) { + case spdy::WriteSchedulerType::LIFO: + priority_write_scheduler_ = + QuicMakeUnique<spdy::LifoWriteScheduler<QuicStreamId>>(); + break; + case spdy::WriteSchedulerType::SPDY: + priority_write_scheduler_ = + QuicMakeUnique<spdy::PriorityWriteScheduler<QuicStreamId>>( + QuicVersionUsesCryptoFrames(version) + ? std::numeric_limits<QuicStreamId>::max() + : 0); + break; + case spdy::WriteSchedulerType::HTTP2: + priority_write_scheduler_ = + QuicMakeUnique<spdy::Http2PriorityWriteScheduler<QuicStreamId>>(); + break; + case spdy::WriteSchedulerType::FIFO: + priority_write_scheduler_ = + QuicMakeUnique<spdy::FifoWriteScheduler<QuicStreamId>>(); + break; + default: + QUIC_BUG << "Scheduler is not supported for type: " + << spdy::WriteSchedulerTypeToString(type); + return false; + } + scheduler_type_ = type; + return true; + } + + // Pops the highest priority stream, special casing crypto and headers + // streams. Latches the most recently popped data stream for batch writing + // purposes. QuicStreamId PopFront() { QuicStreamId static_stream_id; if (static_stream_collection_.UnblockFirstBlocked(&static_stream_id)) { @@ -70,12 +118,16 @@ class QUIC_EXPORT_PRIVATE QuicWriteBlockedList { } const auto id_and_precedence = - priority_write_scheduler_.PopNextReadyStreamAndPrecedence(); + priority_write_scheduler_->PopNextReadyStreamAndPrecedence(); const QuicStreamId id = std::get<0>(id_and_precedence); + if (scheduler_type_ != spdy::WriteSchedulerType::SPDY) { + // No batch writing logic for non-SPDY priority write scheduler. + return id; + } const spdy::SpdyPriority priority = std::get<1>(id_and_precedence).spdy3_priority(); - if (!priority_write_scheduler_.HasReadyStreams()) { + if (!priority_write_scheduler_->HasReadyStreams()) { // If no streams are blocked, don't bother latching. This stream will be // the first popped for its priority anyway. batch_write_stream_id_[priority] = 0; @@ -92,15 +144,15 @@ class QUIC_EXPORT_PRIVATE QuicWriteBlockedList { void RegisterStream(QuicStreamId stream_id, bool is_static_stream, - spdy::SpdyPriority priority) { - DCHECK(!priority_write_scheduler_.StreamRegistered(stream_id)); + const spdy::SpdyStreamPrecedence& precedence) { + DCHECK(!priority_write_scheduler_->StreamRegistered(stream_id) && + PrecedenceMatchesSchedulerType(precedence)); if (is_static_stream) { static_stream_collection_.Register(stream_id); return; } - priority_write_scheduler_.RegisterStream( - stream_id, spdy::SpdyStreamPrecedence(priority)); + priority_write_scheduler_->RegisterStream(stream_id, precedence); } void UnregisterStream(QuicStreamId stream_id, bool is_static) { @@ -108,17 +160,21 @@ class QUIC_EXPORT_PRIVATE QuicWriteBlockedList { static_stream_collection_.Unregister(stream_id); return; } - priority_write_scheduler_.UnregisterStream(stream_id); + priority_write_scheduler_->UnregisterStream(stream_id); } void UpdateStreamPriority(QuicStreamId stream_id, - spdy::SpdyPriority new_priority) { - DCHECK(!static_stream_collection_.IsRegistered(stream_id)); - priority_write_scheduler_.UpdateStreamPrecedence( - stream_id, spdy::SpdyStreamPrecedence(new_priority)); + const spdy::SpdyStreamPrecedence& new_precedence) { + DCHECK(!static_stream_collection_.IsRegistered(stream_id) && + PrecedenceMatchesSchedulerType(new_precedence)); + priority_write_scheduler_->UpdateStreamPrecedence(stream_id, + new_precedence); } void UpdateBytesForStream(QuicStreamId stream_id, size_t bytes) { + if (scheduler_type_ != spdy::WriteSchedulerType::SPDY) { + return; + } if (batch_write_stream_id_[last_priority_popped_] == stream_id) { // If this was the last data stream popped by PopFront, update the // bytes remaining in its batch write. @@ -137,9 +193,10 @@ class QUIC_EXPORT_PRIVATE QuicWriteBlockedList { } bool push_front = + scheduler_type_ == spdy::WriteSchedulerType::SPDY && stream_id == batch_write_stream_id_[last_priority_popped_] && bytes_left_for_batch_write_[last_priority_popped_] > 0; - priority_write_scheduler_.MarkStreamReady(stream_id, push_front); + priority_write_scheduler_->MarkStreamReady(stream_id, push_front); } // Returns true if stream with |stream_id| is write blocked. @@ -150,11 +207,29 @@ class QUIC_EXPORT_PRIVATE QuicWriteBlockedList { } } - return priority_write_scheduler_.IsStreamReady(stream_id); + return priority_write_scheduler_->IsStreamReady(stream_id); } private: - QuicPriorityWriteScheduler priority_write_scheduler_; + bool PrecedenceMatchesSchedulerType( + const spdy::SpdyStreamPrecedence& precedence) { + switch (scheduler_type_) { + case spdy::WriteSchedulerType::LIFO: + break; + case spdy::WriteSchedulerType::SPDY: + return precedence.is_spdy3_priority(); + case spdy::WriteSchedulerType::HTTP2: + return !precedence.is_spdy3_priority(); + case spdy::WriteSchedulerType::FIFO: + break; + default: + DCHECK(false); + return false; + } + return true; + } + + std::unique_ptr<QuicPriorityWriteScheduler> priority_write_scheduler_; // If performing batch writes, this will be the stream ID of the stream doing // batch writes for this priority level. We will allow this stream to write @@ -254,6 +329,8 @@ class QUIC_EXPORT_PRIVATE QuicWriteBlockedList { }; StaticStreamCollection static_stream_collection_; + + spdy::WriteSchedulerType scheduler_type_; }; } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_write_blocked_list_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_write_blocked_list_test.cc index 30bb08ef010..5d4b6397232 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_write_blocked_list_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_write_blocked_list_test.cc @@ -7,6 +7,7 @@ #include "net/third_party/quiche/src/quic/platform/api/quic_test.h" #include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" +using spdy::kHttp2DefaultStreamWeight; using spdy::kV3HighestPriority; using spdy::kV3LowestPriority; @@ -14,23 +15,70 @@ namespace quic { namespace test { namespace { -class QuicWriteBlockedListTest : public QuicTest { +const bool kExclusiveBit = true; + +class QuicWriteBlockedListTest : public QuicTestWithParam<bool> { public: QuicWriteBlockedListTest() - : write_blocked_list_(AllSupportedVersions()[0].transport_version) {} + : write_blocked_list_(AllSupportedVersions()[0].transport_version) { + if (GetParam()) { + write_blocked_list_.SwitchWriteScheduler( + spdy::WriteSchedulerType::HTTP2, + AllSupportedVersions()[0].transport_version); + } + } protected: QuicWriteBlockedList write_blocked_list_; }; -TEST_F(QuicWriteBlockedListTest, PriorityOrder) { - // Mark streams blocked in roughly reverse priority order, and - // verify that streams are sorted. - write_blocked_list_.RegisterStream(40, false, kV3LowestPriority); - write_blocked_list_.RegisterStream(23, false, kV3HighestPriority); - write_blocked_list_.RegisterStream(17, false, kV3HighestPriority); - write_blocked_list_.RegisterStream(1, true, kV3HighestPriority); - write_blocked_list_.RegisterStream(3, true, kV3HighestPriority); +INSTANTIATE_TEST_SUITE_P(Tests, QuicWriteBlockedListTest, testing::Bool()); + +TEST_P(QuicWriteBlockedListTest, PriorityOrder) { + if (GetParam()) { + /* + 0 + | + 23 + | + 17 + | + 40 + */ + write_blocked_list_.RegisterStream( + 17, false, + spdy::SpdyStreamPrecedence(0, kHttp2DefaultStreamWeight, + kExclusiveBit)); + write_blocked_list_.RegisterStream( + 40, false, + spdy::SpdyStreamPrecedence(17, kHttp2DefaultStreamWeight, + kExclusiveBit)); + write_blocked_list_.RegisterStream( + 23, false, + spdy::SpdyStreamPrecedence(0, kHttp2DefaultStreamWeight, + kExclusiveBit)); + write_blocked_list_.RegisterStream( + 1, true, + spdy::SpdyStreamPrecedence(0, kHttp2DefaultStreamWeight, + kExclusiveBit)); + write_blocked_list_.RegisterStream( + 3, true, + spdy::SpdyStreamPrecedence(0, kHttp2DefaultStreamWeight, + kExclusiveBit)); + } else { + // Mark streams blocked in roughly reverse priority order, and + // verify that streams are sorted. + write_blocked_list_.RegisterStream( + 40, false, spdy::SpdyStreamPrecedence(kV3LowestPriority)); + write_blocked_list_.RegisterStream( + 23, false, spdy::SpdyStreamPrecedence(kV3HighestPriority)); + write_blocked_list_.RegisterStream( + 17, false, spdy::SpdyStreamPrecedence(kV3HighestPriority)); + write_blocked_list_.RegisterStream( + 1, true, spdy::SpdyStreamPrecedence(kV3HighestPriority)); + write_blocked_list_.RegisterStream( + 3, true, spdy::SpdyStreamPrecedence(kV3HighestPriority)); + } write_blocked_list_.AddStream(40); EXPECT_TRUE(write_blocked_list_.IsStreamBlocked(40)); @@ -69,8 +117,16 @@ TEST_F(QuicWriteBlockedListTest, PriorityOrder) { EXPECT_FALSE(write_blocked_list_.HasWriteBlockedDataStreams()); } -TEST_F(QuicWriteBlockedListTest, CryptoStream) { - write_blocked_list_.RegisterStream(1, true, kV3HighestPriority); +TEST_P(QuicWriteBlockedListTest, CryptoStream) { + if (GetParam()) { + write_blocked_list_.RegisterStream( + 1, true, + spdy::SpdyStreamPrecedence(0, kHttp2DefaultStreamWeight, + kExclusiveBit)); + } else { + write_blocked_list_.RegisterStream( + 1, true, spdy::SpdyStreamPrecedence(kV3HighestPriority)); + } write_blocked_list_.AddStream(1); EXPECT_EQ(1u, write_blocked_list_.NumBlockedStreams()); @@ -80,8 +136,16 @@ TEST_F(QuicWriteBlockedListTest, CryptoStream) { EXPECT_FALSE(write_blocked_list_.HasWriteBlockedSpecialStream()); } -TEST_F(QuicWriteBlockedListTest, HeadersStream) { - write_blocked_list_.RegisterStream(3, true, kV3HighestPriority); +TEST_P(QuicWriteBlockedListTest, HeadersStream) { + if (GetParam()) { + write_blocked_list_.RegisterStream( + 3, true, + spdy::SpdyStreamPrecedence(0, kHttp2DefaultStreamWeight, + kExclusiveBit)); + } else { + write_blocked_list_.RegisterStream( + 3, true, spdy::SpdyStreamPrecedence(kV3HighestPriority)); + } write_blocked_list_.AddStream(3); EXPECT_EQ(1u, write_blocked_list_.NumBlockedStreams()); @@ -91,9 +155,22 @@ TEST_F(QuicWriteBlockedListTest, HeadersStream) { EXPECT_FALSE(write_blocked_list_.HasWriteBlockedSpecialStream()); } -TEST_F(QuicWriteBlockedListTest, VerifyHeadersStream) { - write_blocked_list_.RegisterStream(5, false, kV3HighestPriority); - write_blocked_list_.RegisterStream(3, true, kV3HighestPriority); +TEST_P(QuicWriteBlockedListTest, VerifyHeadersStream) { + if (GetParam()) { + write_blocked_list_.RegisterStream( + 5, false, + spdy::SpdyStreamPrecedence(0, kHttp2DefaultStreamWeight, + kExclusiveBit)); + write_blocked_list_.RegisterStream( + 3, true, + spdy::SpdyStreamPrecedence(0, kHttp2DefaultStreamWeight, + kExclusiveBit)); + } else { + write_blocked_list_.RegisterStream( + 5, false, spdy::SpdyStreamPrecedence(kV3HighestPriority)); + write_blocked_list_.RegisterStream( + 3, true, spdy::SpdyStreamPrecedence(kV3HighestPriority)); + } write_blocked_list_.AddStream(5); write_blocked_list_.AddStream(3); @@ -109,12 +186,20 @@ TEST_F(QuicWriteBlockedListTest, VerifyHeadersStream) { EXPECT_FALSE(write_blocked_list_.HasWriteBlockedDataStreams()); } -TEST_F(QuicWriteBlockedListTest, NoDuplicateEntries) { +TEST_P(QuicWriteBlockedListTest, NoDuplicateEntries) { // Test that QuicWriteBlockedList doesn't allow duplicate entries. // Try to add a stream to the write blocked list multiple times at the same // priority. const QuicStreamId kBlockedId = 3 + 2; - write_blocked_list_.RegisterStream(kBlockedId, false, kV3HighestPriority); + if (GetParam()) { + write_blocked_list_.RegisterStream( + kBlockedId, false, + spdy::SpdyStreamPrecedence(0, kHttp2DefaultStreamWeight, + kExclusiveBit)); + } else { + write_blocked_list_.RegisterStream( + kBlockedId, false, spdy::SpdyStreamPrecedence(kV3HighestPriority)); + } write_blocked_list_.AddStream(kBlockedId); write_blocked_list_.AddStream(kBlockedId); write_blocked_list_.AddStream(kBlockedId); @@ -129,13 +214,19 @@ TEST_F(QuicWriteBlockedListTest, NoDuplicateEntries) { EXPECT_FALSE(write_blocked_list_.HasWriteBlockedDataStreams()); } -TEST_F(QuicWriteBlockedListTest, BatchingWrites) { +TEST_P(QuicWriteBlockedListTest, BatchingWrites) { + if (GetParam()) { + return; + } const QuicStreamId id1 = 3 + 2; const QuicStreamId id2 = id1 + 2; const QuicStreamId id3 = id2 + 2; - write_blocked_list_.RegisterStream(id1, false, kV3LowestPriority); - write_blocked_list_.RegisterStream(id2, false, kV3LowestPriority); - write_blocked_list_.RegisterStream(id3, false, kV3HighestPriority); + write_blocked_list_.RegisterStream( + id1, false, spdy::SpdyStreamPrecedence(kV3LowestPriority)); + write_blocked_list_.RegisterStream( + id2, false, spdy::SpdyStreamPrecedence(kV3LowestPriority)); + write_blocked_list_.RegisterStream( + id3, false, spdy::SpdyStreamPrecedence(kV3HighestPriority)); write_blocked_list_.AddStream(id1); write_blocked_list_.AddStream(id2); @@ -179,14 +270,62 @@ TEST_F(QuicWriteBlockedListTest, BatchingWrites) { EXPECT_EQ(id1, write_blocked_list_.PopFront()); } -TEST_F(QuicWriteBlockedListTest, Ceding) { - write_blocked_list_.RegisterStream(15, false, kV3HighestPriority); - write_blocked_list_.RegisterStream(16, false, kV3HighestPriority); - write_blocked_list_.RegisterStream(5, false, 5); - write_blocked_list_.RegisterStream(4, false, 5); - write_blocked_list_.RegisterStream(7, false, 7); - write_blocked_list_.RegisterStream(1, true, kV3HighestPriority); - write_blocked_list_.RegisterStream(3, true, kV3HighestPriority); +TEST_P(QuicWriteBlockedListTest, Ceding) { + if (GetParam()) { + /* + 0 + | + 15 + | + 16 + | + 5 + | + 4 + | + 7 + */ + write_blocked_list_.RegisterStream( + 15, false, + spdy::SpdyStreamPrecedence(0, kHttp2DefaultStreamWeight, + kExclusiveBit)); + write_blocked_list_.RegisterStream( + 16, false, + spdy::SpdyStreamPrecedence(15, kHttp2DefaultStreamWeight, + kExclusiveBit)); + write_blocked_list_.RegisterStream( + 4, false, + spdy::SpdyStreamPrecedence(16, kHttp2DefaultStreamWeight, + kExclusiveBit)); + write_blocked_list_.RegisterStream( + 5, false, + spdy::SpdyStreamPrecedence(16, kHttp2DefaultStreamWeight, + kExclusiveBit)); + write_blocked_list_.RegisterStream( + 7, false, + spdy::SpdyStreamPrecedence(4, kHttp2DefaultStreamWeight, + kExclusiveBit)); + write_blocked_list_.RegisterStream( + 1, true, + spdy::SpdyStreamPrecedence(0, kHttp2DefaultStreamWeight, + kExclusiveBit)); + write_blocked_list_.RegisterStream( + 3, true, + spdy::SpdyStreamPrecedence(0, kHttp2DefaultStreamWeight, + kExclusiveBit)); + } else { + write_blocked_list_.RegisterStream( + 15, false, spdy::SpdyStreamPrecedence(kV3HighestPriority)); + write_blocked_list_.RegisterStream( + 16, false, spdy::SpdyStreamPrecedence(kV3HighestPriority)); + write_blocked_list_.RegisterStream(5, false, spdy::SpdyStreamPrecedence(5)); + write_blocked_list_.RegisterStream(4, false, spdy::SpdyStreamPrecedence(5)); + write_blocked_list_.RegisterStream(7, false, spdy::SpdyStreamPrecedence(7)); + write_blocked_list_.RegisterStream( + 1, true, spdy::SpdyStreamPrecedence(kV3HighestPriority)); + write_blocked_list_.RegisterStream( + 3, true, spdy::SpdyStreamPrecedence(kV3HighestPriority)); + } // When nothing is on the list, nothing yields. EXPECT_FALSE(write_blocked_list_.ShouldYield(5)); @@ -224,6 +363,66 @@ TEST_F(QuicWriteBlockedListTest, Ceding) { EXPECT_FALSE(write_blocked_list_.ShouldYield(1)); } +TEST_P(QuicWriteBlockedListTest, UpdateStreamPriority) { + if (!GetParam()) { + return; + } + /* + 0 + | + 5 + | + 7 + | + 9 + | + 11 + */ + write_blocked_list_.RegisterStream( + 5, false, + spdy::SpdyStreamPrecedence(0, kHttp2DefaultStreamWeight, kExclusiveBit)); + write_blocked_list_.RegisterStream( + 7, false, + spdy::SpdyStreamPrecedence(5, kHttp2DefaultStreamWeight, kExclusiveBit)); + write_blocked_list_.RegisterStream( + 9, false, + spdy::SpdyStreamPrecedence(7, kHttp2DefaultStreamWeight, kExclusiveBit)); + write_blocked_list_.RegisterStream( + 11, false, + spdy::SpdyStreamPrecedence(9, kHttp2DefaultStreamWeight, kExclusiveBit)); + + write_blocked_list_.AddStream(7); + EXPECT_FALSE(write_blocked_list_.ShouldYield(5)); + EXPECT_TRUE(write_blocked_list_.ShouldYield(9)); + EXPECT_TRUE(write_blocked_list_.ShouldYield(11)); + + // Update 9's priority. + if (GetParam()) { + /* + 0 + | + 5 + / \ + 7 9 + | + 11 + */ + write_blocked_list_.UpdateStreamPriority( + 9, spdy::SpdyStreamPrecedence(5, kHttp2DefaultStreamWeight, + kExclusiveBit)); + } else { + write_blocked_list_.UpdateStreamPriority(9, spdy::SpdyStreamPrecedence(1)); + } + EXPECT_FALSE(write_blocked_list_.ShouldYield(5)); + // Verify 9 now does not yield to 7. + EXPECT_FALSE(write_blocked_list_.ShouldYield(9)); + EXPECT_FALSE(write_blocked_list_.ShouldYield(11)); + + write_blocked_list_.AddStream(9); + // Verify 11 yield to 9. + EXPECT_TRUE(write_blocked_list_.ShouldYield(11)); +} + } // namespace } // namespace test } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/tls_client_handshaker.cc b/chromium/net/third_party/quiche/src/quic/core/tls_client_handshaker.cc index d6fc038639e..bb456027469 100644 --- a/chromium/net/third_party/quiche/src/quic/core/tls_client_handshaker.cc +++ b/chromium/net/third_party/quiche/src/quic/core/tls_client_handshaker.cc @@ -76,30 +76,15 @@ bool TlsClientHandshaker::CryptoConnect() { return false; } - std::string alpn_string = AlpnForVersion(session()->connection()->version()); - if (alpn_string.length() > std::numeric_limits<uint8_t>::max()) { - QUIC_BUG << "ALPN too long: '" << alpn_string << "'"; - CloseConnection(QUIC_HANDSHAKE_FAILED, "ALPN too long"); + if (!SetAlpn()) { + CloseConnection(QUIC_HANDSHAKE_FAILED, "Client failed to set ALPN"); return false; } - const uint8_t alpn_length = alpn_string.length(); - // SSL_set_alpn_protos expects a sequence of one-byte-length-prefixed strings - // so we copy alpn_string to a new buffer that has the length in alpn[0]. - uint8_t alpn[std::numeric_limits<uint8_t>::max() + 1]; - alpn[0] = alpn_length; - memcpy(reinterpret_cast<char*>(alpn + 1), alpn_string.data(), alpn_length); - if (SSL_set_alpn_protos(ssl(), alpn, - static_cast<unsigned>(alpn_length) + 1) != 0) { - QUIC_BUG << "Failed to set ALPN: '" << alpn_string << "'"; - CloseConnection(QUIC_HANDSHAKE_FAILED, "Failed to set ALPN"); - return false; - } - QUIC_DLOG(INFO) << "Client using ALPN: '" << alpn_string << "'"; // Set the Transport Parameters to send in the ClientHello if (!SetTransportParameters()) { CloseConnection(QUIC_HANDSHAKE_FAILED, - "Failed to set Transport Parameters"); + "Client failed to set Transport Parameters"); return false; } @@ -108,6 +93,46 @@ bool TlsClientHandshaker::CryptoConnect() { return session()->connection()->connected(); } +static bool IsValidAlpn(const std::string& alpn_string) { + return alpn_string.length() <= std::numeric_limits<uint8_t>::max(); +} + +bool TlsClientHandshaker::SetAlpn() { + std::vector<std::string> alpns = session()->GetAlpnsToOffer(); + if (alpns.empty()) { + if (allow_empty_alpn_for_tests_) { + return true; + } + + QUIC_BUG << "ALPN missing"; + return false; + } + if (!std::all_of(alpns.begin(), alpns.end(), IsValidAlpn)) { + QUIC_BUG << "ALPN too long"; + return false; + } + + // SSL_set_alpn_protos expects a sequence of one-byte-length-prefixed + // strings. + uint8_t alpn[1024]; + QuicDataWriter alpn_writer(sizeof(alpn), reinterpret_cast<char*>(alpn)); + bool success = true; + for (const std::string& alpn_string : alpns) { + success = success && alpn_writer.WriteUInt8(alpn_string.size()) && + alpn_writer.WriteStringPiece(alpn_string); + } + success = + success && (SSL_set_alpn_protos(ssl(), alpn, alpn_writer.length()) == 0); + if (!success) { + QUIC_BUG << "Failed to set ALPN: " + << QuicTextUtils::HexDump( + QuicStringPiece(alpn_writer.data(), alpn_writer.length())); + return false; + } + QUIC_DLOG(INFO) << "Client using ALPN: '" << alpns[0] << "'"; + return true; +} + bool TlsClientHandshaker::SetTransportParameters() { TransportParameters params; params.perspective = Perspective::IS_CLIENT; @@ -120,7 +145,8 @@ bool TlsClientHandshaker::SetTransportParameters() { params.google_quic_params->SetStringPiece(kUAID, user_agent_id_); std::vector<uint8_t> param_bytes; - return SerializeTransportParameters(params, ¶m_bytes) && + return SerializeTransportParameters(session()->connection()->version(), + params, ¶m_bytes) && SSL_set_quic_transport_params(ssl(), param_bytes.data(), param_bytes.size()) == 1; } @@ -132,8 +158,9 @@ bool TlsClientHandshaker::ProcessTransportParameters( size_t param_bytes_len; SSL_get_peer_quic_transport_params(ssl(), ¶m_bytes, ¶m_bytes_len); if (param_bytes_len == 0 || - !ParseTransportParameters(param_bytes, param_bytes_len, - Perspective::IS_SERVER, ¶ms)) { + !ParseTransportParameters(session()->connection()->version(), + Perspective::IS_SERVER, param_bytes, + param_bytes_len, ¶ms)) { *error_details = "Unable to parse Transport Parameters"; return false; } @@ -197,6 +224,11 @@ CryptoMessageParser* TlsClientHandshaker::crypto_message_parser() { return TlsHandshaker::crypto_message_parser(); } +size_t TlsClientHandshaker::BufferSizeLimitForLevel( + EncryptionLevel level) const { + return TlsHandshaker::BufferSizeLimitForLevel(level); +} + void TlsClientHandshaker::AdvanceHandshake() { if (state_ == STATE_CONNECTION_CLOSED) { QUIC_LOG(INFO) @@ -204,7 +236,8 @@ void TlsClientHandshaker::AdvanceHandshake() { return; } if (state_ == STATE_IDLE) { - CloseConnection(QUIC_HANDSHAKE_FAILED, "TLS handshake failed"); + CloseConnection(QUIC_HANDSHAKE_FAILED, + "Client observed TLS handshake idle failure"); return; } if (state_ == STATE_HANDSHAKE_COMPLETE) { @@ -234,7 +267,8 @@ void TlsClientHandshaker::AdvanceHandshake() { // TODO(nharper): Surface error details from the error queue when ssl_error // is SSL_ERROR_SSL. QUIC_LOG(WARNING) << "SSL_do_handshake failed; closing connection"; - CloseConnection(QUIC_HANDSHAKE_FAILED, "TLS handshake failed"); + CloseConnection(QUIC_HANDSHAKE_FAILED, + "Client observed TLS handshake failure"); } } @@ -259,29 +293,38 @@ void TlsClientHandshaker::FinishHandshake() { const uint8_t* alpn_data = nullptr; unsigned alpn_length = 0; SSL_get0_alpn_selected(ssl(), &alpn_data, &alpn_length); - // TODO(b/130164908) Act on ALPN. - if (alpn_length != 0) { - std::string received_alpn_string(reinterpret_cast<const char*>(alpn_data), - alpn_length); - std::string sent_alpn_string = - AlpnForVersion(session()->connection()->version()); - if (received_alpn_string != sent_alpn_string) { - QUIC_LOG(ERROR) << "Client: received mismatched ALPN '" - << received_alpn_string << "', expected '" - << sent_alpn_string << "'"; - CloseConnection(QUIC_HANDSHAKE_FAILED, "Mismatched ALPN"); - return; - } - QUIC_DLOG(INFO) << "Client: server selected ALPN: '" << received_alpn_string - << "'"; - } else { - QUIC_DLOG(INFO) << "Client: server did not select ALPN"; + + if (alpn_length == 0) { + QUIC_DLOG(ERROR) << "Client: server did not select ALPN"; + // TODO(b/130164908) this should send no_application_protocol + // instead of QUIC_HANDSHAKE_FAILED. + CloseConnection(QUIC_HANDSHAKE_FAILED, "Server did not select ALPN"); + return; + } + + std::string received_alpn_string(reinterpret_cast<const char*>(alpn_data), + alpn_length); + std::vector<std::string> offered_alpns = session()->GetAlpnsToOffer(); + if (std::find(offered_alpns.begin(), offered_alpns.end(), + received_alpn_string) == offered_alpns.end()) { + QUIC_LOG(ERROR) << "Client: received mismatched ALPN '" + << received_alpn_string; + // TODO(b/130164908) this should send no_application_protocol + // instead of QUIC_HANDSHAKE_FAILED. + CloseConnection(QUIC_HANDSHAKE_FAILED, "Client received mismatched ALPN"); + return; } + session()->OnAlpnSelected(received_alpn_string); + QUIC_DLOG(INFO) << "Client: server selected ALPN: '" << received_alpn_string + << "'"; session()->connection()->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); session()->NeuterUnencryptedData(); encryption_established_ = true; handshake_confirmed_ = true; + session()->OnCryptoHandshakeEvent(QuicSession::ENCRYPTION_ESTABLISHED); + session()->OnCryptoHandshakeEvent(QuicSession::HANDSHAKE_CONFIRMED); + session()->connection()->OnHandshakeComplete(); } enum ssl_verify_result_t TlsClientHandshaker::VerifyCert(uint8_t* out_alert) { diff --git a/chromium/net/third_party/quiche/src/quic/core/tls_client_handshaker.h b/chromium/net/third_party/quiche/src/quic/core/tls_client_handshaker.h index c22fbe9e8cc..f3e90ce9410 100644 --- a/chromium/net/third_party/quiche/src/quic/core/tls_client_handshaker.h +++ b/chromium/net/third_party/quiche/src/quic/core/tls_client_handshaker.h @@ -52,9 +52,14 @@ class QUIC_EXPORT_PRIVATE TlsClientHandshaker const QuicCryptoNegotiatedParameters& crypto_negotiated_params() const override; CryptoMessageParser* crypto_message_parser() override; + size_t BufferSizeLimitForLevel(EncryptionLevel level) const override; + + void AllowEmptyAlpnForTests() { allow_empty_alpn_for_tests_ = true; } protected: - TlsConnection* tls_connection() override { return &tls_connection_; } + const TlsConnection* tls_connection() const override { + return &tls_connection_; + } void AdvanceHandshake() override; void CloseConnection(QuicErrorCode error, @@ -92,6 +97,7 @@ class QUIC_EXPORT_PRIVATE TlsClientHandshaker STATE_CONNECTION_CLOSED, } state_ = STATE_IDLE; + bool SetAlpn(); bool SetTransportParameters(); bool ProcessTransportParameters(std::string* error_details); void FinishHandshake(); @@ -118,6 +124,8 @@ class QUIC_EXPORT_PRIVATE TlsClientHandshaker QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> crypto_negotiated_params_; + bool allow_empty_alpn_for_tests_ = false; + TlsClientConnection tls_connection_; }; diff --git a/chromium/net/third_party/quiche/src/quic/core/tls_handshaker.cc b/chromium/net/third_party/quiche/src/quic/core/tls_handshaker.cc index db50f5abf2e..e6e59fd7b5b 100644 --- a/chromium/net/third_party/quiche/src/quic/core/tls_handshaker.cc +++ b/chromium/net/third_party/quiche/src/quic/core/tls_handshaker.cc @@ -54,6 +54,11 @@ bool TlsHandshaker::ProcessInput(QuicStringPiece input, EncryptionLevel level) { return true; } +size_t TlsHandshaker::BufferSizeLimitForLevel(EncryptionLevel level) const { + return SSL_quic_max_handshake_flight_len( + ssl(), TlsConnection::BoringEncryptionLevel(level)); +} + const EVP_MD* TlsHandshaker::Prf() { return EVP_get_digestbynid( SSL_CIPHER_get_prf_nid(SSL_get_pending_cipher(ssl()))); diff --git a/chromium/net/third_party/quiche/src/quic/core/tls_handshaker.h b/chromium/net/third_party/quiche/src/quic/core/tls_handshaker.h index b4f16e8a3d1..7d5b9bcb75f 100644 --- a/chromium/net/third_party/quiche/src/quic/core/tls_handshaker.h +++ b/chromium/net/third_party/quiche/src/quic/core/tls_handshaker.h @@ -50,6 +50,7 @@ class QUIC_EXPORT_PRIVATE TlsHandshaker : public TlsConnection::Delegate, virtual const QuicCryptoNegotiatedParameters& crypto_negotiated_params() const = 0; virtual CryptoMessageParser* crypto_message_parser() { return this; } + size_t BufferSizeLimitForLevel(EncryptionLevel level) const; protected: virtual void AdvanceHandshake() = 0; @@ -65,9 +66,9 @@ class QUIC_EXPORT_PRIVATE TlsHandshaker : public TlsConnection::Delegate, std::unique_ptr<QuicDecrypter> CreateDecrypter( const std::vector<uint8_t>& pp_secret); - virtual TlsConnection* tls_connection() = 0; + virtual const TlsConnection* tls_connection() const = 0; - SSL* ssl() { return tls_connection()->ssl(); } + SSL* ssl() const { return tls_connection()->ssl(); } QuicCryptoStream* stream() { return stream_; } QuicSession* session() { return session_; } diff --git a/chromium/net/third_party/quiche/src/quic/core/tls_handshaker_test.cc b/chromium/net/third_party/quiche/src/quic/core/tls_handshaker_test.cc index f15a2667210..a298089cdd3 100644 --- a/chromium/net/third_party/quiche/src/quic/core/tls_handshaker_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/tls_handshaker_test.cc @@ -10,6 +10,7 @@ #include "net/third_party/quiche/src/quic/core/tls_client_handshaker.h" #include "net/third_party/quiche/src/quic/core/tls_server_handshaker.h" #include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h" #include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" #include "net/third_party/quiche/src/quic/platform/api/quic_test.h" #include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h" @@ -22,6 +23,8 @@ namespace test { namespace { using ::testing::_; +using ::testing::ElementsAreArray; +using ::testing::Return; class FakeProofVerifier : public ProofVerifier { public: @@ -225,6 +228,7 @@ class TestQuicCryptoClientStream : public TestQuicCryptoStream { ~TestQuicCryptoClientStream() override = default; TlsHandshaker* handshaker() const override { return handshaker_.get(); } + TlsClientHandshaker* client_handshaker() const { return handshaker_.get(); } bool CryptoConnect() { return handshaker_->CryptoConnect(); } @@ -302,6 +306,15 @@ class TlsHandshakerTest : public QuicTest { EXPECT_FALSE(client_stream_->handshake_confirmed()); EXPECT_FALSE(server_stream_->encryption_established()); EXPECT_FALSE(server_stream_->handshake_confirmed()); + const std::string default_alpn = + AlpnForVersion(client_session_.connection()->version()); + ON_CALL(client_session_, GetAlpnsToOffer()) + .WillByDefault(Return(std::vector<std::string>({default_alpn}))); + ON_CALL(server_session_, SelectAlpn(_)) + .WillByDefault( + [default_alpn](const std::vector<QuicStringPiece>& alpns) { + return std::find(alpns.begin(), alpns.end(), default_alpn); + }); } MockQuicConnectionHelper conn_helper_; @@ -317,8 +330,17 @@ class TlsHandshakerTest : public QuicTest { }; TEST_F(TlsHandshakerTest, CryptoHandshake) { + EXPECT_FALSE(client_conn_->IsHandshakeConfirmed()); + EXPECT_FALSE(server_conn_->IsHandshakeConfirmed()); + EXPECT_CALL(*client_conn_, CloseConnection(_, _, _)).Times(0); EXPECT_CALL(*server_conn_, CloseConnection(_, _, _)).Times(0); + EXPECT_CALL(client_session_, + OnCryptoHandshakeEvent(QuicSession::ENCRYPTION_ESTABLISHED)); + EXPECT_CALL(client_session_, + OnCryptoHandshakeEvent(QuicSession::HANDSHAKE_CONFIRMED)); + EXPECT_CALL(server_session_, + OnCryptoHandshakeEvent(QuicSession::HANDSHAKE_CONFIRMED)); client_stream_->CryptoConnect(); ExchangeHandshakeMessages(client_stream_, server_stream_); @@ -326,6 +348,8 @@ TEST_F(TlsHandshakerTest, CryptoHandshake) { EXPECT_TRUE(client_stream_->encryption_established()); EXPECT_TRUE(server_stream_->handshake_confirmed()); EXPECT_TRUE(server_stream_->encryption_established()); + EXPECT_TRUE(client_conn_->IsHandshakeConfirmed()); + EXPECT_FALSE(server_conn_->IsHandshakeConfirmed()); } TEST_F(TlsHandshakerTest, HandshakeWithAsyncProofSource) { @@ -430,6 +454,111 @@ TEST_F(TlsHandshakerTest, ServerConnectionClosedOnTlsError) { EXPECT_FALSE(server_stream_->handshake_confirmed()); } +TEST_F(TlsHandshakerTest, ClientNotSendingALPN) { + client_stream_->client_handshaker()->AllowEmptyAlpnForTests(); + EXPECT_CALL(client_session_, GetAlpnsToOffer()) + .WillOnce(Return(std::vector<std::string>())); + EXPECT_CALL(*client_conn_, CloseConnection(QUIC_HANDSHAKE_FAILED, + "Server did not select ALPN", _)); + EXPECT_CALL(*server_conn_, + CloseConnection(QUIC_HANDSHAKE_FAILED, + "Server did not receive a known ALPN", _)); + client_stream_->CryptoConnect(); + ExchangeHandshakeMessages(client_stream_, server_stream_); + + EXPECT_FALSE(client_stream_->handshake_confirmed()); + EXPECT_FALSE(client_stream_->encryption_established()); + EXPECT_FALSE(server_stream_->handshake_confirmed()); + EXPECT_FALSE(server_stream_->encryption_established()); +} + +TEST_F(TlsHandshakerTest, ClientSendingBadALPN) { + static std::string kTestBadClientAlpn = "bad-client-alpn"; + EXPECT_CALL(client_session_, GetAlpnsToOffer()) + .WillOnce(Return(std::vector<std::string>({kTestBadClientAlpn}))); + EXPECT_CALL(*client_conn_, CloseConnection(QUIC_HANDSHAKE_FAILED, + "Server did not select ALPN", _)); + EXPECT_CALL(*server_conn_, + CloseConnection(QUIC_HANDSHAKE_FAILED, + "Server did not receive a known ALPN", _)); + client_stream_->CryptoConnect(); + ExchangeHandshakeMessages(client_stream_, server_stream_); + + EXPECT_FALSE(client_stream_->handshake_confirmed()); + EXPECT_FALSE(client_stream_->encryption_established()); + EXPECT_FALSE(server_stream_->handshake_confirmed()); + EXPECT_FALSE(server_stream_->encryption_established()); +} + +TEST_F(TlsHandshakerTest, ClientSendingTooManyALPNs) { + std::string long_alpn(250, 'A'); + EXPECT_CALL(client_session_, GetAlpnsToOffer()) + .WillOnce(Return(std::vector<std::string>({ + long_alpn + "1", + long_alpn + "2", + long_alpn + "3", + long_alpn + "4", + long_alpn + "5", + long_alpn + "6", + long_alpn + "7", + long_alpn + "8", + }))); + EXPECT_QUIC_BUG(client_stream_->CryptoConnect(), "Failed to set ALPN"); +} + +TEST_F(TlsHandshakerTest, ServerRequiresCustomALPN) { + static const std::string kTestAlpn = "An ALPN That Client Did Not Offer"; + EXPECT_CALL(server_session_, SelectAlpn(_)) + .WillOnce([](const std::vector<QuicStringPiece>& alpns) { + return std::find(alpns.cbegin(), alpns.cend(), kTestAlpn); + }); + EXPECT_CALL(*client_conn_, CloseConnection(QUIC_HANDSHAKE_FAILED, + "Server did not select ALPN", _)); + EXPECT_CALL(*server_conn_, + CloseConnection(QUIC_HANDSHAKE_FAILED, + "Server did not receive a known ALPN", _)); + client_stream_->CryptoConnect(); + ExchangeHandshakeMessages(client_stream_, server_stream_); + + EXPECT_FALSE(client_stream_->handshake_confirmed()); + EXPECT_FALSE(client_stream_->encryption_established()); + EXPECT_FALSE(server_stream_->handshake_confirmed()); + EXPECT_FALSE(server_stream_->encryption_established()); +} + +TEST_F(TlsHandshakerTest, CustomALPNNegotiation) { + EXPECT_CALL(*client_conn_, CloseConnection(_, _, _)).Times(0); + EXPECT_CALL(*server_conn_, CloseConnection(_, _, _)).Times(0); + EXPECT_CALL(client_session_, + OnCryptoHandshakeEvent(QuicSession::ENCRYPTION_ESTABLISHED)); + EXPECT_CALL(client_session_, + OnCryptoHandshakeEvent(QuicSession::HANDSHAKE_CONFIRMED)); + EXPECT_CALL(server_session_, + OnCryptoHandshakeEvent(QuicSession::HANDSHAKE_CONFIRMED)); + + static const std::string kTestAlpn = "A Custom ALPN Value"; + static const std::vector<std::string> kTestAlpns( + {"foo", "bar", kTestAlpn, "something else"}); + EXPECT_CALL(client_session_, GetAlpnsToOffer()) + .WillRepeatedly(Return(kTestAlpns)); + EXPECT_CALL(server_session_, SelectAlpn(_)) + .WillOnce([](const std::vector<QuicStringPiece>& alpns) { + EXPECT_THAT(alpns, ElementsAreArray(kTestAlpns)); + return std::find(alpns.cbegin(), alpns.cend(), kTestAlpn); + }); + EXPECT_CALL(client_session_, OnAlpnSelected(QuicStringPiece(kTestAlpn))); + EXPECT_CALL(server_session_, OnAlpnSelected(QuicStringPiece(kTestAlpn))); + client_stream_->CryptoConnect(); + ExchangeHandshakeMessages(client_stream_, server_stream_); + + EXPECT_TRUE(client_stream_->handshake_confirmed()); + EXPECT_TRUE(client_stream_->encryption_established()); + EXPECT_TRUE(server_stream_->handshake_confirmed()); + EXPECT_TRUE(server_stream_->encryption_established()); + EXPECT_TRUE(client_conn_->IsHandshakeConfirmed()); + EXPECT_FALSE(server_conn_->IsHandshakeConfirmed()); +} + } // namespace } // namespace test } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/tls_server_handshaker.cc b/chromium/net/third_party/quiche/src/quic/core/tls_server_handshaker.cc index 81721041847..0b4215d3b04 100644 --- a/chromium/net/third_party/quiche/src/quic/core/tls_server_handshaker.cc +++ b/chromium/net/third_party/quiche/src/quic/core/tls_server_handshaker.cc @@ -61,7 +61,7 @@ TlsServerHandshaker::TlsServerHandshaker(QuicCryptoStream* stream, if (!SetTransportParameters()) { CloseConnection(QUIC_HANDSHAKE_FAILED, - "Failed to set Transport Parameters"); + "Server failed to set Transport Parameters"); } } @@ -136,6 +136,11 @@ CryptoMessageParser* TlsServerHandshaker::crypto_message_parser() { return TlsHandshaker::crypto_message_parser(); } +size_t TlsServerHandshaker::BufferSizeLimitForLevel( + EncryptionLevel level) const { + return TlsHandshaker::BufferSizeLimitForLevel(level); +} + void TlsServerHandshaker::AdvanceHandshake() { if (state_ == STATE_CONNECTION_CLOSED) { QUIC_LOG(INFO) << "TlsServerHandshaker received handshake message after " @@ -170,7 +175,8 @@ void TlsServerHandshaker::AdvanceHandshake() { QUIC_LOG(WARNING) << "SSL_do_handshake failed; SSL_get_error returns " << ssl_error << ", state_ = " << state_; ERR_print_errors_fp(stderr); - CloseConnection(QUIC_HANDSHAKE_FAILED, "TLS handshake failed"); + CloseConnection(QUIC_HANDSHAKE_FAILED, + "Server observed TLS handshake failure"); } } @@ -188,8 +194,9 @@ bool TlsServerHandshaker::ProcessTransportParameters( SSL_get_peer_quic_transport_params(ssl(), &client_params_bytes, ¶ms_bytes_len); if (params_bytes_len == 0 || - !ParseTransportParameters(client_params_bytes, params_bytes_len, - Perspective::IS_CLIENT, &client_params)) { + !ParseTransportParameters(session()->connection()->version(), + Perspective::IS_CLIENT, client_params_bytes, + params_bytes_len, &client_params)) { *error_details = "Unable to parse Transport Parameters"; return false; } @@ -228,7 +235,8 @@ bool TlsServerHandshaker::SetTransportParameters() { // TODO(nharper): Provide an actual value for the stateless reset token. server_params.stateless_reset_token.resize(16); std::vector<uint8_t> server_params_bytes; - if (!SerializeTransportParameters(server_params, &server_params_bytes) || + if (!SerializeTransportParameters(session()->connection()->version(), + server_params, &server_params_bytes) || SSL_set_quic_transport_params(ssl(), server_params_bytes.data(), server_params_bytes.size()) != 1) { return false; @@ -237,6 +245,16 @@ bool TlsServerHandshaker::SetTransportParameters() { } void TlsServerHandshaker::FinishHandshake() { + if (!valid_alpn_received_) { + QUIC_DLOG(ERROR) + << "Server: handshake finished without receiving a known ALPN"; + // TODO(b/130164908) this should send no_application_protocol + // instead of QUIC_HANDSHAKE_FAILED. + CloseConnection(QUIC_HANDSHAKE_FAILED, + "Server did not receive a known ALPN"); + return; + } + QUIC_LOG(INFO) << "Server: handshake finished"; state_ = STATE_HANDSHAKE_COMPLETE; @@ -244,6 +262,7 @@ void TlsServerHandshaker::FinishHandshake() { session()->NeuterUnencryptedData(); encryption_established_ = true; handshake_confirmed_ = true; + session()->OnCryptoHandshakeEvent(QuicSession::HANDSHAKE_CONFIRMED); } ssl_private_key_result_t TlsServerHandshaker::PrivateKeySign( @@ -326,25 +345,44 @@ int TlsServerHandshaker::SelectAlpn(const uint8_t** out, const uint8_t* in, unsigned in_len) { // |in| contains a sequence of 1-byte-length-prefixed values. - // We currently simply return the first provided ALPN value. - // TODO(b/130164908) Act on ALPN. + *out_len = 0; + *out = nullptr; if (in_len == 0) { - *out_len = 0; - *out = nullptr; - QUIC_DLOG(INFO) << "No ALPN provided"; - return SSL_TLSEXT_ERR_OK; + QUIC_DLOG(ERROR) << "No ALPN provided by client"; + return SSL_TLSEXT_ERR_NOACK; } - const uint8_t first_alpn_length = in[0]; - if (static_cast<unsigned>(first_alpn_length) > in_len - 1) { - QUIC_LOG(ERROR) << "Failed to parse ALPN"; - return SSL_TLSEXT_ERR_ALERT_FATAL; + + CBS all_alpns; + CBS_init(&all_alpns, in, in_len); + + std::vector<QuicStringPiece> alpns; + while (CBS_len(&all_alpns) > 0) { + CBS alpn; + if (!CBS_get_u8_length_prefixed(&all_alpns, &alpn)) { + QUIC_DLOG(ERROR) << "Failed to parse ALPN length"; + return SSL_TLSEXT_ERR_NOACK; + } + + const size_t alpn_length = CBS_len(&alpn); + if (alpn_length == 0) { + QUIC_DLOG(ERROR) << "Received invalid zero-length ALPN"; + return SSL_TLSEXT_ERR_NOACK; + } + + alpns.emplace_back(reinterpret_cast<const char*>(CBS_data(&alpn)), + alpn_length); } - *out_len = first_alpn_length; - *out = in + 1; - QUIC_DLOG(INFO) << "Server selecting ALPN '" - << QuicStringPiece(reinterpret_cast<const char*>(*out), - *out_len) - << "'"; + + auto selected_alpn = session()->SelectAlpn(alpns); + if (selected_alpn == alpns.end()) { + QUIC_DLOG(ERROR) << "No known ALPN provided by client"; + return SSL_TLSEXT_ERR_NOACK; + } + + session()->OnAlpnSelected(*selected_alpn); + valid_alpn_received_ = true; + *out_len = selected_alpn->size(); + *out = reinterpret_cast<const uint8_t*>(selected_alpn->data()); return SSL_TLSEXT_ERR_OK; } diff --git a/chromium/net/third_party/quiche/src/quic/core/tls_server_handshaker.h b/chromium/net/third_party/quiche/src/quic/core/tls_server_handshaker.h index 71fe6dd0276..829aeaf618b 100644 --- a/chromium/net/third_party/quiche/src/quic/core/tls_server_handshaker.h +++ b/chromium/net/third_party/quiche/src/quic/core/tls_server_handshaker.h @@ -58,9 +58,12 @@ class QUIC_EXPORT_PRIVATE TlsServerHandshaker const QuicCryptoNegotiatedParameters& crypto_negotiated_params() const override; CryptoMessageParser* crypto_message_parser() override; + size_t BufferSizeLimitForLevel(EncryptionLevel level) const override; protected: - TlsConnection* tls_connection() override { return &tls_connection_; } + const TlsConnection* tls_connection() const override { + return &tls_connection_; + } // Called when a new message is received on the crypto stream and is available // for the TLS stack to read. @@ -123,6 +126,7 @@ class QUIC_EXPORT_PRIVATE TlsServerHandshaker bool encryption_established_ = false; bool handshake_confirmed_ = false; + bool valid_alpn_received_ = false; QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> crypto_negotiated_params_; TlsServerConnection tls_connection_; diff --git a/chromium/net/third_party/quiche/src/quic/core/uber_quic_stream_id_manager.cc b/chromium/net/third_party/quiche/src/quic/core/uber_quic_stream_id_manager.cc index 24c3b7cc62f..a2b3b7a6abc 100644 --- a/chromium/net/third_party/quiche/src/quic/core/uber_quic_stream_id_manager.cc +++ b/chromium/net/third_party/quiche/src/quic/core/uber_quic_stream_id_manager.cc @@ -24,40 +24,7 @@ UberQuicStreamIdManager::UberQuicStreamIdManager( /*unidirectional=*/true, max_open_outgoing_unidirectional_streams, max_open_incoming_unidirectional_streams) {} -void UberQuicStreamIdManager::RegisterStaticStream( - QuicStreamId id, - bool stream_already_counted) { - if (QuicUtils::IsBidirectionalStreamId(id)) { - bidirectional_stream_id_manager_.RegisterStaticStream( - id, stream_already_counted); - return; - } - unidirectional_stream_id_manager_.RegisterStaticStream( - id, stream_already_counted); -} - -void UberQuicStreamIdManager::AdjustMaxOpenOutgoingUnidirectionalStreams( - size_t max_streams) { - unidirectional_stream_id_manager_.AdjustMaxOpenOutgoingStreams(max_streams); -} -void UberQuicStreamIdManager::AdjustMaxOpenOutgoingBidirectionalStreams( - size_t max_streams) { - bidirectional_stream_id_manager_.AdjustMaxOpenOutgoingStreams(max_streams); -} - -void UberQuicStreamIdManager::ConfigureMaxOpenOutgoingBidirectionalStreams( - size_t max_streams) { - bidirectional_stream_id_manager_.ConfigureMaxOpenOutgoingStreams(max_streams); -} -void UberQuicStreamIdManager::ConfigureMaxOpenOutgoingUnidirectionalStreams( - size_t max_streams) { - unidirectional_stream_id_manager_.ConfigureMaxOpenOutgoingStreams( - max_streams); -} -// TODO(fkastenholz): SetMax is cognizant of the number of static streams and -// sets the maximum to be max_streams + number_of_statics. This should -// eventually be removed from IETF QUIC. void UberQuicStreamIdManager::SetMaxOpenOutgoingBidirectionalStreams( size_t max_open_streams) { bidirectional_stream_id_manager_.SetMaxOpenOutgoingStreams(max_open_streams); diff --git a/chromium/net/third_party/quiche/src/quic/core/uber_quic_stream_id_manager.h b/chromium/net/third_party/quiche/src/quic/core/uber_quic_stream_id_manager.h index 45b0d33ff46..61eaf62373b 100644 --- a/chromium/net/third_party/quiche/src/quic/core/uber_quic_stream_id_manager.h +++ b/chromium/net/third_party/quiche/src/quic/core/uber_quic_stream_id_manager.h @@ -27,35 +27,12 @@ class QUIC_EXPORT_PRIVATE UberQuicStreamIdManager { QuicStreamCount max_open_incoming_bidirectional_streams, QuicStreamCount max_open_incoming_unidirectional_streams); - // Called when a stream with |stream_id| is registered as a static stream. - // If |stream_already_counted| is true, the static stream is already counted - // as an open stream earlier, so no need to count it again. - void RegisterStaticStream(QuicStreamId id, bool stream_already_counted); - - // Sets the maximum outgoing stream count as a result of doing the transport - // configuration negotiation. Forces the limit to max_streams, regardless of - // static streams. - void ConfigureMaxOpenOutgoingBidirectionalStreams(size_t max_streams); - void ConfigureMaxOpenOutgoingUnidirectionalStreams(size_t max_streams); - - // Sets the limits to max_open_streams + number of static streams - // in existence. SetMaxOpenOutgoingStreams will QUIC_BUG if it is called - // after getting the first MAX_STREAMS frame or the transport configuration - // was done. - // TODO(fkastenholz): SetMax is cognizant of the number of static streams and - // sets the maximum to be max_streams + number_of_statics. This should - // eventually be removed from IETF QUIC. + // Sets the limits to max_open_streams. void SetMaxOpenOutgoingBidirectionalStreams(size_t max_open_streams); void SetMaxOpenOutgoingUnidirectionalStreams(size_t max_open_streams); void SetMaxOpenIncomingBidirectionalStreams(size_t max_open_streams); void SetMaxOpenIncomingUnidirectionalStreams(size_t max_open_streams); - // Sets the outgoing stream count to the number of static streams + max - // outgoing streams. Unlike SetMaxOpenOutgoingStreams, this method will - // not QUIC_BUG if called after getting the first MAX_STREAMS frame. - void AdjustMaxOpenOutgoingBidirectionalStreams(size_t max_streams); - void AdjustMaxOpenOutgoingUnidirectionalStreams(size_t max_streams); - // Returns true if next outgoing bidirectional stream ID can be allocated. bool CanOpenNextOutgoingBidirectionalStream(); diff --git a/chromium/net/third_party/quiche/src/quic/core/uber_quic_stream_id_manager_test.cc b/chromium/net/third_party/quiche/src/quic/core/uber_quic_stream_id_manager_test.cc index 203dbb801f5..6f5d34e4efd 100644 --- a/chromium/net/third_party/quiche/src/quic/core/uber_quic_stream_id_manager_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/uber_quic_stream_id_manager_test.cc @@ -123,38 +123,6 @@ TEST_P(UberQuicStreamIdManagerTest, Initialization) { } } -TEST_P(UberQuicStreamIdManagerTest, RegisterStaticStream) { - QuicStreamId first_incoming_bidirectional_stream_id = - GetParam() == Perspective::IS_SERVER - ? GetNthClientInitiatedBidirectionalId(0) - : GetNthServerInitiatedBidirectionalId(0); - QuicStreamId first_incoming_unidirectional_stream_id = - GetParam() == Perspective::IS_SERVER - ? GetNthClientInitiatedUnidirectionalId(0) - : GetNthServerInitiatedUnidirectionalId(0); - - QuicStreamCount actual_max_allowed_incoming_bidirectional_streams = - manager_->actual_max_allowed_incoming_bidirectional_streams(); - QuicStreamCount actual_max_allowed_incoming_unidirectional_streams = - manager_->actual_max_allowed_incoming_unidirectional_streams(); - manager_->RegisterStaticStream(first_incoming_bidirectional_stream_id, - /*stream_already_counted = */ false); - // Verify actual_max_allowed_incoming_bidirectional_streams increases. - EXPECT_EQ(actual_max_allowed_incoming_bidirectional_streams + 1u, - manager_->actual_max_allowed_incoming_bidirectional_streams()); - // Verify actual_max_allowed_incoming_unidirectional_streams does not - // change. - EXPECT_EQ(actual_max_allowed_incoming_unidirectional_streams, - manager_->actual_max_allowed_incoming_unidirectional_streams()); - - manager_->RegisterStaticStream(first_incoming_unidirectional_stream_id, - /*stream_already_counted = */ false); - EXPECT_EQ(actual_max_allowed_incoming_bidirectional_streams + 1u, - manager_->actual_max_allowed_incoming_bidirectional_streams()); - EXPECT_EQ(actual_max_allowed_incoming_unidirectional_streams + 1u, - manager_->actual_max_allowed_incoming_unidirectional_streams()); -} - TEST_P(UberQuicStreamIdManagerTest, SetMaxOpenOutgoingStreams) { const size_t kNumMaxOutgoingStream = 123; // Set the uni- and bi- directional limits to different values to ensure diff --git a/chromium/net/third_party/quiche/src/quic/core/uber_received_packet_manager.cc b/chromium/net/third_party/quiche/src/quic/core/uber_received_packet_manager.cc index 6df7490a85a..78cf35919d1 100644 --- a/chromium/net/third_party/quiche/src/quic/core/uber_received_packet_manager.cc +++ b/chromium/net/third_party/quiche/src/quic/core/uber_received_packet_manager.cc @@ -4,6 +4,7 @@ #include "net/third_party/quiche/src/quic/core/uber_received_packet_manager.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" #include "net/third_party/quiche/src/quic/core/quic_utils.h" #include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" @@ -77,19 +78,18 @@ void UberReceivedPacketManager::MaybeUpdateAckTimeout( QuicPacketNumber last_received_packet_number, QuicTime time_of_last_received_packet, QuicTime now, - const RttStats* rtt_stats, - QuicTime::Delta local_max_ack_delay) { + const RttStats* rtt_stats) { if (!supports_multiple_packet_number_spaces_) { received_packet_managers_[0].MaybeUpdateAckTimeout( should_last_packet_instigate_acks, last_received_packet_number, - time_of_last_received_packet, now, rtt_stats, local_max_ack_delay); + time_of_last_received_packet, now, rtt_stats); return; } received_packet_managers_[QuicUtils::GetPacketNumberSpace( decrypted_packet_level)] - .MaybeUpdateAckTimeout( - should_last_packet_instigate_acks, last_received_packet_number, - time_of_last_received_packet, now, rtt_stats, local_max_ack_delay); + .MaybeUpdateAckTimeout(should_last_packet_instigate_acks, + last_received_packet_number, + time_of_last_received_packet, now, rtt_stats); } void UberReceivedPacketManager::ResetAckStates( @@ -112,6 +112,12 @@ void UberReceivedPacketManager::EnableMultiplePacketNumberSpacesSupport() { "packet has been received."; return; } + // In IETF QUIC, the peer is expected to acknowledge packets in Initial and + // Handshake packets with minimal delay. + received_packet_managers_[INITIAL_DATA].set_local_max_ack_delay( + QuicTime::Delta::FromMilliseconds(1)); + received_packet_managers_[HANDSHAKE_DATA].set_local_max_ack_delay( + QuicTime::Delta::FromMilliseconds(1)); supports_multiple_packet_number_spaces_ = true; } @@ -207,6 +213,23 @@ void UberReceivedPacketManager::set_max_ack_ranges(size_t max_ack_ranges) { } } +QuicTime::Delta UberReceivedPacketManager::max_ack_delay() { + if (!supports_multiple_packet_number_spaces_) { + return received_packet_managers_[0].local_max_ack_delay(); + } + return received_packet_managers_[APPLICATION_DATA].local_max_ack_delay(); +} + +void UberReceivedPacketManager::set_max_ack_delay( + QuicTime::Delta max_ack_delay) { + if (!supports_multiple_packet_number_spaces_) { + received_packet_managers_[0].set_local_max_ack_delay(max_ack_delay); + return; + } + received_packet_managers_[APPLICATION_DATA].set_local_max_ack_delay( + max_ack_delay); +} + void UberReceivedPacketManager::set_save_timestamps(bool save_timestamps) { for (auto& received_packet_manager : received_packet_managers_) { received_packet_manager.set_save_timestamps(save_timestamps); diff --git a/chromium/net/third_party/quiche/src/quic/core/uber_received_packet_manager.h b/chromium/net/third_party/quiche/src/quic/core/uber_received_packet_manager.h index c4428041465..14ba8684f5a 100644 --- a/chromium/net/third_party/quiche/src/quic/core/uber_received_packet_manager.h +++ b/chromium/net/third_party/quiche/src/quic/core/uber_received_packet_manager.h @@ -48,8 +48,7 @@ class QUIC_EXPORT_PRIVATE UberReceivedPacketManager { QuicPacketNumber last_received_packet_number, QuicTime time_of_last_received_packet, QuicTime now, - const RttStats* rtt_stats, - QuicTime::Delta local_max_ack_delay); + const RttStats* rtt_stats); // Resets ACK related states, called after an ACK is successfully sent. void ResetAckStates(EncryptionLevel encryption_level); @@ -89,6 +88,10 @@ class QUIC_EXPORT_PRIVATE UberReceivedPacketManager { void set_max_ack_ranges(size_t max_ack_ranges); + // Get and set the max ack delay to use for application data. + QuicTime::Delta max_ack_delay(); + void set_max_ack_delay(QuicTime::Delta max_ack_delay); + void set_save_timestamps(bool save_timestamps); private: diff --git a/chromium/net/third_party/quiche/src/quic/core/uber_received_packet_manager_test.cc b/chromium/net/third_party/quiche/src/quic/core/uber_received_packet_manager_test.cc index 64dbb177362..9372da8e370 100644 --- a/chromium/net/third_party/quiche/src/quic/core/uber_received_packet_manager_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/uber_received_packet_manager_test.cc @@ -98,7 +98,7 @@ class UberReceivedPacketManagerTest : public QuicTest { manager_->MaybeUpdateAckTimeout( should_last_packet_instigate_acks, decrypted_packet_level, QuicPacketNumber(last_received_packet_number), clock_.ApproximateNow(), - clock_.ApproximateNow(), &rtt_stats_, kDelayedAckTime); + clock_.ApproximateNow(), &rtt_stats_); } void CheckAckTimeout(QuicTime time) { @@ -378,6 +378,20 @@ TEST_F(UberReceivedPacketManagerTest, SendDelayedAfterQuiescence) { CheckAckTimeout(ack_time); } +TEST_F(UberReceivedPacketManagerTest, SendDelayedMaxAckDelay) { + EXPECT_FALSE(HasPendingAck()); + QuicTime::Delta max_ack_delay = QuicTime::Delta::FromMilliseconds(100); + manager_->set_max_ack_delay(max_ack_delay); + QuicTime ack_time = clock_.ApproximateNow() + max_ack_delay; + + RecordPacketReceipt(1, clock_.ApproximateNow()); + MaybeUpdateAckTimeout(kInstigateAck, 1); + CheckAckTimeout(ack_time); + // Simulate delayed ack alarm firing. + clock_.AdvanceTime(max_ack_delay); + CheckAckTimeout(clock_.ApproximateNow()); +} + TEST_F(UberReceivedPacketManagerTest, SendDelayedAckDecimation) { EXPECT_FALSE(HasPendingAck()); UberReceivedPacketManagerPeer::SetAckMode(manager_.get(), ACK_DECIMATION); @@ -765,7 +779,12 @@ TEST_F(UberReceivedPacketManagerTest, AckSendingDifferentPacketNumberSpaces) { MaybeUpdateAckTimeout(kInstigateAck, ENCRYPTION_HANDSHAKE, 3); EXPECT_TRUE(HasPendingAck()); // Delayed ack is scheduled. - CheckAckTimeout(clock_.ApproximateNow() + kDelayedAckTime); + CheckAckTimeout(clock_.ApproximateNow() + + QuicTime::Delta::FromMilliseconds(1)); + // Send delayed handshake data ACK. + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1)); + CheckAckTimeout(clock_.ApproximateNow()); + EXPECT_FALSE(HasPendingAck()); RecordPacketReceipt(ENCRYPTION_FORWARD_SECURE, 3); MaybeUpdateAckTimeout(kInstigateAck, ENCRYPTION_FORWARD_SECURE, 3); @@ -777,12 +796,6 @@ TEST_F(UberReceivedPacketManagerTest, AckSendingDifferentPacketNumberSpaces) { MaybeUpdateAckTimeout(kInstigateAck, ENCRYPTION_FORWARD_SECURE, 2); // Application data ACK should be sent immediately. CheckAckTimeout(clock_.ApproximateNow()); - // Delayed ACK of handshake data is pending. - CheckAckTimeout(clock_.ApproximateNow() + kDelayedAckTime); - - // Send delayed handshake data ACK. - clock_.AdvanceTime(kDelayedAckTime); - CheckAckTimeout(clock_.ApproximateNow()); EXPECT_FALSE(HasPendingAck()); } diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_default_proof_providers.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_default_proof_providers.h index 6f3b636372f..98907096137 100644 --- a/chromium/net/third_party/quiche/src/quic/platform/api/quic_default_proof_providers.h +++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_default_proof_providers.h @@ -13,11 +13,12 @@ namespace quic { -// Provides a default proof verifier. The verifier has to do a good faith -// attempt at verifying the certificate against a reasonable root store, and not -// just always return success. -inline std::unique_ptr<ProofVerifier> CreateDefaultProofVerifier() { - return CreateDefaultProofVerifierImpl(); +// Provides a default proof verifier that can verify a cert chain for |host|. +// The verifier has to do a good faith attempt at verifying the certificate +// against a reasonable root store, and not just always return success. +inline std::unique_ptr<ProofVerifier> CreateDefaultProofVerifier( + const std::string& host) { + return CreateDefaultProofVerifierImpl(host); } // Provides a default proof source for CLI-based tools. The actual certificates diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_logging.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_logging.h index ba48dc12b76..ce706e53ec8 100644 --- a/chromium/net/third_party/quiche/src/quic/platform/api/quic_logging.h +++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_logging.h @@ -25,6 +25,7 @@ #define QUIC_LOG_IF(severity, condition) QUIC_LOG_IF_IMPL(severity, condition) #define QUIC_PREDICT_FALSE(x) QUIC_PREDICT_FALSE_IMPL(x) +#define QUIC_PREDICT_TRUE(x) QUIC_PREDICT_TRUE_IMPL(x) // This is a noop in release build. #define QUIC_NOTREACHED() QUIC_NOTREACHED_IMPL() diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_mutex.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_mutex.h index 162863a06b0..d35c6602aeb 100644 --- a/chromium/net/third_party/quiche/src/quic/platform/api/quic_mutex.h +++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_mutex.h @@ -7,31 +7,42 @@ #include "net/quic/platform/impl/quic_mutex_impl.h" +#define QUIC_EXCLUSIVE_LOCKS_REQUIRED QUIC_EXCLUSIVE_LOCKS_REQUIRED_IMPL +#define QUIC_GUARDED_BY QUIC_GUARDED_BY_IMPL +#define QUIC_LOCKABLE QUIC_LOCKABLE_IMPL +#define QUIC_LOCKS_EXCLUDED QUIC_LOCKS_EXCLUDED_IMPL +#define QUIC_SHARED_LOCKS_REQUIRED QUIC_SHARED_LOCKS_REQUIRED_IMPL +#define QUIC_EXCLUSIVE_LOCK_FUNCTION QUIC_EXCLUSIVE_LOCK_FUNCTION_IMPL +#define QUIC_UNLOCK_FUNCTION QUIC_UNLOCK_FUNCTION_IMPL +#define QUIC_SHARED_LOCK_FUNCTION QUIC_SHARED_LOCK_FUNCTION_IMPL +#define QUIC_SCOPED_LOCKABLE QUIC_SCOPED_LOCKABLE_IMPL +#define QUIC_ASSERT_SHARED_LOCK QUIC_ASSERT_SHARED_LOCK_IMPL + namespace quic { // A class representing a non-reentrant mutex in QUIC. -class LOCKABLE QUIC_EXPORT_PRIVATE QuicMutex { +class QUIC_LOCKABLE QUIC_EXPORT_PRIVATE QuicMutex { public: QuicMutex() = default; QuicMutex(const QuicMutex&) = delete; QuicMutex& operator=(const QuicMutex&) = delete; // Block until this Mutex is free, then acquire it exclusively. - void WriterLock() EXCLUSIVE_LOCK_FUNCTION(); + void WriterLock() QUIC_EXCLUSIVE_LOCK_FUNCTION(); // Release this Mutex. Caller must hold it exclusively. - void WriterUnlock() UNLOCK_FUNCTION(); + void WriterUnlock() QUIC_UNLOCK_FUNCTION(); // Block until this Mutex is free or shared, then acquire a share of it. - void ReaderLock() SHARED_LOCK_FUNCTION(); + void ReaderLock() QUIC_SHARED_LOCK_FUNCTION(); // Release this Mutex. Caller could hold it in shared mode. - void ReaderUnlock() UNLOCK_FUNCTION(); + void ReaderUnlock() QUIC_UNLOCK_FUNCTION(); // Returns immediately if current thread holds the Mutex in at least shared // mode. Otherwise, may report an error (typically by crashing with a // diagnostic), or may return immediately. - void AssertReaderHeld() const ASSERT_SHARED_LOCK(); + void AssertReaderHeld() const QUIC_ASSERT_SHARED_LOCK(); private: QuicLockImpl impl_; @@ -39,13 +50,13 @@ class LOCKABLE QUIC_EXPORT_PRIVATE QuicMutex { // A helper class that acquires the given QuicMutex shared lock while the // QuicReaderMutexLock is in scope. -class SCOPED_LOCKABLE QUIC_EXPORT_PRIVATE QuicReaderMutexLock { +class QUIC_SCOPED_LOCKABLE QUIC_EXPORT_PRIVATE QuicReaderMutexLock { public: - explicit QuicReaderMutexLock(QuicMutex* lock) SHARED_LOCK_FUNCTION(lock); + explicit QuicReaderMutexLock(QuicMutex* lock) QUIC_SHARED_LOCK_FUNCTION(lock); QuicReaderMutexLock(const QuicReaderMutexLock&) = delete; QuicReaderMutexLock& operator=(const QuicReaderMutexLock&) = delete; - ~QuicReaderMutexLock() UNLOCK_FUNCTION(); + ~QuicReaderMutexLock() QUIC_UNLOCK_FUNCTION(); private: QuicMutex* const lock_; @@ -53,13 +64,14 @@ class SCOPED_LOCKABLE QUIC_EXPORT_PRIVATE QuicReaderMutexLock { // A helper class that acquires the given QuicMutex exclusive lock while the // QuicWriterMutexLock is in scope. -class SCOPED_LOCKABLE QUIC_EXPORT_PRIVATE QuicWriterMutexLock { +class QUIC_SCOPED_LOCKABLE QUIC_EXPORT_PRIVATE QuicWriterMutexLock { public: - explicit QuicWriterMutexLock(QuicMutex* lock) EXCLUSIVE_LOCK_FUNCTION(lock); + explicit QuicWriterMutexLock(QuicMutex* lock) + QUIC_EXCLUSIVE_LOCK_FUNCTION(lock); QuicWriterMutexLock(const QuicWriterMutexLock&) = delete; QuicWriterMutexLock& operator=(const QuicWriterMutexLock&) = delete; - ~QuicWriterMutexLock() UNLOCK_FUNCTION(); + ~QuicWriterMutexLock() QUIC_UNLOCK_FUNCTION(); private: QuicMutex* const lock_; diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_pcc_sender.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_pcc_sender.h index add66827181..03db5b3645c 100644 --- a/chromium/net/third_party/quiche/src/quic/platform/api/quic_pcc_sender.h +++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_pcc_sender.h @@ -11,7 +11,7 @@ namespace quic { class SendAlgorithmInterface; // Interface for creating a PCC SendAlgorithmInterface -SendAlgorithmInterface* CreatePccSender( +inline SendAlgorithmInterface* CreatePccSender( const QuicClock* clock, const RttStats* rtt_stats, const QuicUnackedPacketMap* unacked_packets, diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_port_utils.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_port_utils.h index aa85fc5a765..685be0a75bd 100644 --- a/chromium/net/third_party/quiche/src/quic/platform/api/quic_port_utils.h +++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_port_utils.h @@ -10,13 +10,14 @@ namespace quic { // Returns a UDP port that is currently unused. Check-fails if none are -// available. -inline int QuicPickUnusedPortOrDie() { - return QuicPickUnusedPortOrDieImpl(); +// available. May return 0 in which case the bind() call will cause the OS +// to use an unused port. +inline int QuicPickServerPortForTestsOrDie() { + return QuicPickServerPortForTestsOrDieImpl(); } // Indicates that a specified port previously returned by -// QuicPickUnusedPortOrDie is no longer used. +// QuicPickServerPortForTestsOrDie is no longer used. inline void QuicRecyclePort(int port) { return QuicRecyclePortImpl(port); } diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_text_utils.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_text_utils.h index 8db44cef75e..1fa3c72ab16 100644 --- a/chromium/net/third_party/quiche/src/quic/platform/api/quic_text_utils.h +++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_text_utils.h @@ -109,6 +109,11 @@ class QuicTextUtils { return QuicTextUtilsImpl::ContainsUpperCase(data); } + // Returns true if |data| contains only decimal digits. + static bool IsAllDigits(QuicStringPiece data) { + return QuicTextUtilsImpl::IsAllDigits(data); + } + // Splits |data| into a vector of pieces delimited by |delim|. static std::vector<QuicStringPiece> Split(QuicStringPiece data, char delim) { return QuicTextUtilsImpl::Split(data, delim); diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_text_utils_test.cc b/chromium/net/third_party/quiche/src/quic/platform/api/quic_text_utils_test.cc index 9b156d05ddf..af4e81d7092 100644 --- a/chromium/net/third_party/quiche/src/quic/platform/api/quic_text_utils_test.cc +++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_text_utils_test.cc @@ -201,6 +201,11 @@ TEST_F(QuicTextUtilsTest, Split) { QuicTextUtils::Split("a:b:c", ':')); EXPECT_EQ(std::vector<QuicStringPiece>({"a:b:c"}), QuicTextUtils::Split("a:b:c", ',')); + // Leading and trailing whitespace is preserved. + EXPECT_EQ(std::vector<QuicStringPiece>({"a", "b", "c"}), + QuicTextUtils::Split("a,b,c", ',')); + EXPECT_EQ(std::vector<QuicStringPiece>({" a", "b ", " c "}), + QuicTextUtils::Split(" a:b : c ", ':')); } } // namespace test diff --git a/chromium/net/third_party/quiche/src/quic/qbone/bonnet/icmp_reachable.cc b/chromium/net/third_party/quiche/src/quic/qbone/bonnet/icmp_reachable.cc new file mode 100644 index 00000000000..7779a8b212f --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/bonnet/icmp_reachable.cc @@ -0,0 +1,212 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/third_party/quiche/src/quic/qbone/bonnet/icmp_reachable.h" + +#include <netinet/ip6.h> + +#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_endian.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_mutex.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h" +#include "net/third_party/quiche/src/quic/qbone/platform/icmp_packet.h" + +namespace quic { +namespace { + +constexpr int kEpollFlags = EPOLLIN | EPOLLET; +constexpr size_t kMtu = 1280; + +constexpr size_t kIPv6AddrSize = sizeof(in6_addr); + +} // namespace + +const char kUnknownSource[] = "UNKNOWN"; +const char kNoSource[] = "N/A"; + +IcmpReachable::IcmpReachable(QuicIpAddress source, + QuicIpAddress destination, + absl::Duration timeout, + KernelInterface* kernel, + QuicEpollServer* epoll_server, + StatsInterface* stats) + : timeout_(timeout), + cb_(this), + kernel_(kernel), + epoll_server_(epoll_server), + stats_(stats), + send_fd_(0), + recv_fd_(0) { + src_.sin6_family = AF_INET6; + dst_.sin6_family = AF_INET6; + + memcpy(&src_.sin6_addr, source.ToPackedString().data(), kIPv6AddrSize); + memcpy(&dst_.sin6_addr, destination.ToPackedString().data(), kIPv6AddrSize); +} + +IcmpReachable::~IcmpReachable() { + if (send_fd_ > 0) { + kernel_->close(send_fd_); + } + if (recv_fd_ > 0) { + if (!epoll_server_->ShutdownCalled()) { + epoll_server_->UnregisterFD(recv_fd_); + } + + kernel_->close(recv_fd_); + } +} + +bool IcmpReachable::Init() { + send_fd_ = kernel_->socket(PF_INET6, SOCK_RAW | SOCK_NONBLOCK, IPPROTO_RAW); + if (send_fd_ < 0) { + QUIC_LOG(ERROR) << "Unable to open socket: " << errno; + return false; + } + + if (kernel_->bind(send_fd_, reinterpret_cast<struct sockaddr*>(&src_), + sizeof(sockaddr_in6)) < 0) { + QUIC_LOG(ERROR) << "Unable to bind socket: " << errno; + return false; + } + + recv_fd_ = + kernel_->socket(PF_INET6, SOCK_RAW | SOCK_NONBLOCK, IPPROTO_ICMPV6); + if (recv_fd_ < 0) { + QUIC_LOG(ERROR) << "Unable to open socket: " << errno; + return false; + } + + if (kernel_->bind(recv_fd_, reinterpret_cast<struct sockaddr*>(&src_), + sizeof(sockaddr_in6)) < 0) { + QUIC_LOG(ERROR) << "Unable to bind socket: " << errno; + return false; + } + + icmp6_filter filter; + ICMP6_FILTER_SETBLOCKALL(&filter); + ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &filter); + if (kernel_->setsockopt(recv_fd_, SOL_ICMPV6, ICMP6_FILTER, &filter, + sizeof(filter)) < 0) { + QUIC_LOG(ERROR) << "Unable to set ICMP6 filter."; + return false; + } + + epoll_server_->RegisterFD(recv_fd_, &cb_, kEpollFlags); + epoll_server_->RegisterAlarm(0, this); + + epoll_server_->set_timeout_in_us(50000); + + QuicWriterMutexLock mu(&header_lock_); + icmp_header_.icmp6_type = ICMP6_ECHO_REQUEST; + icmp_header_.icmp6_code = 0; + + QuicRandom::GetInstance()->RandBytes(&icmp_header_.icmp6_id, + sizeof(uint16_t)); + + return true; +} + +bool IcmpReachable::OnEvent(int fd) { + char buffer[kMtu]; + + sockaddr_in6 source_addr{}; + socklen_t source_addr_len = sizeof(source_addr); + + ssize_t size = kernel_->recvfrom(fd, &buffer, kMtu, 0, + reinterpret_cast<sockaddr*>(&source_addr), + &source_addr_len); + + if (size < 0) { + if (errno != EAGAIN && errno != EWOULDBLOCK) { + stats_->OnReadError(errno); + } + return false; + } + + QUIC_VLOG(2) << QuicTextUtils::HexDump(QuicStringPiece(buffer, size)); + + auto* header = reinterpret_cast<const icmp6_hdr*>(&buffer); + QuicWriterMutexLock mu(&header_lock_); + if (header->icmp6_data32[0] != icmp_header_.icmp6_data32[0]) { + QUIC_VLOG(2) << "Unexpected response. id: " << header->icmp6_id + << " seq: " << header->icmp6_seq + << " Expected id: " << icmp_header_.icmp6_id + << " seq: " << icmp_header_.icmp6_seq; + return true; + } + end_ = absl::Now(); + QUIC_VLOG(1) << "Received ping response in " + << absl::ToInt64Microseconds(end_ - start_) << "us."; + + string source; + QuicIpAddress source_ip; + if (!source_ip.FromPackedString( + reinterpret_cast<char*>(&source_addr.sin6_addr), sizeof(in6_addr))) { + QUIC_LOG(WARNING) << "Unable to parse source address."; + source = kUnknownSource; + } else { + source = source_ip.ToString(); + } + stats_->OnEvent({Status::REACHABLE, end_ - start_, source}); + return true; +} + +int64 /* allow-non-std-int */ IcmpReachable::OnAlarm() { + EpollAlarm::OnAlarm(); + + QuicWriterMutexLock mu(&header_lock_); + + if (end_ < start_) { + QUIC_VLOG(1) << "Timed out on sequence: " << icmp_header_.icmp6_seq; + stats_->OnEvent({Status::UNREACHABLE, absl::ZeroDuration(), kNoSource}); + } + + icmp_header_.icmp6_seq++; + CreateIcmpPacket(src_.sin6_addr, dst_.sin6_addr, icmp_header_, "", + [this](QuicStringPiece packet) { + QUIC_VLOG(2) << QuicTextUtils::HexDump(packet); + + ssize_t size = kernel_->sendto( + send_fd_, packet.data(), packet.size(), 0, + reinterpret_cast<struct sockaddr*>(&dst_), + sizeof(sockaddr_in6)); + + if (size < packet.size()) { + stats_->OnWriteError(errno); + } + start_ = absl::Now(); + }); + + return absl::ToUnixMicros(absl::Now() + timeout_); +} + +QuicStringPiece IcmpReachable::StatusName(IcmpReachable::Status status) { + switch (status) { + case REACHABLE: + return "REACHABLE"; + case UNREACHABLE: + return "UNREACHABLE"; + default: + return "UNKNOWN"; + } +} + +void IcmpReachable::EpollCallback::OnEvent(int fd, QuicEpollEvent* event) { + bool can_read_more = reachable_->OnEvent(fd); + if (can_read_more) { + event->out_ready_mask |= EPOLLIN; + } +} + +void IcmpReachable::EpollCallback::OnShutdown(QuicEpollServer* eps, int fd) { + eps->UnregisterFD(fd); +} + +string IcmpReachable::EpollCallback::Name() const { + return "ICMP Reachable"; +} + +} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/qbone/bonnet/icmp_reachable.h b/chromium/net/third_party/quiche/src/quic/qbone/bonnet/icmp_reachable.h new file mode 100644 index 00000000000..b4ce4c13966 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/bonnet/icmp_reachable.h @@ -0,0 +1,140 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef QUICHE_QUIC_QBONE_BONNET_ICMP_REACHABLE_H_ +#define QUICHE_QUIC_QBONE_BONNET_ICMP_REACHABLE_H_ + +#include <netinet/icmp6.h> + +#include "net/third_party/quiche/src/quic/platform/api/quic_ip_address.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_mutex.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" +#include "net/third_party/quiche/src/quic/qbone/bonnet/icmp_reachable_interface.h" +#include "net/third_party/quiche/src/quic/qbone/platform/kernel_interface.h" + +namespace quic { + +extern const char kUnknownSource[]; +extern const char kNoSource[]; + +// IcmpReachable schedules itself with an EpollServer, periodically sending +// ICMPv6 Echo Requests to the given |destination| on the interface that the +// given |source| is bound to. Echo Requests are sent once every |timeout|. +// On Echo Replies, timeouts, and I/O errors, the given |stats| object will +// be called back with details of the event. +class IcmpReachable : public IcmpReachableInterface { + public: + enum Status { REACHABLE, UNREACHABLE }; + + struct ReachableEvent { + Status status; + absl::Duration response_time; + string source; + }; + + class StatsInterface { + public: + StatsInterface() = default; + + StatsInterface(const StatsInterface&) = delete; + StatsInterface& operator=(const StatsInterface&) = delete; + + StatsInterface(StatsInterface&&) = delete; + StatsInterface& operator=(StatsInterface&&) = delete; + + virtual ~StatsInterface() = default; + + virtual void OnEvent(ReachableEvent event) = 0; + + virtual void OnReadError(int error) = 0; + + virtual void OnWriteError(int error) = 0; + }; + + // |source| is the IPv6 address bound to the interface that IcmpReachable will + // send Echo Requests on. + // |destination| is the IPv6 address of the destination of the Echo Requests. + // |timeout| is the duration IcmpReachable will wait between Echo Requests. + // If no Echo Response is received by the next Echo Request, it will + // be considered a timeout. + // |kernel| is not owned, but should outlive this instance. + // |epoll_server| is not owned, but should outlive this instance. + // IcmpReachable's Init() must be called from within the Epoll + // Server's thread. + // |stats| is not owned, but should outlive this instance. It will be called + // back on Echo Replies, timeouts, and I/O errors. + IcmpReachable(QuicIpAddress source, + QuicIpAddress destination, + absl::Duration timeout, + KernelInterface* kernel, + QuicEpollServer* epoll_server, + StatsInterface* stats); + + ~IcmpReachable() override; + + // Initializes this reachability probe. Must be called from within the + // |epoll_server|'s thread. + bool Init() QUIC_LOCKS_EXCLUDED(header_lock_) override; + + int64 /* allow-non-std-int */ OnAlarm() + QUIC_LOCKS_EXCLUDED(header_lock_) override; + + static QuicStringPiece StatusName(Status status); + + private: + class EpollCallback : public QuicEpollCallbackInterface { + public: + explicit EpollCallback(IcmpReachable* reachable) : reachable_(reachable) {} + + EpollCallback(const EpollCallback&) = delete; + EpollCallback& operator=(const EpollCallback&) = delete; + + EpollCallback(EpollCallback&&) = delete; + EpollCallback& operator=(EpollCallback&&) = delete; + + void OnRegistration(QuicEpollServer* eps, + int fd, + int event_mask) override{}; + + void OnModification(int fd, int event_mask) override{}; + + void OnEvent(int fd, QuicEpollEvent* event) override; + + void OnUnregistration(int fd, bool replaced) override{}; + + void OnShutdown(QuicEpollServer* eps, int fd) override; + + string Name() const override; + + private: + IcmpReachable* reachable_; + }; + + bool OnEvent(int fd) QUIC_LOCKS_EXCLUDED(header_lock_); + + const absl::Duration timeout_; + + EpollCallback cb_; + + sockaddr_in6 src_{}; + sockaddr_in6 dst_{}; + + KernelInterface* kernel_; + QuicEpollServer* epoll_server_; + + StatsInterface* stats_; + + int send_fd_; + int recv_fd_; + + QuicMutex header_lock_; + icmp6_hdr icmp_header_ QUIC_GUARDED_BY(header_lock_){}; + + absl::Time start_ = absl::InfinitePast(); + absl::Time end_ = absl::InfinitePast(); +}; + +} // namespace quic + +#endif // QUICHE_QUIC_QBONE_BONNET_ICMP_REACHABLE_H_ diff --git a/chromium/net/third_party/quiche/src/quic/qbone/bonnet/icmp_reachable_interface.h b/chromium/net/third_party/quiche/src/quic/qbone/bonnet/icmp_reachable_interface.h new file mode 100644 index 00000000000..e766a89ba3f --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/bonnet/icmp_reachable_interface.h @@ -0,0 +1,28 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef QUICHE_QUIC_QBONE_BONNET_ICMP_REACHABLE_INTERFACE_H_ +#define QUICHE_QUIC_QBONE_BONNET_ICMP_REACHABLE_INTERFACE_H_ + +#include "net/third_party/quiche/src/quic/platform/api/quic_epoll.h" + +namespace quic { + +class IcmpReachableInterface : public QuicEpollAlarmBase { + public: + IcmpReachableInterface() = default; + + IcmpReachableInterface(const IcmpReachableInterface&) = delete; + IcmpReachableInterface& operator=(const IcmpReachableInterface&) = delete; + + IcmpReachableInterface(IcmpReachableInterface&&) = delete; + IcmpReachableInterface& operator=(IcmpReachableInterface&&) = delete; + + // Initializes this reachability probe. + virtual bool Init() = 0; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_QBONE_BONNET_ICMP_REACHABLE_INTERFACE_H_ diff --git a/chromium/net/third_party/quiche/src/quic/qbone/bonnet/icmp_reachable_test.cc b/chromium/net/third_party/quiche/src/quic/qbone/bonnet/icmp_reachable_test.cc new file mode 100644 index 00000000000..303f0e280b7 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/bonnet/icmp_reachable_test.cc @@ -0,0 +1,266 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/third_party/quiche/src/quic/qbone/bonnet/icmp_reachable.h" + +#include <netinet/ip6.h> + +#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_epoll.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ip_address.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/qbone/platform/mock_kernel.h" + +namespace quic { +namespace { + +using ::testing::_; +using ::testing::InSequence; +using ::testing::Invoke; +using ::testing::Return; +using ::testing::StrictMock; + +constexpr char kSourceAddress[] = "fe80:1:2:3:4::1"; +constexpr char kDestinationAddress[] = "fe80:4:3:2:1::1"; + +constexpr int kFakeWriteFd = 0; + +icmp6_hdr GetHeaderFromPacket(const void* buf, size_t len) { + CHECK_GE(len, sizeof(ip6_hdr) + sizeof(icmp6_hdr)); + + auto* buffer = reinterpret_cast<const char*>(buf); + return *reinterpret_cast<const icmp6_hdr*>(&buffer[sizeof(ip6_hdr)]); +} + +class StatsInterface : public IcmpReachable::StatsInterface { + public: + void OnEvent(IcmpReachable::ReachableEvent event) override { + switch (event.status) { + case IcmpReachable::REACHABLE: { + reachable_count_++; + break; + } + case IcmpReachable::UNREACHABLE: { + unreachable_count_++; + break; + } + } + current_source_ = event.source; + } + + void OnReadError(int error) override { read_errors_[error]++; } + + void OnWriteError(int error) override { write_errors_[error]++; } + + bool HasWriteErrors() { return !write_errors_.empty(); } + + int WriteErrorCount(int error) { return write_errors_[error]; } + + bool HasReadErrors() { return !read_errors_.empty(); } + + int ReadErrorCount(int error) { return read_errors_[error]; } + + int reachable_count() { return reachable_count_; } + + int unreachable_count() { return unreachable_count_; } + + string current_source() { return current_source_; } + + private: + int reachable_count_ = 0; + int unreachable_count_ = 0; + + string current_source_{}; + + QuicUnorderedMap<int, int> read_errors_; + QuicUnorderedMap<int, int> write_errors_; +}; + +class IcmpReachableTest : public QuicTest { + public: + IcmpReachableTest() { + CHECK(source_.FromString(kSourceAddress)); + CHECK(destination_.FromString(kDestinationAddress)); + + int pipe_fds[2]; + CHECK(pipe(pipe_fds) >= 0) << "pipe() failed"; + + read_fd_ = pipe_fds[0]; + read_src_fd_ = pipe_fds[1]; + } + + void SetFdExpectations() { + InSequence seq; + EXPECT_CALL(kernel_, socket(_, _, _)).WillOnce(Return(kFakeWriteFd)); + EXPECT_CALL(kernel_, bind(kFakeWriteFd, _, _)).WillOnce(Return(0)); + + EXPECT_CALL(kernel_, socket(_, _, _)).WillOnce(Return(read_fd_)); + EXPECT_CALL(kernel_, bind(read_fd_, _, _)).WillOnce(Return(0)); + + EXPECT_CALL(kernel_, setsockopt(read_fd_, SOL_ICMPV6, ICMP6_FILTER, _, _)); + + EXPECT_CALL(kernel_, close(read_fd_)).WillOnce(Invoke([](int fd) { + return close(fd); + })); + } + + protected: + QuicIpAddress source_; + QuicIpAddress destination_; + + int read_fd_; + int read_src_fd_; + + StrictMock<MockKernel> kernel_; + QuicEpollServer epoll_server_; + StatsInterface stats_; +}; + +TEST_F(IcmpReachableTest, SendsPings) { + IcmpReachable reachable(source_, destination_, absl::Seconds(0), &kernel_, + &epoll_server_, &stats_); + + SetFdExpectations(); + ASSERT_TRUE(reachable.Init()); + + EXPECT_CALL(kernel_, sendto(kFakeWriteFd, _, _, _, _, _)) + .WillOnce(Invoke([](int sockfd, const void* buf, size_t len, int flags, + const struct sockaddr* dest_addr, socklen_t addrlen) { + auto icmp_header = GetHeaderFromPacket(buf, len); + EXPECT_EQ(icmp_header.icmp6_type, ICMP6_ECHO_REQUEST); + EXPECT_EQ(icmp_header.icmp6_seq, 1); + return len; + })); + + epoll_server_.WaitForEventsAndExecuteCallbacks(); + EXPECT_FALSE(stats_.HasWriteErrors()); + + epoll_server_.Shutdown(); +} + +TEST_F(IcmpReachableTest, HandlesUnreachableEvents) { + IcmpReachable reachable(source_, destination_, absl::Seconds(0), &kernel_, + &epoll_server_, &stats_); + + SetFdExpectations(); + ASSERT_TRUE(reachable.Init()); + + EXPECT_CALL(kernel_, sendto(kFakeWriteFd, _, _, _, _, _)) + .Times(2) + .WillRepeatedly(Invoke([](int sockfd, const void* buf, size_t len, + int flags, const struct sockaddr* dest_addr, + socklen_t addrlen) { return len; })); + + epoll_server_.WaitForEventsAndExecuteCallbacks(); + EXPECT_EQ(stats_.unreachable_count(), 0); + + epoll_server_.WaitForEventsAndExecuteCallbacks(); + EXPECT_FALSE(stats_.HasWriteErrors()); + EXPECT_EQ(stats_.unreachable_count(), 1); + EXPECT_EQ(stats_.current_source(), kNoSource); + + epoll_server_.Shutdown(); +} + +TEST_F(IcmpReachableTest, HandlesReachableEvents) { + IcmpReachable reachable(source_, destination_, absl::Seconds(0), &kernel_, + &epoll_server_, &stats_); + + SetFdExpectations(); + ASSERT_TRUE(reachable.Init()); + + icmp6_hdr last_request_hdr{}; + EXPECT_CALL(kernel_, sendto(kFakeWriteFd, _, _, _, _, _)) + .Times(2) + .WillRepeatedly( + Invoke([&last_request_hdr]( + int sockfd, const void* buf, size_t len, int flags, + const struct sockaddr* dest_addr, socklen_t addrlen) { + last_request_hdr = GetHeaderFromPacket(buf, len); + return len; + })); + + sockaddr_in6 source_addr{}; + string packed_source = source_.ToPackedString(); + memcpy(&source_addr.sin6_addr, packed_source.data(), packed_source.size()); + + EXPECT_CALL(kernel_, recvfrom(read_fd_, _, _, _, _, _)) + .WillOnce( + Invoke([&source_addr](int sockfd, void* buf, size_t len, int flags, + struct sockaddr* src_addr, socklen_t* addrlen) { + *reinterpret_cast<sockaddr_in6*>(src_addr) = source_addr; + return read(sockfd, buf, len); + })); + + epoll_server_.WaitForEventsAndExecuteCallbacks(); + EXPECT_EQ(stats_.reachable_count(), 0); + + icmp6_hdr response = last_request_hdr; + response.icmp6_type = ICMP6_ECHO_REPLY; + + write(read_src_fd_, reinterpret_cast<const void*>(&response), + sizeof(icmp6_hdr)); + + epoll_server_.WaitForEventsAndExecuteCallbacks(); + EXPECT_FALSE(stats_.HasReadErrors()); + EXPECT_FALSE(stats_.HasWriteErrors()); + EXPECT_EQ(stats_.reachable_count(), 1); + EXPECT_EQ(stats_.current_source(), source_.ToString()); + + epoll_server_.Shutdown(); +} + +TEST_F(IcmpReachableTest, HandlesWriteErrors) { + IcmpReachable reachable(source_, destination_, absl::Seconds(0), &kernel_, + &epoll_server_, &stats_); + + SetFdExpectations(); + ASSERT_TRUE(reachable.Init()); + + EXPECT_CALL(kernel_, sendto(kFakeWriteFd, _, _, _, _, _)) + .WillOnce(Invoke([](int sockfd, const void* buf, size_t len, int flags, + const struct sockaddr* dest_addr, socklen_t addrlen) { + errno = EAGAIN; + return 0; + })); + + epoll_server_.WaitForEventsAndExecuteCallbacks(); + EXPECT_EQ(stats_.WriteErrorCount(EAGAIN), 1); + + epoll_server_.Shutdown(); +} + +TEST_F(IcmpReachableTest, HandlesReadErrors) { + IcmpReachable reachable(source_, destination_, absl::Seconds(0), &kernel_, + &epoll_server_, &stats_); + + SetFdExpectations(); + ASSERT_TRUE(reachable.Init()); + + EXPECT_CALL(kernel_, sendto(kFakeWriteFd, _, _, _, _, _)) + .WillOnce(Invoke([](int sockfd, const void* buf, size_t len, int flags, + const struct sockaddr* dest_addr, + socklen_t addrlen) { return len; })); + + EXPECT_CALL(kernel_, recvfrom(read_fd_, _, _, _, _, _)) + .WillOnce(Invoke([](int sockfd, void* buf, size_t len, int flags, + struct sockaddr* src_addr, socklen_t* addrlen) { + errno = EIO; + return -1; + })); + + icmp6_hdr response{}; + + write(read_src_fd_, reinterpret_cast<const void*>(&response), + sizeof(icmp6_hdr)); + + epoll_server_.WaitForEventsAndExecuteCallbacks(); + EXPECT_EQ(stats_.reachable_count(), 0); + EXPECT_EQ(stats_.ReadErrorCount(EIO), 1); + + epoll_server_.Shutdown(); +} + +} // namespace +} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/qbone/bonnet/mock_icmp_reachable.h b/chromium/net/third_party/quiche/src/quic/qbone/bonnet/mock_icmp_reachable.h new file mode 100644 index 00000000000..092845eba7d --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/bonnet/mock_icmp_reachable.h @@ -0,0 +1,20 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef QUICHE_QUIC_QBONE_BONNET_MOCK_ICMP_REACHABLE_H_ +#define QUICHE_QUIC_QBONE_BONNET_MOCK_ICMP_REACHABLE_H_ + +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/qbone/bonnet/icmp_reachable_interface.h" + +namespace quic { + +class MockIcmpReachable : public IcmpReachableInterface { + public: + MOCK_METHOD0(Init, bool()); +}; + +} // namespace quic + +#endif // QUICHE_QUIC_QBONE_BONNET_MOCK_ICMP_REACHABLE_H_ diff --git a/chromium/net/third_party/quiche/src/quic/qbone/bonnet/mock_tun_device.h b/chromium/net/third_party/quiche/src/quic/qbone/bonnet/mock_tun_device.h new file mode 100644 index 00000000000..37e852af515 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/bonnet/mock_tun_device.h @@ -0,0 +1,26 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef QUICHE_QUIC_QBONE_BONNET_MOCK_TUN_DEVICE_H_ +#define QUICHE_QUIC_QBONE_BONNET_MOCK_TUN_DEVICE_H_ + +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/qbone/bonnet/tun_device_interface.h" + +namespace quic { + +class MockTunDevice : public TunDeviceInterface { + public: + MOCK_METHOD0(Init, bool()); + + MOCK_METHOD0(Up, bool()); + + MOCK_METHOD0(Down, bool()); + + MOCK_CONST_METHOD0(GetFileDescriptor, int()); +}; + +} // namespace quic + +#endif // QUICHE_QUIC_QBONE_BONNET_MOCK_TUN_DEVICE_H_ diff --git a/chromium/net/third_party/quiche/src/quic/qbone/bonnet/tun_device.cc b/chromium/net/third_party/quiche/src/quic/qbone/bonnet/tun_device.cc new file mode 100644 index 00000000000..6c0a8a55ed8 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/bonnet/tun_device.cc @@ -0,0 +1,201 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/third_party/quiche/src/quic/qbone/bonnet/tun_device.h" + +#include <fcntl.h> +#include <linux/if_tun.h> +#include <net/if.h> +#include <sys/ioctl.h> +#include <sys/socket.h> + +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/qbone/platform/kernel_interface.h" + +namespace quic { + +const char kTapTunDevicePath[] = "/dev/net/tun"; +const int kInvalidFd = -1; + +TunDevice::TunDevice(const string& interface_name, + int mtu, + bool persist, + KernelInterface* kernel) + : interface_name_(interface_name), + mtu_(mtu), + persist_(persist), + file_descriptor_(kInvalidFd), + kernel_(*kernel) {} + +TunDevice::~TunDevice() { + Down(); + CleanUpFileDescriptor(); +} + +bool TunDevice::Init() { + if (interface_name_.empty() || interface_name_.size() >= IFNAMSIZ) { + QUIC_BUG << "interface_name must be nonempty and shorter than " << IFNAMSIZ; + return false; + } + + if (!OpenDevice()) { + return false; + } + + if (!ConfigureInterface()) { + return false; + } + + return true; +} + +// TODO(pengg): might be better to use netlink socket, once we have a library to +// use +bool TunDevice::Up() { + if (!is_interface_up_) { + struct ifreq if_request; + memset(&if_request, 0, sizeof(if_request)); + // copy does not zero-terminate the result string, but we've memset the + // entire struct. + interface_name_.copy(if_request.ifr_name, IFNAMSIZ); + if_request.ifr_flags = IFF_UP; + + is_interface_up_ = + NetdeviceIoctl(SIOCSIFFLAGS, reinterpret_cast<void*>(&if_request)); + return is_interface_up_; + } else { + return true; + } +} + +// TODO(pengg): might be better to use netlink socket, once we have a library to +// use +bool TunDevice::Down() { + if (is_interface_up_) { + struct ifreq if_request; + memset(&if_request, 0, sizeof(if_request)); + // copy does not zero-terminate the result string, but we've memset the + // entire struct. + interface_name_.copy(if_request.ifr_name, IFNAMSIZ); + if_request.ifr_flags = 0; + + is_interface_up_ = + !NetdeviceIoctl(SIOCSIFFLAGS, reinterpret_cast<void*>(&if_request)); + return !is_interface_up_; + } else { + return true; + } +} + +int TunDevice::GetFileDescriptor() const { + return file_descriptor_; +} + +bool TunDevice::OpenDevice() { + struct ifreq if_request; + memset(&if_request, 0, sizeof(if_request)); + // copy does not zero-terminate the result string, but we've memset the entire + // struct. + interface_name_.copy(if_request.ifr_name, IFNAMSIZ); + + // Always set IFF_MULTI_QUEUE since a persistent device does not allow this + // flag to be flipped when re-opening it. The only way to flip this flag is to + // destroy the device and create a new one, but that deletes any existing + // routing associated with the interface, which makes the meaning of the + // 'persist' bit ambiguous. + if_request.ifr_flags = IFF_TUN | IFF_MULTI_QUEUE | IFF_NO_PI; + + // TODO(pengg): port MakeCleanup to quic/platform? This makes the call to + // CleanUpFileDescriptor nicer and less error-prone. + // When the device is running with IFF_MULTI_QUEUE set, each call to open will + // create a queue which can be used to read/write packets from/to the device. + int fd = kernel_.open(kTapTunDevicePath, O_RDWR); + if (fd < 0) { + QUIC_PLOG(WARNING) << "Failed to open " << kTapTunDevicePath; + CleanUpFileDescriptor(); + return false; + } + file_descriptor_ = fd; + if (!CheckFeatures(fd)) { + CleanUpFileDescriptor(); + return false; + } + + if (kernel_.ioctl(fd, TUNSETIFF, reinterpret_cast<void*>(&if_request)) != 0) { + QUIC_PLOG(WARNING) << "Failed to TUNSETIFF on fd(" << fd << ")"; + CleanUpFileDescriptor(); + return false; + } + + if (kernel_.ioctl( + fd, TUNSETPERSIST, + persist_ ? reinterpret_cast<void*>(&if_request) : nullptr) != 0) { + QUIC_PLOG(WARNING) << "Failed to TUNSETPERSIST on fd(" << fd << ")"; + CleanUpFileDescriptor(); + return false; + } + + return true; +} + +// TODO(pengg): might be better to use netlink socket, once we have a library to +// use +bool TunDevice::ConfigureInterface() { + struct ifreq if_request; + memset(&if_request, 0, sizeof(if_request)); + // copy does not zero-terminate the result string, but we've memset the entire + // struct. + interface_name_.copy(if_request.ifr_name, IFNAMSIZ); + if_request.ifr_mtu = mtu_; + + if (!NetdeviceIoctl(SIOCSIFMTU, reinterpret_cast<void*>(&if_request))) { + CleanUpFileDescriptor(); + return false; + } + + return true; +} + +bool TunDevice::CheckFeatures(int tun_device_fd) { + unsigned int actual_features; + if (kernel_.ioctl(tun_device_fd, TUNGETFEATURES, &actual_features) != 0) { + QUIC_PLOG(WARNING) << "Failed to TUNGETFEATURES"; + return false; + } + unsigned int required_features = IFF_TUN | IFF_NO_PI; + if ((required_features & actual_features) != required_features) { + QUIC_LOG(WARNING) + << "Required feature does not exist. required_features: 0x" << std::hex + << required_features << " vs actual_features: 0x" << std::hex + << actual_features; + return false; + } + return true; +} + +bool TunDevice::NetdeviceIoctl(int request, void* argp) { + int fd = kernel_.socket(AF_INET6, SOCK_DGRAM, 0); + if (fd < 0) { + QUIC_PLOG(WARNING) << "Failed to create AF_INET6 socket."; + return false; + } + + if (kernel_.ioctl(fd, request, argp) != 0) { + QUIC_PLOG(WARNING) << "Failed ioctl request: " << request; + kernel_.close(fd); + return false; + } + kernel_.close(fd); + return true; +} + +void TunDevice::CleanUpFileDescriptor() { + if (file_descriptor_ != kInvalidFd) { + kernel_.close(file_descriptor_); + file_descriptor_ = kInvalidFd; + } +} + +} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/qbone/bonnet/tun_device.h b/chromium/net/third_party/quiche/src/quic/qbone/bonnet/tun_device.h new file mode 100644 index 00000000000..1828b81a1b0 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/bonnet/tun_device.h @@ -0,0 +1,82 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef QUICHE_QUIC_QBONE_BONNET_TUN_DEVICE_H_ +#define QUICHE_QUIC_QBONE_BONNET_TUN_DEVICE_H_ + +#include <string> +#include <vector> + +#include "net/third_party/quiche/src/quic/qbone/bonnet/tun_device_interface.h" +#include "net/third_party/quiche/src/quic/qbone/platform/kernel_interface.h" + +namespace quic { + +class TunDevice : public TunDeviceInterface { + public: + // This represents a tun device created in the OS kernel, which is a virtual + // network interface that any packets sent to it can be read by a user space + // program that owns it. The routing rule that routes packets to this + // interface should be defined somewhere else. + // + // Standard read/write system calls can be used to receive/send packets + // from/to this interface. The file descriptor is owned by this class. + // + // If persist is set to true, the device won't be deleted even after + // destructing. The device will be picked up when initializing this class with + // the same interface_name on the next time. + // + // Persisting the device is useful if one wants to keep the routing rules + // since once a tun device is destroyed by the kernel, all the associated + // routing rules go away. + // + // The caller should own kernel and make sure it outlives this. + TunDevice(const string& interface_name, + int mtu, + bool persist, + KernelInterface* kernel); + + ~TunDevice() override; + + // Actually creates/reopens and configures the device. + bool Init() override; + + // Marks the interface up to start receiving packets. + bool Up() override; + + // Marks the interface down to stop receiving packets. + bool Down() override; + + // Gets the file descriptor that can be used to send/receive packets. + // This returns -1 when the TUN device is in an invalid state. + int GetFileDescriptor() const override; + + private: + // Creates or reopens the tun device. + bool OpenDevice(); + + // Configure the interface. + bool ConfigureInterface(); + + // Checks if the required kernel features exists. + bool CheckFeatures(int tun_device_fd); + + // Closes the opened file descriptor and makes sure the file descriptor + // is no longer available from GetFileDescriptor; + void CleanUpFileDescriptor(); + + // Opens a socket and makes netdevice ioctl call + bool NetdeviceIoctl(int request, void* argp); + + const string interface_name_; + const int mtu_; + const bool persist_; + int file_descriptor_; + KernelInterface& kernel_; + bool is_interface_up_ = false; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_QBONE_BONNET_TUN_DEVICE_H_ diff --git a/chromium/net/third_party/quiche/src/quic/qbone/bonnet/tun_device_interface.h b/chromium/net/third_party/quiche/src/quic/qbone/bonnet/tun_device_interface.h new file mode 100644 index 00000000000..e99c547ee7c --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/bonnet/tun_device_interface.h @@ -0,0 +1,33 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef QUICHE_QUIC_QBONE_BONNET_TUN_DEVICE_INTERFACE_H_ +#define QUICHE_QUIC_QBONE_BONNET_TUN_DEVICE_INTERFACE_H_ + +#include <vector> + +namespace quic { + +// An interface with methods for interacting with a TUN device. +class TunDeviceInterface { + public: + virtual ~TunDeviceInterface() {} + + // Actually creates/reopens and configures the device. + virtual bool Init() = 0; + + // Marks the interface up to start receiving packets. + virtual bool Up() = 0; + + // Marks the interface down to stop receiving packets. + virtual bool Down() = 0; + + // Gets the file descriptor that can be used to send/receive packets. + // This returns -1 when the TUN device is in an invalid state. + virtual int GetFileDescriptor() const = 0; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_QBONE_BONNET_TUN_DEVICE_INTERFACE_H_ diff --git a/chromium/net/third_party/quiche/src/quic/qbone/bonnet/tun_device_packet_exchanger.cc b/chromium/net/third_party/quiche/src/quic/qbone/bonnet/tun_device_packet_exchanger.cc new file mode 100644 index 00000000000..1d246a21dc6 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/bonnet/tun_device_packet_exchanger.cc @@ -0,0 +1,82 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/third_party/quiche/src/quic/qbone/bonnet/tun_device_packet_exchanger.h" + +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h" + +namespace quic { + +TunDevicePacketExchanger::TunDevicePacketExchanger( + int fd, + size_t mtu, + KernelInterface* kernel, + QbonePacketExchanger::Visitor* visitor, + size_t max_pending_packets, StatsInterface* stats) + : QbonePacketExchanger(visitor, max_pending_packets), + fd_(fd), + mtu_(mtu), + kernel_(kernel), + stats_(stats) {} + +bool TunDevicePacketExchanger::WritePacket(const char* packet, + size_t size, + bool* blocked, + string* error) { + *blocked = false; + if (fd_ < 0) { + *error = QuicStrCat("Invalid file descriptor of the TUN device: ", fd_); + stats_->OnWriteError(error); + return false; + } + + int result = kernel_->write(fd_, packet, size); + if (result == -1) { + if (errno == EWOULDBLOCK || errno == EAGAIN) { + // The tunnel is blocked. Note that this does not mean the receive buffer + // of a TCP connection is filled. This simply means the TUN device itself + // is blocked on handing packets to the rest part of the kernel. + *error = QuicStrCat("Write to the TUN device was blocked: ", errno); + *blocked = true; + stats_->OnWriteError(error); + } + return false; + } + stats_->OnPacketWritten(); + + return true; +} + +std::unique_ptr<QuicData> TunDevicePacketExchanger::ReadPacket(bool* blocked, + string* error) { + *blocked = false; + if (fd_ < 0) { + *error = QuicStrCat("Invalid file descriptor of the TUN device: ", fd_); + stats_->OnReadError(error); + return nullptr; + } + // Reading on a TUN device returns a packet at a time. If the packet is longer + // than the buffer, it's truncated. + auto read_buffer = QuicMakeUnique<char[]>(mtu_); + int result = kernel_->read(fd_, read_buffer.get(), mtu_); + // Note that 0 means end of file, but we're talking about a TUN device - there + // is no end of file. Therefore 0 also indicates error. + if (result <= 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + *error = QuicStrCat("Read from the TUN device was blocked: ", errno); + *blocked = true; + stats_->OnReadError(error); + } + return nullptr; + } + stats_->OnPacketRead(); + return QuicMakeUnique<QuicData>(read_buffer.release(), result, true); +} + +int TunDevicePacketExchanger::file_descriptor() const { + return fd_; +} + +} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/qbone/bonnet/tun_device_packet_exchanger.h b/chromium/net/third_party/quiche/src/quic/qbone/bonnet/tun_device_packet_exchanger.h new file mode 100644 index 00000000000..12d9efa714a --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/bonnet/tun_device_packet_exchanger.h @@ -0,0 +1,72 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef QUICHE_QUIC_QBONE_BONNET_TUN_DEVICE_PACKET_EXCHANGER_H_ +#define QUICHE_QUIC_QBONE_BONNET_TUN_DEVICE_PACKET_EXCHANGER_H_ + +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/qbone/platform/kernel_interface.h" +#include "net/third_party/quiche/src/quic/qbone/qbone_client_interface.h" +#include "net/third_party/quiche/src/quic/qbone/qbone_packet_exchanger.h" + +namespace quic { + +class TunDevicePacketExchanger : public QbonePacketExchanger { + public: + class StatsInterface { + public: + StatsInterface() = default; + + StatsInterface(const StatsInterface&) = delete; + StatsInterface& operator=(const StatsInterface&) = delete; + + StatsInterface(StatsInterface&&) = delete; + StatsInterface& operator=(StatsInterface&&) = delete; + + virtual ~StatsInterface() = default; + + virtual void OnPacketRead() = 0; + virtual void OnPacketWritten() = 0; + virtual void OnReadError(string* error) = 0; + virtual void OnWriteError(string* error) = 0; + }; + + // |fd| is a open file descriptor on a TUN device that's opened for both read + // and write. + // |mtu| is the mtu of the TUN device. + // |kernel| is not owned but should out live objects of this class. + // |visitor| is not owned but should out live objects of this class. + // |max_pending_packets| controls the number of packets to be queued should + // the TUN device become blocked. + // |stats| is notified about packet read/write statistics. It is not owned, + // but should outlive objects of this class. + TunDevicePacketExchanger(int fd, + size_t mtu, + KernelInterface* kernel, + QbonePacketExchanger::Visitor* visitor, + size_t max_pending_packets, + StatsInterface* stats); + + int file_descriptor() const; + + private: + // From QbonePacketExchanger. + std::unique_ptr<QuicData> ReadPacket(bool* blocked, string* error) override; + + // From QbonePacketExchanger. + bool WritePacket(const char* packet, + size_t size, + bool* blocked, + string* error) override; + + int fd_ = -1; + size_t mtu_; + KernelInterface* kernel_; + + StatsInterface* stats_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_QBONE_BONNET_TUN_DEVICE_PACKET_EXCHANGER_H_ diff --git a/chromium/net/third_party/quiche/src/quic/qbone/bonnet/tun_device_packet_exchanger_test.cc b/chromium/net/third_party/quiche/src/quic/qbone/bonnet/tun_device_packet_exchanger_test.cc new file mode 100644 index 00000000000..026ec26da5e --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/bonnet/tun_device_packet_exchanger_test.cc @@ -0,0 +1,128 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/third_party/quiche/src/quic/qbone/bonnet/tun_device_packet_exchanger.h" + +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/qbone/mock_qbone_client.h" +#include "net/third_party/quiche/src/quic/qbone/platform/mock_kernel.h" + +namespace quic { +namespace { + +const size_t kMtu = 1000; +const size_t kMaxPendingPackets = 5; +const int kFd = 15; + +using ::testing::_; +using ::testing::Invoke; +using ::testing::StrEq; +using ::testing::StrictMock; + +class MockVisitor : public QbonePacketExchanger::Visitor { + public: + MOCK_METHOD1(OnReadError, void(const string&)); + MOCK_METHOD1(OnWriteError, void(const string&)); +}; + +class MockStatsInterface : public TunDevicePacketExchanger::StatsInterface { + public: + MOCK_METHOD0(OnPacketRead, void()); + MOCK_METHOD0(OnPacketWritten, void()); + + MOCK_METHOD1(OnReadError, void(string*)); + MOCK_METHOD1(OnWriteError, void(string*)); +}; + +class TunDevicePacketExchangerTest : public QuicTest { + protected: + TunDevicePacketExchangerTest() + : exchanger_(kFd, + kMtu, + &mock_kernel_, + &mock_visitor_, + kMaxPendingPackets, + &mock_stats_) {} + + ~TunDevicePacketExchangerTest() override {} + + MockKernel mock_kernel_; + StrictMock<MockVisitor> mock_visitor_; + StrictMock<MockQboneClient> mock_client_; + StrictMock<MockStatsInterface> mock_stats_; + TunDevicePacketExchanger exchanger_; +}; + +TEST_F(TunDevicePacketExchangerTest, WritePacketReturnsFalseOnError) { + string packet = "fake packet"; + EXPECT_CALL(mock_kernel_, write(kFd, _, packet.size())) + .WillOnce(Invoke([](int fd, const void* buf, size_t count) { + errno = ECOMM; + return -1; + })); + + EXPECT_CALL(mock_visitor_, OnWriteError(_)); + exchanger_.WritePacketToNetwork(packet.data(), packet.size()); +} + +TEST_F(TunDevicePacketExchangerTest, + WritePacketReturnFalseAndBlockedOnBlockedTunnel) { + string packet = "fake packet"; + EXPECT_CALL(mock_kernel_, write(kFd, _, packet.size())) + .WillOnce(Invoke([](int fd, const void* buf, size_t count) { + errno = EAGAIN; + return -1; + })); + + EXPECT_CALL(mock_stats_, OnWriteError(_)).Times(1); + exchanger_.WritePacketToNetwork(packet.data(), packet.size()); +} + +TEST_F(TunDevicePacketExchangerTest, WritePacketReturnsTrueOnSuccessfulWrite) { + string packet = "fake packet"; + EXPECT_CALL(mock_kernel_, write(kFd, _, packet.size())) + .WillOnce(Invoke([packet](int fd, const void* buf, size_t count) { + EXPECT_THAT(reinterpret_cast<const char*>(buf), StrEq(packet)); + return count; + })); + + EXPECT_CALL(mock_stats_, OnPacketWritten()).Times(1); + exchanger_.WritePacketToNetwork(packet.data(), packet.size()); +} + +TEST_F(TunDevicePacketExchangerTest, ReadPacketReturnsNullOnError) { + EXPECT_CALL(mock_kernel_, read(kFd, _, kMtu)) + .WillOnce(Invoke([](int fd, void* buf, size_t count) { + errno = ECOMM; + return -1; + })); + EXPECT_CALL(mock_visitor_, OnReadError(_)); + exchanger_.ReadAndDeliverPacket(&mock_client_); +} + +TEST_F(TunDevicePacketExchangerTest, ReadPacketReturnsNullOnBlockedRead) { + EXPECT_CALL(mock_kernel_, read(kFd, _, kMtu)) + .WillOnce(Invoke([](int fd, void* buf, size_t count) { + errno = EAGAIN; + return -1; + })); + EXPECT_CALL(mock_stats_, OnReadError(_)).Times(1); + EXPECT_FALSE(exchanger_.ReadAndDeliverPacket(&mock_client_)); +} + +TEST_F(TunDevicePacketExchangerTest, + ReadPacketReturnsThePacketOnSuccessfulRead) { + string packet = "fake_packet"; + EXPECT_CALL(mock_kernel_, read(kFd, _, kMtu)) + .WillOnce(Invoke([packet](int fd, void* buf, size_t count) { + memcpy(buf, packet.data(), packet.size()); + return packet.size(); + })); + EXPECT_CALL(mock_client_, ProcessPacketFromNetwork(StrEq(packet))); + EXPECT_CALL(mock_stats_, OnPacketRead()).Times(1); + EXPECT_TRUE(exchanger_.ReadAndDeliverPacket(&mock_client_)); +} + +} // namespace +} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/qbone/bonnet/tun_device_test.cc b/chromium/net/third_party/quiche/src/quic/qbone/bonnet/tun_device_test.cc new file mode 100644 index 00000000000..e9ae4d336e7 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/bonnet/tun_device_test.cc @@ -0,0 +1,208 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/third_party/quiche/src/quic/qbone/bonnet/tun_device.h" + +#include <linux/if.h> +#include <linux/if_tun.h> +#include <sys/ioctl.h> + +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/qbone/platform/mock_kernel.h" + +namespace quic { +namespace { + +using ::testing::_; +using ::testing::AnyNumber; +using ::testing::Invoke; +using ::testing::Return; +using ::testing::StrEq; +using ::testing::Unused; + +const char kDeviceName[] = "tun0"; +const int kSupportedFeatures = + IFF_TUN | IFF_TAP | IFF_MULTI_QUEUE | IFF_ONE_QUEUE | IFF_NO_PI; + +// Quite a bit of EXPECT_CALL().Times(AnyNumber()).WillRepeatedly() are used to +// make sure we can correctly set common expectations and override the +// expectation with later call to EXPECT_CALL(). ON_CALL cannot be used here +// since when EPXECT_CALL overrides ON_CALL, it ignores the parameter matcher +// which results in unexpected call even if ON_CALL exists. +class TunDeviceTest : public QuicTest { + protected: + void SetUp() override { + EXPECT_CALL(mock_kernel_, socket(AF_INET6, _, _)) + .Times(AnyNumber()) + .WillRepeatedly(Invoke([this](Unused, Unused, Unused) { + EXPECT_CALL(mock_kernel_, close(next_fd_)).WillOnce(Return(0)); + return next_fd_++; + })); + } + + // Set the expectations for calling Init(). + void SetInitExpectations(int mtu, bool persist) { + EXPECT_CALL(mock_kernel_, open(StrEq("/dev/net/tun"), _)) + .Times(AnyNumber()) + .WillRepeatedly(Invoke([this](Unused, Unused) { + EXPECT_CALL(mock_kernel_, close(next_fd_)).WillOnce(Return(0)); + return next_fd_++; + })); + EXPECT_CALL(mock_kernel_, ioctl(_, TUNGETFEATURES, _)) + .Times(AnyNumber()) + .WillRepeatedly(Invoke([](Unused, Unused, void* argp) { + auto* actual_flags = reinterpret_cast<int*>(argp); + *actual_flags = kSupportedFeatures; + return 0; + })); + EXPECT_CALL(mock_kernel_, ioctl(_, TUNSETIFF, _)) + .Times(AnyNumber()) + .WillRepeatedly(Invoke([](Unused, Unused, void* argp) { + auto* ifr = reinterpret_cast<struct ifreq*>(argp); + EXPECT_EQ(IFF_TUN | IFF_MULTI_QUEUE | IFF_NO_PI, ifr->ifr_flags); + EXPECT_THAT(ifr->ifr_name, StrEq(kDeviceName)); + return 0; + })); + EXPECT_CALL(mock_kernel_, ioctl(_, TUNSETPERSIST, _)) + .Times(AnyNumber()) + .WillRepeatedly(Invoke([persist](Unused, Unused, void* argp) { + auto* ifr = reinterpret_cast<struct ifreq*>(argp); + if (persist) { + EXPECT_THAT(ifr->ifr_name, StrEq(kDeviceName)); + } else { + EXPECT_EQ(nullptr, ifr); + } + return 0; + })); + EXPECT_CALL(mock_kernel_, ioctl(_, SIOCSIFMTU, _)) + .Times(AnyNumber()) + .WillRepeatedly(Invoke([mtu](Unused, Unused, void* argp) { + auto* ifr = reinterpret_cast<struct ifreq*>(argp); + EXPECT_EQ(mtu, ifr->ifr_mtu); + EXPECT_THAT(ifr->ifr_name, StrEq(kDeviceName)); + return 0; + })); + } + + // Expect that Up() will be called. Force the call to fail when fail == true. + void ExpectUp(bool fail) { + EXPECT_CALL(mock_kernel_, ioctl(_, SIOCSIFFLAGS, _)) + .WillOnce(Invoke([fail](Unused, Unused, void* argp) { + auto* ifr = reinterpret_cast<struct ifreq*>(argp); + EXPECT_TRUE(ifr->ifr_flags & IFF_UP); + EXPECT_THAT(ifr->ifr_name, StrEq(kDeviceName)); + if (fail) { + return -1; + } else { + return 0; + } + })); + } + + // Expect that Down() will be called *after* the interface is up. Force the + // call to fail when fail == true. + void ExpectDown(bool fail) { + EXPECT_CALL(mock_kernel_, ioctl(_, SIOCSIFFLAGS, _)) + .WillOnce(Invoke([fail](Unused, Unused, void* argp) { + auto* ifr = reinterpret_cast<struct ifreq*>(argp); + EXPECT_FALSE(ifr->ifr_flags & IFF_UP); + EXPECT_THAT(ifr->ifr_name, StrEq(kDeviceName)); + if (fail) { + return -1; + } else { + return 0; + } + })); + } + + MockKernel mock_kernel_; + int next_fd_ = 100; +}; + +// A TunDevice can be initialized and up +TEST_F(TunDeviceTest, BasicWorkFlow) { + SetInitExpectations(/* mtu = */ 1500, /* persist = */ false); + TunDevice tun_device(kDeviceName, 1500, false, &mock_kernel_); + EXPECT_TRUE(tun_device.Init()); + EXPECT_GT(tun_device.GetFileDescriptor(), -1); + + ExpectUp(/* fail = */ false); + EXPECT_TRUE(tun_device.Up()); + ExpectDown(/* fail = */ false); +} + +TEST_F(TunDeviceTest, FailToOpenTunDevice) { + SetInitExpectations(/* mtu = */ 1500, /* persist = */ false); + EXPECT_CALL(mock_kernel_, open(StrEq("/dev/net/tun"), _)) + .WillOnce(Return(-1)); + TunDevice tun_device(kDeviceName, 1500, false, &mock_kernel_); + EXPECT_FALSE(tun_device.Init()); + EXPECT_EQ(tun_device.GetFileDescriptor(), -1); +} + +TEST_F(TunDeviceTest, FailToCheckFeature) { + SetInitExpectations(/* mtu = */ 1500, /* persist = */ false); + EXPECT_CALL(mock_kernel_, ioctl(_, TUNGETFEATURES, _)).WillOnce(Return(-1)); + TunDevice tun_device(kDeviceName, 1500, false, &mock_kernel_); + EXPECT_FALSE(tun_device.Init()); + EXPECT_EQ(tun_device.GetFileDescriptor(), -1); +} + +TEST_F(TunDeviceTest, TooFewFeature) { + SetInitExpectations(/* mtu = */ 1500, /* persist = */ false); + EXPECT_CALL(mock_kernel_, ioctl(_, TUNGETFEATURES, _)) + .WillOnce(Invoke([](Unused, Unused, void* argp) { + int* actual_features = reinterpret_cast<int*>(argp); + *actual_features = IFF_TUN | IFF_ONE_QUEUE; + return 0; + })); + TunDevice tun_device(kDeviceName, 1500, false, &mock_kernel_); + EXPECT_FALSE(tun_device.Init()); + EXPECT_EQ(tun_device.GetFileDescriptor(), -1); +} + +TEST_F(TunDeviceTest, FailToSetFlag) { + SetInitExpectations(/* mtu = */ 1500, /* persist = */ true); + EXPECT_CALL(mock_kernel_, ioctl(_, TUNSETIFF, _)).WillOnce(Return(-1)); + TunDevice tun_device(kDeviceName, 1500, true, &mock_kernel_); + EXPECT_FALSE(tun_device.Init()); + EXPECT_EQ(tun_device.GetFileDescriptor(), -1); +} + +TEST_F(TunDeviceTest, FailToPersistDevice) { + SetInitExpectations(/* mtu = */ 1500, /* persist = */ true); + EXPECT_CALL(mock_kernel_, ioctl(_, TUNSETPERSIST, _)).WillOnce(Return(-1)); + TunDevice tun_device(kDeviceName, 1500, true, &mock_kernel_); + EXPECT_FALSE(tun_device.Init()); + EXPECT_EQ(tun_device.GetFileDescriptor(), -1); +} + +TEST_F(TunDeviceTest, FailToOpenSocket) { + SetInitExpectations(/* mtu = */ 1500, /* persist = */ true); + EXPECT_CALL(mock_kernel_, socket(AF_INET6, _, _)).WillOnce(Return(-1)); + TunDevice tun_device(kDeviceName, 1500, true, &mock_kernel_); + EXPECT_FALSE(tun_device.Init()); + EXPECT_EQ(tun_device.GetFileDescriptor(), -1); +} + +TEST_F(TunDeviceTest, FailToSetMtu) { + SetInitExpectations(/* mtu = */ 1500, /* persist = */ true); + EXPECT_CALL(mock_kernel_, ioctl(_, SIOCSIFMTU, _)).WillOnce(Return(-1)); + TunDevice tun_device(kDeviceName, 1500, true, &mock_kernel_); + EXPECT_FALSE(tun_device.Init()); + EXPECT_EQ(tun_device.GetFileDescriptor(), -1); +} + +TEST_F(TunDeviceTest, FailToUp) { + SetInitExpectations(/* mtu = */ 1500, /* persist = */ true); + TunDevice tun_device(kDeviceName, 1500, true, &mock_kernel_); + EXPECT_TRUE(tun_device.Init()); + EXPECT_GT(tun_device.GetFileDescriptor(), -1); + + ExpectUp(/* fail = */ true); + EXPECT_FALSE(tun_device.Up()); +} + +} // namespace +} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/qbone/mock_qbone_client.h b/chromium/net/third_party/quiche/src/quic/qbone/mock_qbone_client.h new file mode 100644 index 00000000000..37df26d5f49 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/mock_qbone_client.h @@ -0,0 +1,20 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef QUICHE_QUIC_QBONE_MOCK_QBONE_CLIENT_H_ +#define QUICHE_QUIC_QBONE_MOCK_QBONE_CLIENT_H_ + +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/qbone/qbone_client_interface.h" + +namespace quic { + +class MockQboneClient : public QboneClientInterface { + public: + MOCK_METHOD1(ProcessPacketFromNetwork, void(QuicStringPiece packet)); +}; + +} // namespace quic + +#endif // QUICHE_QUIC_QBONE_MOCK_QBONE_CLIENT_H_ diff --git a/chromium/net/third_party/quiche/src/quic/qbone/mock_qbone_server_session.h b/chromium/net/third_party/quiche/src/quic/qbone/mock_qbone_server_session.h new file mode 100644 index 00000000000..652c017f64c --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/mock_qbone_server_session.h @@ -0,0 +1,36 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef QUICHE_QUIC_QBONE_MOCK_QBONE_SERVER_SESSION_H_ +#define QUICHE_QUIC_QBONE_MOCK_QBONE_SERVER_SESSION_H_ + +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/qbone/qbone_server_session.h" + +namespace quic { + +class MockQboneServerSession : public QboneServerSession { + public: + explicit MockQboneServerSession(QuicConnection* connection) + : QboneServerSession(CurrentSupportedVersions(), + connection, + /*owner=*/nullptr, + /*config=*/{}, + /*quic_crypto_server_config=*/nullptr, + /*compressed_certs_cache=*/nullptr, + /*writer=*/nullptr, + /*self_ip=*/QuicIpAddress::Loopback6(), + /*client_ip=*/QuicIpAddress::Loopback6(), + /*client_ip_subnet_length=*/0, + /*handler=*/nullptr) {} + + MOCK_METHOD1(SendClientRequest, bool(const QboneClientRequest&)); + + MOCK_METHOD1(ProcessPacketFromNetwork, void(QuicStringPiece)); + MOCK_METHOD1(ProcessPacketFromPeer, void(QuicStringPiece)); +}; + +} // namespace quic + +#endif // QUICHE_QUIC_QBONE_MOCK_QBONE_SERVER_SESSION_H_ diff --git a/chromium/net/third_party/quiche/src/quic/qbone/platform/icmp_packet.cc b/chromium/net/third_party/quiche/src/quic/qbone/platform/icmp_packet.cc new file mode 100644 index 00000000000..8ba3916b951 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/platform/icmp_packet.cc @@ -0,0 +1,84 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/third_party/quiche/src/quic/qbone/platform/icmp_packet.h" + +#include <netinet/ip6.h> +#include "net/third_party/quiche/src/quic/platform/api/quic_endian.h" +#include "net/third_party/quiche/src/quic/qbone/platform/internet_checksum.h" + +namespace quic { +namespace { + +constexpr size_t kIPv6AddressSize = sizeof(in6_addr); +constexpr size_t kIPv6HeaderSize = sizeof(ip6_hdr); +constexpr size_t kICMPv6HeaderSize = sizeof(icmp6_hdr); +constexpr size_t kIPv6MinPacketSize = 1280; +constexpr size_t kIcmpTtl = 64; +constexpr size_t kICMPv6BodyMaxSize = + kIPv6MinPacketSize - kIPv6HeaderSize - kICMPv6HeaderSize; + +struct ICMPv6Packet { + ip6_hdr ip_header; + icmp6_hdr icmp_header; + uint8_t body[kICMPv6BodyMaxSize]; +}; + +// pseudo header as described in RFC 2460 Section 8.1 (excluding addresses) +struct IPv6PseudoHeader { + uint32_t payload_size{}; + uint8_t zeros[3] = {0, 0, 0}; + uint8_t next_header = IPPROTO_ICMPV6; +}; + +} // namespace + +void CreateIcmpPacket(in6_addr src, + in6_addr dst, + const icmp6_hdr& icmp_header, + QuicStringPiece body, + const std::function<void(QuicStringPiece)>& cb) { + const size_t body_size = std::min(body.size(), kICMPv6BodyMaxSize); + const size_t payload_size = kICMPv6HeaderSize + body_size; + + ICMPv6Packet icmp_packet{}; + // Set version to 6. + icmp_packet.ip_header.ip6_vfc = 0x6 << 4; + // Set the payload size, protocol and TTL. + icmp_packet.ip_header.ip6_plen = QuicEndian::HostToNet16(payload_size); + icmp_packet.ip_header.ip6_nxt = IPPROTO_ICMPV6; + icmp_packet.ip_header.ip6_hops = kIcmpTtl; + // Set the source address to the specified self IP. + icmp_packet.ip_header.ip6_src = src; + icmp_packet.ip_header.ip6_dst = dst; + + icmp_packet.icmp_header = icmp_header; + // Per RFC 4443 Section 2.3, set checksum field to 0 prior to computing it + icmp_packet.icmp_header.icmp6_cksum = 0; + + IPv6PseudoHeader pseudo_header{}; + pseudo_header.payload_size = QuicEndian::HostToNet32(payload_size); + + InternetChecksum checksum; + // Pseudoheader. + checksum.Update(icmp_packet.ip_header.ip6_src.s6_addr, kIPv6AddressSize); + checksum.Update(icmp_packet.ip_header.ip6_dst.s6_addr, kIPv6AddressSize); + checksum.Update(reinterpret_cast<char*>(&pseudo_header), + sizeof(pseudo_header)); + // ICMP header. + checksum.Update(reinterpret_cast<const char*>(&icmp_packet.icmp_header), + sizeof(icmp_packet.icmp_header)); + // Body. + checksum.Update(body.data(), body_size); + icmp_packet.icmp_header.icmp6_cksum = checksum.Value(); + + memcpy(icmp_packet.body, body.data(), body_size); + + const char* packet = reinterpret_cast<char*>(&icmp_packet); + const size_t packet_size = offsetof(ICMPv6Packet, body) + body_size; + + cb(QuicStringPiece(packet, packet_size)); +} + +} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/qbone/platform/icmp_packet.h b/chromium/net/third_party/quiche/src/quic/qbone/platform/icmp_packet.h new file mode 100644 index 00000000000..ae440d2901a --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/platform/icmp_packet.h @@ -0,0 +1,29 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef QUICHE_QUIC_QBONE_PLATFORM_ICMP_PACKET_H_ +#define QUICHE_QUIC_QBONE_PLATFORM_ICMP_PACKET_H_ + +#include <netinet/icmp6.h> +#include <netinet/in.h> + +#include <functional> + +#include "net/third_party/quiche/src/quic/platform/api/quic_ip_address.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" + +namespace quic { + +// Creates an ICMPv6 packet, returning a packed string representation of the +// packet to |cb|. The resulting packet is given to a callback because it's +// stack allocated inside CreateIcmpPacket. +void CreateIcmpPacket(in6_addr src, + in6_addr dst, + const icmp6_hdr& icmp_header, + quic::QuicStringPiece body, + const std::function<void(quic::QuicStringPiece)>& cb); + +} // namespace quic + +#endif // QUICHE_QUIC_QBONE_PLATFORM_ICMP_PACKET_H_ diff --git a/chromium/net/third_party/quiche/src/quic/qbone/platform/icmp_packet_test.cc b/chromium/net/third_party/quiche/src/quic/qbone/platform/icmp_packet_test.cc new file mode 100644 index 00000000000..1aeabe08570 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/platform/icmp_packet_test.cc @@ -0,0 +1,127 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/third_party/quiche/src/quic/qbone/platform/icmp_packet.h" + +#include <netinet/ip6.h> + +#include <cstdint> + +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h" + +namespace quic { +namespace { + +constexpr char kReferenceSourceAddress[] = "fe80:1:2:3:4::1"; +constexpr char kReferenceDestinationAddress[] = "fe80:4:3:2:1::1"; + +// clang-format off +constexpr uint8_t kReferenceICMPMessageBody[] { + 0xd2, 0x61, 0x29, 0x5b, 0x00, 0x00, 0x00, 0x00, + 0x0d, 0x59, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37 +}; + +constexpr uint8_t kReferenceICMPPacket[] = { + // START IPv6 Header + // IPv6 with zero TOS and flow label. + 0x60, 0x00, 0x00, 0x00, + // Payload is 64 bytes + 0x00, 0x40, + // Next header is 58 + 0x3a, + // Hop limit is 64 + 0x40, + // Source address of fe80:1:2:3:4::1 + 0xfe, 0x80, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, + 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + // Destination address of fe80:4:3:2:1::1 + 0xfe, 0x80, 0x00, 0x04, 0x00, 0x03, 0x00, 0x02, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + // END IPv6 Header + // START ICMPv6 Header + // Echo Request, zero code + 0x80, 0x00, + // Checksum + 0xec, 0x00, + // Identifier + 0xcb, 0x82, + // Sequence Number + 0x00, 0x01, + // END ICMPv6 Header + // Message body + 0xd2, 0x61, 0x29, 0x5b, 0x00, 0x00, 0x00, 0x00, + 0x0d, 0x59, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37 +}; +// clang-format on + +} // namespace + +TEST(IcmpPacketTest, CreatedPacketMatchesReference) { + QuicIpAddress src; + ASSERT_TRUE(src.FromString(kReferenceSourceAddress)); + in6_addr src_addr; + memcpy(src_addr.s6_addr, src.ToPackedString().data(), sizeof(in6_addr)); + + QuicIpAddress dst; + ASSERT_TRUE(dst.FromString(kReferenceDestinationAddress)); + in6_addr dst_addr; + memcpy(dst_addr.s6_addr, dst.ToPackedString().data(), sizeof(in6_addr)); + + icmp6_hdr icmp_header{}; + icmp_header.icmp6_type = ICMP6_ECHO_REQUEST; + icmp_header.icmp6_id = 0x82cb; + icmp_header.icmp6_seq = 0x0100; + + QuicStringPiece message_body = QuicStringPiece( + reinterpret_cast<const char*>(kReferenceICMPMessageBody), 56); + QuicStringPiece expected_packet = + QuicStringPiece(reinterpret_cast<const char*>(kReferenceICMPPacket), 104); + CreateIcmpPacket(src_addr, dst_addr, icmp_header, message_body, + [&expected_packet](QuicStringPiece packet) { + QUIC_LOG(INFO) << QuicTextUtils::HexDump(packet); + ASSERT_EQ(packet, expected_packet); + }); +} + +TEST(IcmpPacketTest, NonZeroChecksumIsIgnored) { + QuicIpAddress src; + ASSERT_TRUE(src.FromString(kReferenceSourceAddress)); + in6_addr src_addr; + memcpy(src_addr.s6_addr, src.ToPackedString().data(), sizeof(in6_addr)); + + QuicIpAddress dst; + ASSERT_TRUE(dst.FromString(kReferenceDestinationAddress)); + in6_addr dst_addr; + memcpy(dst_addr.s6_addr, dst.ToPackedString().data(), sizeof(in6_addr)); + + icmp6_hdr icmp_header{}; + icmp_header.icmp6_type = ICMP6_ECHO_REQUEST; + icmp_header.icmp6_id = 0x82cb; + icmp_header.icmp6_seq = 0x0100; + // Set the checksum to a bogus value + icmp_header.icmp6_cksum = 0x1234; + + QuicStringPiece message_body = QuicStringPiece( + reinterpret_cast<const char*>(kReferenceICMPMessageBody), 56); + QuicStringPiece expected_packet = + QuicStringPiece(reinterpret_cast<const char*>(kReferenceICMPPacket), 104); + CreateIcmpPacket(src_addr, dst_addr, icmp_header, message_body, + [&expected_packet](QuicStringPiece packet) { + QUIC_LOG(INFO) << QuicTextUtils::HexDump(packet); + ASSERT_EQ(packet, expected_packet); + }); +} + +} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/qbone/platform/internet_checksum.cc b/chromium/net/third_party/quiche/src/quic/qbone/platform/internet_checksum.cc new file mode 100644 index 00000000000..9cbe227abdc --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/platform/internet_checksum.cc @@ -0,0 +1,32 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/third_party/quiche/src/quic/qbone/platform/internet_checksum.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_endian.h" + +namespace quic { + +void InternetChecksum::Update(const char* data, size_t size) { + const char* current; + for (current = data; current + 1 < data + size; current += 2) { + accumulator_ += *reinterpret_cast<const uint16_t*>(current); + } + if (current < data + size) { + accumulator_ += *reinterpret_cast<const uint8_t*>(current); + } +} + +void InternetChecksum::Update(const uint8_t* data, size_t size) { + Update(reinterpret_cast<const char*>(data), size); +} + +uint16_t InternetChecksum::Value() const { + uint32_t total = accumulator_; + while (total & 0xffff0000u) { + total = (total >> 16u) + (total & 0xffffu); + } + return ~static_cast<uint16_t>(total); +} + +} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/qbone/platform/internet_checksum.h b/chromium/net/third_party/quiche/src/quic/qbone/platform/internet_checksum.h new file mode 100644 index 00000000000..85d24155f29 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/platform/internet_checksum.h @@ -0,0 +1,32 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef QUICHE_QUIC_QBONE_PLATFORM_INTERNET_CHECKSUM_H_ +#define QUICHE_QUIC_QBONE_PLATFORM_INTERNET_CHECKSUM_H_ + +#include <cstddef> +#include <cstdint> + +namespace quic { + +// Incrementally compute an Internet header checksum as described in RFC 1071. +class InternetChecksum { + public: + // Update the checksum with the specified data. Note that while the checksum + // is commutative, the data has to be supplied in the units of two-byte words. + // If there is an extra byte at the end, the function has to be called on it + // last. + void Update(const char* data, size_t size); + + void Update(const uint8_t* data, size_t size); + + uint16_t Value() const; + + private: + uint32_t accumulator_ = 0; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_QBONE_PLATFORM_INTERNET_CHECKSUM_H_ diff --git a/chromium/net/third_party/quiche/src/quic/qbone/platform/internet_checksum_test.cc b/chromium/net/third_party/quiche/src/quic/qbone/platform/internet_checksum_test.cc new file mode 100644 index 00000000000..a4736e2c183 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/platform/internet_checksum_test.cc @@ -0,0 +1,67 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/third_party/quiche/src/quic/qbone/platform/internet_checksum.h" + +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" + +namespace quic { +namespace { + +// From the Numerical Example described in RFC 1071 +// https://tools.ietf.org/html/rfc1071#section-3 +TEST(InternetChecksumTest, MatchesRFC1071Example) { + uint8_t data[] = {0x00, 0x01, 0xf2, 0x03, 0xf4, 0xf5, 0xf6, 0xf7}; + + InternetChecksum checksum; + checksum.Update(data, 8); + uint16_t result = checksum.Value(); + auto* result_bytes = reinterpret_cast<uint8_t*>(&result); + ASSERT_EQ(0x22, result_bytes[0]); + ASSERT_EQ(0x0d, result_bytes[1]); +} + +// Same as above, except 7 bytes. Should behave as if there was an 8th byte +// that equals 0. +TEST(InternetChecksumTest, MatchesRFC1071ExampleWithOddByteCount) { + uint8_t data[] = {0x00, 0x01, 0xf2, 0x03, 0xf4, 0xf5, 0xf6}; + + InternetChecksum checksum; + checksum.Update(data, 7); + uint16_t result = checksum.Value(); + auto* result_bytes = reinterpret_cast<uint8_t*>(&result); + ASSERT_EQ(0x23, result_bytes[0]); + ASSERT_EQ(0x04, result_bytes[1]); +} + +// From the example described at: +// http://www.cs.berkeley.edu/~kfall/EE122/lec06/tsld023.htm +TEST(InternetChecksumTest, MatchesBerkleyExample) { + uint8_t data[] = {0xe3, 0x4f, 0x23, 0x96, 0x44, 0x27, 0x99, 0xf3}; + + InternetChecksum checksum; + checksum.Update(data, 8); + uint16_t result = checksum.Value(); + auto* result_bytes = reinterpret_cast<uint8_t*>(&result); + ASSERT_EQ(0x1a, result_bytes[0]); + ASSERT_EQ(0xff, result_bytes[1]); +} + +TEST(InternetChecksumTest, ChecksumRequiringMultipleCarriesInLittleEndian) { + uint8_t data[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x02, 0x00}; + + // Data will accumulate to 0x0002FFFF + // Summing lower and upper halves gives 0x00010001 + // Second sum of lower and upper halves gives 0x0002 + // One's complement gives 0xfffd, or [0xfd, 0xff] in network byte order + InternetChecksum checksum; + checksum.Update(data, 8); + uint16_t result = checksum.Value(); + auto* result_bytes = reinterpret_cast<uint8_t*>(&result); + EXPECT_EQ(0xfd, result_bytes[0]); + EXPECT_EQ(0xff, result_bytes[1]); +} + +} // namespace +} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/qbone/platform/ip_range.cc b/chromium/net/third_party/quiche/src/quic/qbone/platform/ip_range.cc new file mode 100644 index 00000000000..15ebb726b86 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/platform/ip_range.cc @@ -0,0 +1,101 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/third_party/quiche/src/quic/qbone/platform/ip_range.h" + +#include "net/third_party/quiche/src/quic/platform/api/quic_endian.h" + +namespace quic { + +namespace { + +constexpr size_t kIPv4Size = 32; +constexpr size_t kIPv6Size = 128; + +QuicIpAddress TruncateToLength(const QuicIpAddress& input, + size_t* prefix_length) { + QuicIpAddress output; + if (input.IsIPv4()) { + if (*prefix_length > kIPv4Size) { + *prefix_length = kIPv4Size; + return input; + } + uint32_t raw_address = + *reinterpret_cast<const uint32_t*>(input.ToPackedString().data()); + raw_address = QuicEndian::NetToHost32(raw_address); + raw_address &= ~0U << (kIPv4Size - *prefix_length); + raw_address = QuicEndian::HostToNet32(raw_address); + output.FromPackedString(reinterpret_cast<const char*>(&raw_address), + sizeof(raw_address)); + return output; + } + if (input.IsIPv6()) { + if (*prefix_length > kIPv6Size) { + *prefix_length = kIPv6Size; + return input; + } + uint64_t raw_address[2]; + memcpy(raw_address, input.ToPackedString().data(), sizeof(raw_address)); + // raw_address[0] holds higher 8 bytes in big endian and raw_address[1] + // holds lower 8 bytes. Converting each to little endian for us to mask bits + // out. + // The endianess between raw_address[0] and raw_address[1] is handled + // explicitly by handling lower and higher bytes separately. + raw_address[0] = QuicEndian::NetToHost64(raw_address[0]); + raw_address[1] = QuicEndian::NetToHost64(raw_address[1]); + if (*prefix_length <= kIPv6Size / 2) { + raw_address[0] &= ~uint64_t{0} << (kIPv6Size / 2 - *prefix_length); + raw_address[1] = 0; + } else { + raw_address[1] &= ~uint64_t{0} << (kIPv6Size - *prefix_length); + } + raw_address[0] = QuicEndian::HostToNet64(raw_address[0]); + raw_address[1] = QuicEndian::HostToNet64(raw_address[1]); + output.FromPackedString(reinterpret_cast<const char*>(raw_address), + sizeof(raw_address)); + return output; + } + return output; +} + +} // namespace + +IpRange::IpRange(const QuicIpAddress& prefix, size_t prefix_length) + : prefix_(prefix), prefix_length_(prefix_length) { + prefix_ = TruncateToLength(prefix_, &prefix_length_); +} + +bool IpRange::operator==(IpRange other) const { + return prefix_ == other.prefix_ && prefix_length_ == other.prefix_length_; +} + +bool IpRange::operator!=(IpRange other) const { + return !(*this == other); +} + +bool IpRange::FromString(const string& range) { + size_t slash_pos = range.find('/'); + if (slash_pos == string::npos) { + return false; + } + QuicIpAddress prefix; + bool success = prefix.FromString(range.substr(0, slash_pos)); + if (!success) { + return false; + } + uint64_t num_processed = 0; + size_t prefix_length = std::stoi(range.substr(slash_pos + 1), &num_processed); + if (num_processed + 1 + slash_pos != range.length()) { + return false; + } + prefix_ = TruncateToLength(prefix, &prefix_length); + prefix_length_ = prefix_length; + return true; +} + +QuicIpAddress IpRange::FirstAddressInRange() { + return prefix(); +} + +} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/qbone/platform/ip_range.h b/chromium/net/third_party/quiche/src/quic/qbone/platform/ip_range.h new file mode 100644 index 00000000000..545c32c6cb9 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/platform/ip_range.h @@ -0,0 +1,60 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef QUICHE_QUIC_QBONE_PLATFORM_IP_RANGE_H_ +#define QUICHE_QUIC_QBONE_PLATFORM_IP_RANGE_H_ + +#include "net/third_party/quiche/src/quic/platform/api/quic_ip_address.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h" + +namespace quic { + +class IpRange { + public: + // Default constructor to have an uninitialized IpRange. + IpRange() : prefix_length_(0) {} + + // prefix will be automatically truncated to prefix_length, so that any bit + // after prefix_length are zero. + IpRange(const QuicIpAddress& prefix, size_t prefix_length); + + bool operator==(IpRange other) const; + bool operator!=(IpRange other) const; + + // Parses range that looks like "10.0.0.1/8". Tailing bits will be set to zero + // after prefix_length. Return false if the parsing failed. + bool FromString(const string& range); + + // Returns the string representation of this object. + string ToString() const { + if (IsInitialized()) { + return absl::StrCat(prefix_.ToString(), "/", prefix_length_); + } + return "(uninitialized)"; + } + + // Whether this object is initialized. + bool IsInitialized() const { return prefix_.IsInitialized(); } + + // Returns the first available IP address in this IpRange. The resulting + // address will be uninitialized if there is no available address. + QuicIpAddress FirstAddressInRange(); + + // The address family of this IpRange. + IpAddressFamily address_family() const { return prefix_.address_family(); } + + // The subnet's prefix address. + QuicIpAddress prefix() const { return prefix_; } + + // The subnet's prefix length. + size_t prefix_length() const { return prefix_length_; } + + private: + QuicIpAddress prefix_; + size_t prefix_length_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_QBONE_PLATFORM_IP_RANGE_H_ diff --git a/chromium/net/third_party/quiche/src/quic/qbone/platform/ip_range_test.cc b/chromium/net/third_party/quiche/src/quic/qbone/platform/ip_range_test.cc new file mode 100644 index 00000000000..bac5c963af8 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/platform/ip_range_test.cc @@ -0,0 +1,65 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/third_party/quiche/src/quic/qbone/platform/ip_range.h" + +#include "net/third_party/quiche/src/quic/platform/api/quic_ip_address.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" + +namespace quic { +namespace { + +TEST(IpRangeTest, TruncateWorksIPv4) { + QuicIpAddress before_truncate; + before_truncate.FromString("255.255.255.255"); + EXPECT_EQ("128.0.0.0/1", IpRange(before_truncate, 1).ToString()); + EXPECT_EQ("192.0.0.0/2", IpRange(before_truncate, 2).ToString()); + EXPECT_EQ("255.224.0.0/11", IpRange(before_truncate, 11).ToString()); + EXPECT_EQ("255.255.255.224/27", IpRange(before_truncate, 27).ToString()); + EXPECT_EQ("255.255.255.254/31", IpRange(before_truncate, 31).ToString()); + EXPECT_EQ("255.255.255.255/32", IpRange(before_truncate, 32).ToString()); + EXPECT_EQ("255.255.255.255/32", IpRange(before_truncate, 33).ToString()); +} + +TEST(IpRangeTest, TruncateWorksIPv6) { + QuicIpAddress before_truncate; + before_truncate.FromString("ffff:ffff:ffff:ffff:f903::5"); + EXPECT_EQ("fe00::/7", IpRange(before_truncate, 7).ToString()); + EXPECT_EQ("ffff:ffff:ffff::/48", IpRange(before_truncate, 48).ToString()); + EXPECT_EQ("ffff:ffff:ffff:ffff::/64", + IpRange(before_truncate, 64).ToString()); + EXPECT_EQ("ffff:ffff:ffff:ffff:8000::/65", + IpRange(before_truncate, 65).ToString()); + EXPECT_EQ("ffff:ffff:ffff:ffff:f903::4/127", + IpRange(before_truncate, 127).ToString()); +} + +TEST(IpRangeTest, FromStringWorksIPv4) { + IpRange range; + ASSERT_TRUE(range.FromString("127.0.3.249/26")); + EXPECT_EQ("127.0.3.192/26", range.ToString()); +} + +TEST(IpRangeTest, FromStringWorksIPv6) { + IpRange range; + ASSERT_TRUE(range.FromString("ff01:8f21:77f9::/33")); + EXPECT_EQ("ff01:8f21::/33", range.ToString()); +} + +TEST(IpRangeTest, FirstAddressWorksIPv6) { + IpRange range; + ASSERT_TRUE(range.FromString("ffff:ffff::/64")); + QuicIpAddress first_address = range.FirstAddressInRange(); + EXPECT_EQ("ffff:ffff::", first_address.ToString()); +} + +TEST(IpRangeTest, FirstAddressWorksIPv4) { + IpRange range; + ASSERT_TRUE(range.FromString("10.0.0.0/24")); + QuicIpAddress first_address = range.FirstAddressInRange(); + EXPECT_EQ("10.0.0.0", first_address.ToString()); +} + +} // namespace +} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/qbone/platform/kernel_interface.h b/chromium/net/third_party/quiche/src/quic/qbone/platform/kernel_interface.h new file mode 100644 index 00000000000..c96b6e67d0d --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/platform/kernel_interface.h @@ -0,0 +1,169 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef QUICHE_QUIC_QBONE_PLATFORM_KERNEL_INTERFACE_H_ +#define QUICHE_QUIC_QBONE_PLATFORM_KERNEL_INTERFACE_H_ + +#include <errno.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <unistd.h> +#include <type_traits> +#include <utility> + +namespace quic { + +// A wrapper for making syscalls to the kernel, so that syscalls can be +// mocked during testing. +class KernelInterface { + public: + virtual ~KernelInterface() {} + virtual int bind(int fd, const struct sockaddr* addr, socklen_t addr_len) = 0; + virtual int close(int fd) = 0; + virtual int ioctl(int fd, int request, void* argp) = 0; + virtual int open(const char* pathname, int flags) = 0; + virtual ssize_t read(int fd, void* buf, size_t count) = 0; + virtual ssize_t recvfrom(int sockfd, + void* buf, + size_t len, + int flags, + struct sockaddr* src_addr, + socklen_t* addrlen) = 0; + virtual ssize_t sendmsg(int sockfd, const struct msghdr* msg, int flags) = 0; + virtual ssize_t sendto(int sockfd, + const void* buf, + size_t len, + int flags, + const struct sockaddr* dest_addr, + socklen_t addrlen) = 0; + virtual int socket(int domain, int type, int protocol) = 0; + virtual int setsockopt(int fd, + int level, + int optname, + const void* optval, + socklen_t optlen) = 0; + virtual ssize_t write(int fd, const void* buf, size_t count) = 0; +}; + +// It is unfortunate to have R here, but std::result_of cannot be used. +template <typename F, typename R, typename... Params> +auto SyscallRetryOnError(R r, F f, Params&&... params) + -> decltype(f(std::forward<Params>(params)...)) { + static_assert( + std::is_same<decltype(f(std::forward<Params>(params)...)), R>::value, + "Return type does not match"); + decltype(f(std::forward<Params>(params)...)) result; + do { + result = f(std::forward<Params>(params)...); + } while (result == r && errno == EINTR); + return result; +} + +template <typename F, typename... Params> +auto SyscallRetry(F f, Params&&... params) + -> decltype(f(std::forward<Params>(params)...)) { + return SyscallRetryOnError(-1, f, std::forward<Params>(params)...); +} + +template <typename Runner> +class ParametrizedKernel final : public KernelInterface { + public: + static_assert(std::is_trivially_destructible<Runner>::value, + "Runner is used as static, must be trivially destructible"); + + ~ParametrizedKernel() override {} + + int bind(int fd, const struct sockaddr* addr, socklen_t addr_len) override { + static Runner syscall("bind"); + return syscall.Retry(&::bind, fd, addr, addr_len); + } + int close(int fd) override { + static Runner syscall("close"); + return syscall.Retry(&::close, fd); + } + int ioctl(int fd, int request, void* argp) override { + static Runner syscall("ioctl"); + return syscall.Retry(&::ioctl, fd, request, argp); + } + int open(const char* pathname, int flags) override { + static Runner syscall("open"); + return syscall.Retry(&::open, pathname, flags); + } + ssize_t read(int fd, void* buf, size_t count) override { + static Runner syscall("read"); + return syscall.Run(&::read, fd, buf, count); + } + ssize_t recvfrom(int sockfd, + void* buf, + size_t len, + int flags, + struct sockaddr* src_addr, + socklen_t* addrlen) override { + static Runner syscall("recvfrom"); + return syscall.RetryOnError(&::recvfrom, static_cast<ssize_t>(-1), sockfd, + buf, len, flags, src_addr, addrlen); + } + ssize_t sendmsg(int sockfd, const struct msghdr* msg, int flags) override { + static Runner syscall("sendmsg"); + return syscall.RetryOnError(&::sendmsg, static_cast<ssize_t>(-1), sockfd, + msg, flags); + } + ssize_t sendto(int sockfd, + const void* buf, + size_t len, + int flags, + const struct sockaddr* dest_addr, + socklen_t addrlen) override { + static Runner syscall("sendto"); + return syscall.RetryOnError(&::sendto, static_cast<ssize_t>(-1), sockfd, + buf, len, flags, dest_addr, addrlen); + } + int socket(int domain, int type, int protocol) override { + static Runner syscall("socket"); + return syscall.Retry(&::socket, domain, type, protocol); + } + int setsockopt(int fd, + int level, + int optname, + const void* optval, + socklen_t optlen) override { + static Runner syscall("setsockopt"); + return syscall.Retry(&::setsockopt, fd, level, optname, optval, optlen); + } + ssize_t write(int fd, const void* buf, size_t count) override { + static Runner syscall("write"); + return syscall.Run(&::write, fd, buf, count); + } +}; + +class DefaultKernelRunner { + public: + explicit DefaultKernelRunner(const char* name) {} + + template <typename F, typename R, typename... Params> + static auto RetryOnError(F f, R r, Params&&... params) + -> decltype(f(std::forward<Params>(params)...)) { + return SyscallRetryOnError(r, f, std::forward<Params>(params)...); + } + + template <typename F, typename... Params> + static auto Retry(F f, Params&&... params) + -> decltype(f(std::forward<Params>(params)...)) { + return SyscallRetry(f, std::forward<Params>(params)...); + } + + template <typename F, typename... Params> + static auto Run(F f, Params&&... params) + -> decltype(f(std::forward<Params>(params)...)) { + return f(std::forward<Params>(params)...); + } +}; + +using Kernel = ParametrizedKernel<DefaultKernelRunner>; + +} // namespace quic + +#endif // QUICHE_QUIC_QBONE_PLATFORM_KERNEL_INTERFACE_H_ diff --git a/chromium/net/third_party/quiche/src/quic/qbone/platform/mock_kernel.h b/chromium/net/third_party/quiche/src/quic/qbone/platform/mock_kernel.h new file mode 100644 index 00000000000..c01aad1708b --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/platform/mock_kernel.h @@ -0,0 +1,46 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef QUICHE_QUIC_QBONE_PLATFORM_MOCK_KERNEL_H_ +#define QUICHE_QUIC_QBONE_PLATFORM_MOCK_KERNEL_H_ + +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/qbone/platform/kernel_interface.h" + +namespace quic { + +class MockKernel : public KernelInterface { + public: + MockKernel() {} + + MOCK_METHOD3(bind, + int(int fd, const struct sockaddr* addr, socklen_t addr_len)); + MOCK_METHOD1(close, int(int fd)); + MOCK_METHOD3(ioctl, int(int fd, int request, void* argp)); + MOCK_METHOD2(open, int(const char* pathname, int flags)); + MOCK_METHOD3(read, ssize_t(int fd, void* buf, size_t count)); + MOCK_METHOD6(recvfrom, + ssize_t(int sockfd, + void* buf, + size_t len, + int flags, + struct sockaddr* src_addr, + socklen_t* addrlen)); + MOCK_METHOD3(sendmsg, + ssize_t(int sockfd, const struct msghdr* msg, int flags)); + MOCK_METHOD6(sendto, + ssize_t(int sockfd, + const void* buf, + size_t len, + int flags, + const struct sockaddr* dest_addr, + socklen_t addrlen)); + MOCK_METHOD3(socket, int(int domain, int type, int protocol)); + MOCK_METHOD5(setsockopt, int(int, int, int, const void*, socklen_t)); + MOCK_METHOD3(write, ssize_t(int fd, const void* buf, size_t count)); +}; + +} // namespace quic + +#endif // QUICHE_QUIC_QBONE_PLATFORM_MOCK_KERNEL_H_ diff --git a/chromium/net/third_party/quiche/src/quic/qbone/platform/mock_netlink.h b/chromium/net/third_party/quiche/src/quic/qbone/platform/mock_netlink.h new file mode 100644 index 00000000000..5c1e7bc2fde --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/platform/mock_netlink.h @@ -0,0 +1,46 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef QUICHE_QUIC_QBONE_PLATFORM_MOCK_NETLINK_H_ +#define QUICHE_QUIC_QBONE_PLATFORM_MOCK_NETLINK_H_ + +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/qbone/platform/netlink_interface.h" + +namespace quic { + +class MockNetlink : public NetlinkInterface { + public: + MOCK_METHOD2(GetLinkInfo, bool(const string&, LinkInfo*)); + + MOCK_METHOD4(GetAddresses, + bool(int, uint8_t, std::vector<AddressInfo>*, int*)); + + MOCK_METHOD7(ChangeLocalAddress, + bool(uint32_t, + Verb, + const QuicIpAddress&, + uint8_t, + uint8_t, + uint8_t, + const std::vector<struct rtattr*>&)); + + MOCK_METHOD1(GetRouteInfo, bool(std::vector<RoutingRule>*)); + + MOCK_METHOD6( + ChangeRoute, + bool(Verb, uint32_t, const IpRange&, uint8_t, QuicIpAddress, int32_t)); + + MOCK_METHOD1(GetRuleInfo, bool(std::vector<IpRule>*)); + + MOCK_METHOD3(ChangeRule, bool(Verb, uint32_t, IpRange)); + + MOCK_METHOD2(Send, bool(struct iovec*, size_t)); + + MOCK_METHOD2(Recv, bool(uint32_t, NetlinkParserInterface*)); +}; + +} // namespace quic + +#endif // QUICHE_QUIC_QBONE_PLATFORM_MOCK_NETLINK_H_ diff --git a/chromium/net/third_party/quiche/src/quic/qbone/platform/netlink.cc b/chromium/net/third_party/quiche/src/quic/qbone/platform/netlink.cc new file mode 100644 index 00000000000..1a4270e6644 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/platform/netlink.cc @@ -0,0 +1,828 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/third_party/quiche/src/quic/qbone/platform/netlink.h" + +#include <linux/fib_rules.h> + +#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_fallthrough.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ip_address.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h" +#include "net/quic/platform/impl/quic_ip_address_impl.h" +#include "net/third_party/quiche/src/quic/qbone/platform/rtnetlink_message.h" + +namespace quic { + +Netlink::Netlink(KernelInterface* kernel) : kernel_(kernel) { + seq_ = QuicRandom::GetInstance()->RandUint64(); +} + +Netlink::~Netlink() { + CloseSocket(); +} + +void Netlink::ResetRecvBuf(size_t size) { + if (size != 0) { + recvbuf_ = QuicMakeUnique<char[]>(size); + } else { + recvbuf_ = nullptr; + } + recvbuf_length_ = size; +} + +bool Netlink::OpenSocket() { + if (socket_fd_ >= 0) { + return true; + } + + socket_fd_ = kernel_->socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + + if (socket_fd_ < 0) { + QUIC_PLOG(ERROR) << "can't open netlink socket"; + return false; + } + + QUIC_LOG(INFO) << "Opened a new netlink socket fd = " << socket_fd_; + + // bind a local address to the socket + sockaddr_nl myaddr; + memset(&myaddr, 0, sizeof(myaddr)); + myaddr.nl_family = AF_NETLINK; + if (kernel_->bind(socket_fd_, reinterpret_cast<struct sockaddr*>(&myaddr), + sizeof(myaddr)) < 0) { + QUIC_LOG(INFO) << "can't bind address to socket"; + CloseSocket(); + return false; + } + + return true; +} + +void Netlink::CloseSocket() { + if (socket_fd_ >= 0) { + QUIC_LOG(INFO) << "Closing netlink socket fd = " << socket_fd_; + kernel_->close(socket_fd_); + } + ResetRecvBuf(0); + socket_fd_ = -1; +} + +namespace { + +class LinkInfoParser : public NetlinkParserInterface { + public: + LinkInfoParser(string interface_name, Netlink::LinkInfo* link_info) + : interface_name_(std::move(interface_name)), link_info_(link_info) {} + + void Run(struct nlmsghdr* netlink_message) override { + if (netlink_message->nlmsg_type != RTM_NEWLINK) { + QUIC_LOG(INFO) << QuicStrCat( + "Unexpected nlmsg_type: ", netlink_message->nlmsg_type, + " expected: ", RTM_NEWLINK); + return; + } + + struct ifinfomsg* interface_info = + reinterpret_cast<struct ifinfomsg*>(NLMSG_DATA(netlink_message)); + + // make sure interface_info is what we asked for. + if (interface_info->ifi_family != AF_UNSPEC) { + QUIC_LOG(INFO) << QuicStrCat( + "Unexpected ifi_family: ", interface_info->ifi_family, + " expected: ", AF_UNSPEC); + return; + } + + char hardware_address[kHwAddrSize]; + size_t hardware_address_length = 0; + char broadcast_address[kHwAddrSize]; + size_t broadcast_address_length = 0; + string name; + + // loop through the attributes + struct rtattr* rta; + int payload_length = IFLA_PAYLOAD(netlink_message); + for (rta = IFLA_RTA(interface_info); RTA_OK(rta, payload_length); + rta = RTA_NEXT(rta, payload_length)) { + int attribute_length; + switch (rta->rta_type) { + case IFLA_ADDRESS: { + attribute_length = RTA_PAYLOAD(rta); + if (attribute_length > kHwAddrSize) { + QUIC_VLOG(2) << "IFLA_ADDRESS too long: " << attribute_length; + break; + } + memmove(hardware_address, RTA_DATA(rta), attribute_length); + hardware_address_length = attribute_length; + break; + } + case IFLA_BROADCAST: { + attribute_length = RTA_PAYLOAD(rta); + if (attribute_length > kHwAddrSize) { + QUIC_VLOG(2) << "IFLA_BROADCAST too long: " << attribute_length; + break; + } + memmove(broadcast_address, RTA_DATA(rta), attribute_length); + broadcast_address_length = attribute_length; + break; + } + case IFLA_IFNAME: { + name = + string(reinterpret_cast<char*>(RTA_DATA(rta)), RTA_PAYLOAD(rta)); + // The name maybe a 0 terminated c string. + name = name.substr(0, name.find('\0')); + break; + } + } + } + + QUIC_VLOG(2) << "interface name: " << name + << ", index: " << interface_info->ifi_index; + + if (name == interface_name_) { + link_info_->index = interface_info->ifi_index; + link_info_->type = interface_info->ifi_type; + link_info_->hardware_address_length = hardware_address_length; + if (hardware_address_length > 0) { + memmove(&link_info_->hardware_address, hardware_address, + hardware_address_length); + } + link_info_->broadcast_address_length = broadcast_address_length; + if (broadcast_address_length > 0) { + memmove(&link_info_->broadcast_address, broadcast_address, + broadcast_address_length); + } + found_link_ = true; + } + } + + bool found_link() { return found_link_; } + + private: + const string interface_name_; + Netlink::LinkInfo* const link_info_; + bool found_link_ = false; +}; + +} // namespace + +bool Netlink::GetLinkInfo(const string& interface_name, LinkInfo* link_info) { + auto message = LinkMessage::New(RtnetlinkMessage::Operation::GET, + NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST, + seq_, getpid(), nullptr); + + if (!Send(message.BuildIoVec().get(), message.IoVecSize())) { + QUIC_LOG(ERROR) << "send failed."; + return false; + } + + // Pass the parser to the receive routine. It may be called multiple times + // since there may be multiple reply packets each with multiple reply + // messages. + LinkInfoParser parser(interface_name, link_info); + if (!Recv(seq_++, &parser)) { + QUIC_LOG(ERROR) << "recv failed."; + return false; + } + + return parser.found_link(); +} + +namespace { + +class LocalAddressParser : public NetlinkParserInterface { + public: + LocalAddressParser(int interface_index, + uint8_t unwanted_flags, + std::vector<Netlink::AddressInfo>* local_addresses, + int* num_ipv6_nodad_dadfailed_addresses) + : interface_index_(interface_index), + unwanted_flags_(unwanted_flags), + local_addresses_(local_addresses), + num_ipv6_nodad_dadfailed_addresses_( + num_ipv6_nodad_dadfailed_addresses) {} + + void Run(struct nlmsghdr* netlink_message) override { + // each nlmsg contains a header and multiple address attributes. + if (netlink_message->nlmsg_type != RTM_NEWADDR) { + QUIC_LOG(INFO) << "Unexpected nlmsg_type: " << netlink_message->nlmsg_type + << " expected: " << RTM_NEWADDR; + return; + } + + struct ifaddrmsg* interface_address = + reinterpret_cast<struct ifaddrmsg*>(NLMSG_DATA(netlink_message)); + + // Make sure this is for an address family we're interested in. + if (interface_address->ifa_family != AF_INET && + interface_address->ifa_family != AF_INET6) { + QUIC_VLOG(2) << QuicStrCat("uninteresting ifa family: ", + interface_address->ifa_family); + return; + } + + // Keep track of addresses with both 'nodad' and 'dadfailed', this really + // should't be possible and is likely a kernel bug. + if (num_ipv6_nodad_dadfailed_addresses_ != nullptr && + (interface_address->ifa_flags & IFA_F_NODAD) && + (interface_address->ifa_flags & IFA_F_DADFAILED)) { + ++(*num_ipv6_nodad_dadfailed_addresses_); + } + + uint8_t unwanted_flags = interface_address->ifa_flags & unwanted_flags_; + if (unwanted_flags != 0) { + QUIC_VLOG(2) << QuicStrCat("unwanted ifa flags: ", unwanted_flags); + return; + } + + // loop through the attributes + struct rtattr* rta; + int payload_length = IFA_PAYLOAD(netlink_message); + Netlink::AddressInfo address_info; + for (rta = IFA_RTA(interface_address); RTA_OK(rta, payload_length); + rta = RTA_NEXT(rta, payload_length)) { + // There's quite a lot of confusion in Linux over the use of IFA_LOCAL and + // IFA_ADDRESS (source and destination address). For broadcast links, such + // as Ethernet, they are identical (see <linux/if_addr.h>), but the kernel + // sometimes uses only one or the other. We'll return both so that the + // caller can decide which to use. + if (rta->rta_type != IFA_LOCAL && rta->rta_type != IFA_ADDRESS) { + QUIC_VLOG(2) << "Ignoring uninteresting rta_type: " << rta->rta_type; + continue; + } + + switch (interface_address->ifa_family) { + case AF_INET: + QUIC_FALLTHROUGH_INTENDED; + case AF_INET6: + // QuicIpAddress knows how to parse ip from raw bytes as long as they + // are in network byte order. + if (RTA_PAYLOAD(rta) == sizeof(struct in_addr) || + RTA_PAYLOAD(rta) == sizeof(struct in6_addr)) { + auto* raw_ip = reinterpret_cast<char*>(RTA_DATA(rta)); + if (rta->rta_type == IFA_LOCAL) { + address_info.local_address.FromPackedString(raw_ip, + RTA_PAYLOAD(rta)); + } else { + address_info.interface_address.FromPackedString(raw_ip, + RTA_PAYLOAD(rta)); + } + } + break; + default: + QUIC_LOG(ERROR) << QuicStrCat("Unknown address family: ", + interface_address->ifa_family); + } + } + + QUIC_VLOG(2) << "local_address: " << address_info.local_address.ToString() + << " interface_address: " + << address_info.interface_address.ToString() + << " index: " << interface_address->ifa_index; + if (interface_address->ifa_index != interface_index_) { + return; + } + + address_info.prefix_length = interface_address->ifa_prefixlen; + address_info.scope = interface_address->ifa_scope; + if (address_info.local_address.IsInitialized() || + address_info.interface_address.IsInitialized()) { + local_addresses_->push_back(address_info); + } + } + + private: + const int interface_index_; + const uint8_t unwanted_flags_; + std::vector<Netlink::AddressInfo>* const local_addresses_; + int* const num_ipv6_nodad_dadfailed_addresses_; +}; + +} // namespace + +bool Netlink::GetAddresses(int interface_index, + uint8_t unwanted_flags, + std::vector<AddressInfo>* addresses, + int* num_ipv6_nodad_dadfailed_addresses) { + // the message doesn't contain the index, we'll have to do the filtering while + // parsing the reply. This is because NLM_F_MATCH, which only returns entries + // that matches the request criteria, is not yet implemented (see man 3 + // netlink). + auto message = AddressMessage::New(RtnetlinkMessage::Operation::GET, + NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST, + seq_, getpid(), nullptr); + + // the send routine returns the socket to listen on. + if (!Send(message.BuildIoVec().get(), message.IoVecSize())) { + QUIC_LOG(ERROR) << "send failed."; + return false; + } + + addresses->clear(); + if (num_ipv6_nodad_dadfailed_addresses != nullptr) { + *num_ipv6_nodad_dadfailed_addresses = 0; + } + + LocalAddressParser parser(interface_index, unwanted_flags, addresses, + num_ipv6_nodad_dadfailed_addresses); + // Pass the parser to the receive routine. It may be called multiple times + // since there may be multiple reply packets each with multiple reply + // messages. + if (!Recv(seq_++, &parser)) { + QUIC_LOG(ERROR) << "recv failed"; + return false; + } + return true; +} + +namespace { + +class UnknownParser : public NetlinkParserInterface { + public: + void Run(struct nlmsghdr* netlink_message) override { + QUIC_LOG(INFO) << "nlmsg reply type: " << netlink_message->nlmsg_type; + } +}; + +} // namespace + +bool Netlink::ChangeLocalAddress( + uint32_t interface_index, + Verb verb, + const QuicIpAddress& address, + uint8_t prefix_length, + uint8_t ifa_flags, + uint8_t ifa_scope, + const std::vector<struct rtattr*>& additional_attributes) { + if (verb == Verb::kReplace) { + return false; + } + auto operation = verb == Verb::kAdd ? RtnetlinkMessage::Operation::NEW + : RtnetlinkMessage::Operation::DEL; + uint8_t address_family; + if (address.address_family() == IpAddressFamily::IP_V4) { + address_family = AF_INET; + } else if (address.address_family() == IpAddressFamily::IP_V6) { + address_family = AF_INET6; + } else { + return false; + } + + struct ifaddrmsg address_header = {address_family, prefix_length, ifa_flags, + ifa_scope, interface_index}; + + auto message = AddressMessage::New(operation, NLM_F_REQUEST | NLM_F_ACK, seq_, + getpid(), &address_header); + + for (const auto& attribute : additional_attributes) { + if (attribute->rta_type == IFA_LOCAL) { + continue; + } + message.AppendAttribute(attribute->rta_type, RTA_DATA(attribute), + RTA_PAYLOAD(attribute)); + } + + message.AppendAttribute(IFA_LOCAL, address.ToPackedString().c_str(), + address.ToPackedString().size()); + + if (!Send(message.BuildIoVec().get(), message.IoVecSize())) { + QUIC_LOG(ERROR) << "send failed"; + return false; + } + + UnknownParser parser; + if (!Recv(seq_++, &parser)) { + QUIC_LOG(ERROR) << "receive failed."; + return false; + } + return true; +} + +namespace { + +class RoutingRuleParser : public NetlinkParserInterface { + public: + explicit RoutingRuleParser(std::vector<Netlink::RoutingRule>* routing_rules) + : routing_rules_(routing_rules) {} + + void Run(struct nlmsghdr* netlink_message) override { + if (netlink_message->nlmsg_type != RTM_NEWROUTE) { + QUIC_LOG(WARNING) << QuicStrCat( + "Unexpected nlmsg_type: ", netlink_message->nlmsg_type, + " expected: ", RTM_NEWROUTE); + return; + } + + auto* route = reinterpret_cast<struct rtmsg*>(NLMSG_DATA(netlink_message)); + int payload_length = RTM_PAYLOAD(netlink_message); + + if (route->rtm_family != AF_INET && route->rtm_family != AF_INET6) { + QUIC_VLOG(2) << QuicStrCat("Uninteresting family: ", route->rtm_family); + return; + } + + Netlink::RoutingRule rule; + rule.scope = route->rtm_scope; + rule.table = route->rtm_table; + + struct rtattr* rta; + for (rta = RTM_RTA(route); RTA_OK(rta, payload_length); + rta = RTA_NEXT(rta, payload_length)) { + switch (rta->rta_type) { + case RTA_TABLE: { + rule.table = *reinterpret_cast<uint32_t*>(RTA_DATA(rta)); + break; + } + case RTA_DST: { + QuicIpAddress destination; + destination.FromPackedString(reinterpret_cast<char*> RTA_DATA(rta), + RTA_PAYLOAD(rta)); + rule.destination_subnet = IpRange(destination, route->rtm_dst_len); + break; + } + case RTA_PREFSRC: { + QuicIpAddress preferred_source; + rule.preferred_source.FromPackedString( + reinterpret_cast<char*> RTA_DATA(rta), RTA_PAYLOAD(rta)); + break; + } + case RTA_OIF: { + rule.out_interface = *reinterpret_cast<int*>(RTA_DATA(rta)); + break; + } + default: { + QUIC_VLOG(2) << QuicStrCat("Uninteresting attribute: ", + rta->rta_type); + } + } + } + routing_rules_->push_back(rule); + } + + private: + std::vector<Netlink::RoutingRule>* routing_rules_; +}; + +} // namespace + +bool Netlink::GetRouteInfo(std::vector<Netlink::RoutingRule>* routing_rules) { + rtmsg route_message{}; + // Only manipulate main routing table. + route_message.rtm_table = RT_TABLE_MAIN; + + auto message = RouteMessage::New(RtnetlinkMessage::Operation::GET, + NLM_F_REQUEST | NLM_F_ROOT | NLM_F_MATCH, + seq_, getpid(), &route_message); + + if (!Send(message.BuildIoVec().get(), message.IoVecSize())) { + QUIC_LOG(ERROR) << "send failed"; + return false; + } + + RoutingRuleParser parser(routing_rules); + if (!Recv(seq_++, &parser)) { + QUIC_LOG(ERROR) << "recv failed"; + return false; + } + + return true; +} + +bool Netlink::ChangeRoute(Netlink::Verb verb, + uint32_t table, + const IpRange& destination_subnet, + uint8_t scope, + QuicIpAddress preferred_source, + int32_t interface_index) { + if (!destination_subnet.prefix().IsInitialized()) { + return false; + } + if (destination_subnet.address_family() != IpAddressFamily::IP_V4 && + destination_subnet.address_family() != IpAddressFamily::IP_V6) { + return false; + } + if (preferred_source.IsInitialized() && + preferred_source.address_family() != + destination_subnet.address_family()) { + return false; + } + + RtnetlinkMessage::Operation operation; + uint16_t flags = NLM_F_REQUEST | NLM_F_ACK; + switch (verb) { + case Verb::kAdd: + operation = RtnetlinkMessage::Operation::NEW; + // Setting NLM_F_EXCL so that an existing entry for this subnet will fail + // the request. NLM_F_CREATE is necessary to indicate this is trying to + // create a new entry - simply having RTM_NEWROUTE is not enough even the + // name suggests so. + flags |= NLM_F_EXCL | NLM_F_CREATE; + break; + case Verb::kRemove: + operation = RtnetlinkMessage::Operation::DEL; + break; + case Verb::kReplace: + operation = RtnetlinkMessage::Operation::NEW; + // Setting NLM_F_REPLACE to tell the kernel that existing entry for this + // subnet should be replaced. + flags |= NLM_F_REPLACE | NLM_F_CREATE; + break; + } + + struct rtmsg route_message; + memset(&route_message, 0, sizeof(route_message)); + route_message.rtm_family = + destination_subnet.address_family() == IpAddressFamily::IP_V4 ? AF_INET + : AF_INET6; + // rtm_dst_len and rtm_src_len are actually the subnet prefix lengths. Poor + // naming. + route_message.rtm_dst_len = destination_subnet.prefix_length(); + // 0 means no source subnet for this rule. + route_message.rtm_src_len = 0; + // Only program the main table. Other tables are intended for the kernel to + // manage. + route_message.rtm_table = RT_TABLE_MAIN; + // Use RTPROT_UNSPEC to match all the different protocol. Rules added by + // kernel have RTPROT_KERNEL. Rules added by the root user have RTPROT_STATIC + // instead. + route_message.rtm_protocol = + verb == Verb::kRemove ? RTPROT_UNSPEC : RTPROT_STATIC; + route_message.rtm_scope = scope; + // Only add unicast routing rule. + route_message.rtm_type = RTN_UNICAST; + auto message = + RouteMessage::New(operation, flags, seq_, getpid(), &route_message); + + message.AppendAttribute(RTA_TABLE, &table, sizeof(table)); + + // RTA_OIF is the target interface for this rule. + message.AppendAttribute(RTA_OIF, &interface_index, sizeof(interface_index)); + // The actual destination subnet must be truncated of all the tailing zeros. + message.AppendAttribute( + RTA_DST, + reinterpret_cast<const void*>( + destination_subnet.prefix().ToPackedString().c_str()), + destination_subnet.prefix().ToPackedString().size()); + // This is the source address to use in the IP packet should this routing rule + // is used. + if (preferred_source.IsInitialized()) { + message.AppendAttribute(RTA_PREFSRC, + reinterpret_cast<const void*>( + preferred_source.ToPackedString().c_str()), + preferred_source.ToPackedString().size()); + } + + if (!Send(message.BuildIoVec().get(), message.IoVecSize())) { + QUIC_LOG(ERROR) << "send failed"; + return false; + } + + UnknownParser parser; + if (!Recv(seq_++, &parser)) { + QUIC_LOG(ERROR) << "receive failed."; + return false; + } + return true; +} + +namespace { + +class IpRuleParser : public NetlinkParserInterface { + public: + explicit IpRuleParser(std::vector<Netlink::IpRule>* ip_rules) + : ip_rules_(ip_rules) {} + + void Run(struct nlmsghdr* netlink_message) override { + if (netlink_message->nlmsg_type != RTM_NEWRULE) { + QUIC_LOG(WARNING) << QuicStrCat( + "Unexpected nlmsg_type: ", netlink_message->nlmsg_type, + " expected: ", RTM_NEWRULE); + return; + } + + auto* rule = reinterpret_cast<rtmsg*>(NLMSG_DATA(netlink_message)); + int payload_length = RTM_PAYLOAD(netlink_message); + + if (rule->rtm_family != AF_INET6) { + QUIC_LOG(ERROR) << QuicStrCat("Unexpected family: ", rule->rtm_family); + return; + } + + Netlink::IpRule ip_rule; + ip_rule.table = rule->rtm_table; + + struct rtattr* rta; + for (rta = RTM_RTA(rule); RTA_OK(rta, payload_length); + rta = RTA_NEXT(rta, payload_length)) { + switch (rta->rta_type) { + case RTA_TABLE: { + ip_rule.table = *reinterpret_cast<uint32_t*>(RTA_DATA(rta)); + break; + } + case RTA_SRC: { + QuicIpAddress src_addr; + src_addr.FromPackedString(reinterpret_cast<char*>(RTA_DATA(rta)), + RTA_PAYLOAD(rta)); + IpRange src_range(src_addr, rule->rtm_src_len); + ip_rule.source_range = src_range; + break; + } + default: { + QUIC_VLOG(2) << QuicStrCat("Uninteresting attribute: ", + rta->rta_type); + } + } + } + ip_rules_->emplace_back(ip_rule); + } + + private: + std::vector<Netlink::IpRule>* ip_rules_; +}; + +} // namespace + +bool Netlink::GetRuleInfo(std::vector<Netlink::IpRule>* ip_rules) { + rtmsg rule_message{}; + rule_message.rtm_family = AF_INET6; + + auto message = RuleMessage::New(RtnetlinkMessage::Operation::GET, + NLM_F_REQUEST | NLM_F_DUMP, seq_, getpid(), + &rule_message); + + if (!Send(message.BuildIoVec().get(), message.IoVecSize())) { + QUIC_LOG(ERROR) << "send failed"; + return false; + } + + IpRuleParser parser(ip_rules); + if (!Recv(seq_++, &parser)) { + QUIC_LOG(ERROR) << "receive failed."; + return false; + } + return true; +} + +bool Netlink::ChangeRule(Verb verb, uint32_t table, IpRange source_range) { + RtnetlinkMessage::Operation operation; + uint16_t flags = NLM_F_REQUEST | NLM_F_ACK; + + rtmsg rule_message{}; + rule_message.rtm_family = AF_INET6; + rule_message.rtm_protocol = RTPROT_STATIC; + rule_message.rtm_scope = RT_SCOPE_UNIVERSE; + rule_message.rtm_table = RT_TABLE_UNSPEC; + + rule_message.rtm_flags |= FIB_RULE_FIND_SADDR; + + switch (verb) { + case Verb::kAdd: + if (!source_range.IsInitialized()) { + QUIC_LOG(ERROR) << "Source range must be initialized."; + return false; + } + operation = RtnetlinkMessage::Operation::NEW; + flags |= NLM_F_EXCL | NLM_F_CREATE; + rule_message.rtm_type = FRA_DST; + rule_message.rtm_src_len = source_range.prefix_length(); + break; + case Verb::kRemove: + operation = RtnetlinkMessage::Operation::DEL; + break; + case Verb::kReplace: + QUIC_LOG(ERROR) << "Unsupported verb: kReplace"; + return false; + } + auto message = + RuleMessage::New(operation, flags, seq_, getpid(), &rule_message); + + message.AppendAttribute(RTA_TABLE, &table, sizeof(table)); + + if (source_range.IsInitialized()) { + std::string packed_src = source_range.prefix().ToPackedString(); + message.AppendAttribute(RTA_SRC, + reinterpret_cast<const void*>(packed_src.c_str()), + packed_src.size()); + } + + if (!Send(message.BuildIoVec().get(), message.IoVecSize())) { + QUIC_LOG(ERROR) << "send failed"; + return false; + } + + UnknownParser parser; + if (!Recv(seq_++, &parser)) { + QUIC_LOG(ERROR) << "receive failed."; + return false; + } + return true; +} + +bool Netlink::Send(struct iovec* iov, size_t iovlen) { + if (!OpenSocket()) { + QUIC_LOG(ERROR) << "can't open socket"; + return false; + } + + // an address for communicating with the kernel netlink code + sockaddr_nl netlink_address; + memset(&netlink_address, 0, sizeof(netlink_address)); + netlink_address.nl_family = AF_NETLINK; + netlink_address.nl_pid = 0; // destination is kernel + netlink_address.nl_groups = 0; // no multicast + + struct msghdr msg = { + &netlink_address, sizeof(netlink_address), iov, iovlen, nullptr, 0, 0}; + + if (kernel_->sendmsg(socket_fd_, &msg, 0) < 0) { + QUIC_LOG(ERROR) << "sendmsg failed"; + CloseSocket(); + return false; + } + + return true; +} + +bool Netlink::Recv(uint32_t seq, NetlinkParserInterface* parser) { + sockaddr_nl netlink_address; + + // replies can span multiple packets + for (;;) { + socklen_t address_length = sizeof(netlink_address); + + // First, call recvfrom with buffer size of 0 and MSG_PEEK | MSG_TRUNC set + // so that we know the size of the incoming packet before actually receiving + // it. + int next_packet_size = kernel_->recvfrom( + socket_fd_, recvbuf_.get(), /* len = */ 0, MSG_PEEK | MSG_TRUNC, + reinterpret_cast<struct sockaddr*>(&netlink_address), &address_length); + if (next_packet_size < 0) { + QUIC_LOG(ERROR) + << "error recvfrom with MSG_PEEK | MSG_TRUNC to get packet length."; + CloseSocket(); + return false; + } + QUIC_VLOG(3) << "netlink packet size: " << next_packet_size; + if (next_packet_size > recvbuf_length_) { + QUIC_VLOG(2) << "resizing recvbuf to " << next_packet_size; + ResetRecvBuf(next_packet_size); + } + + // Get the packet for real. + memset(recvbuf_.get(), 0, recvbuf_length_); + int len = kernel_->recvfrom( + socket_fd_, recvbuf_.get(), recvbuf_length_, /* flags = */ 0, + reinterpret_cast<struct sockaddr*>(&netlink_address), &address_length); + QUIC_VLOG(3) << "recvfrom returned: " << len; + if (len < 0) { + QUIC_LOG(INFO) << "can't receive netlink packet"; + CloseSocket(); + return false; + } + + // there may be multiple nlmsg's in each reply packet + struct nlmsghdr* netlink_message; + for (netlink_message = reinterpret_cast<struct nlmsghdr*>(recvbuf_.get()); + NLMSG_OK(netlink_message, len); + netlink_message = NLMSG_NEXT(netlink_message, len)) { + QUIC_VLOG(3) << "netlink_message->nlmsg_type = " + << netlink_message->nlmsg_type; + // make sure this is to us + if (netlink_message->nlmsg_seq != seq) { + QUIC_LOG(INFO) << "netlink_message not meant for us." + << " seq: " << seq + << " nlmsg_seq: " << netlink_message->nlmsg_seq; + continue; + } + + // done with this whole reply (not just this particular packet) + if (netlink_message->nlmsg_type == NLMSG_DONE) { + return true; + } + if (netlink_message->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr* err = + reinterpret_cast<struct nlmsgerr*>(NLMSG_DATA(netlink_message)); + if (netlink_message->nlmsg_len < + NLMSG_LENGTH(sizeof(struct nlmsgerr))) { + QUIC_LOG(INFO) << "netlink_message ERROR truncated"; + } else { + // an ACK + if (err->error == 0) { + QUIC_VLOG(3) << "Netlink sent an ACK"; + return true; + } + QUIC_LOG(INFO) << "netlink_message ERROR: " << err->error; + } + return false; + } + + parser->Run(netlink_message); + } + } +} + +} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/qbone/platform/netlink.h b/chromium/net/third_party/quiche/src/quic/qbone/platform/netlink.h new file mode 100644 index 00000000000..591da0f7ed0 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/platform/netlink.h @@ -0,0 +1,142 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef QUICHE_QUIC_QBONE_PLATFORM_NETLINK_H_ +#define QUICHE_QUIC_QBONE_PLATFORM_NETLINK_H_ + +#include <linux/netlink.h> +#include <linux/rtnetlink.h> + +#include <cstdint> +#include <functional> +#include <memory> +#include <string> +#include <vector> + +#include "net/third_party/quiche/src/quic/platform/api/quic_ip_address.h" +#include "net/third_party/quiche/src/quic/qbone/platform/ip_range.h" +#include "net/third_party/quiche/src/quic/qbone/platform/kernel_interface.h" +#include "net/third_party/quiche/src/quic/qbone/platform/netlink_interface.h" + +namespace quic { + +// A wrapper class to provide convenient methods of manipulating IP address and +// routing table using netlink (man 7 netlink) socket. More specifically, +// rtnetlink is used (man 7 rtnetlink). +// +// This class is not thread safe, but thread compatible, as long as callers can +// make sure Send and Recv pairs are executed in sequence for a particular +// query. +class Netlink : public NetlinkInterface { + public: + explicit Netlink(KernelInterface* kernel); + ~Netlink() override; + + // Gets the link information for the interface referred by the given + // interface_name. + // + // This is a synchronous communication. That should not be a problem since the + // kernel should answer immediately. + bool GetLinkInfo(const string& interface_name, LinkInfo* link_info) override; + + // Gets the addresses for the given interface referred by the given + // interface_index. + // + // This is a synchronous communication. This should not be a problem since the + // kernel should answer immediately. + bool GetAddresses(int interface_index, + uint8_t unwanted_flags, + std::vector<AddressInfo>* addresses, + int* num_ipv6_nodad_dadfailed_addresses) override; + + // Performs the given verb that modifies local addresses on the given + // interface_index. + // + // additional_attributes are RTAs (man 7 rtnelink) that will be sent together + // with the netlink message. Note that rta_len in each RTA is used to decide + // the length of the payload. The caller is responsible for making sure + // payload bytes are accessible after the RTA header. + bool ChangeLocalAddress( + uint32_t interface_index, + Verb verb, + const QuicIpAddress& address, + uint8_t prefix_length, + uint8_t ifa_flags, + uint8_t ifa_scope, + const std::vector<struct rtattr*>& additional_attributes) override; + + // Gets the list of routing rules from the main routing table (RT_TABLE_MAIN), + // which is programmable. + // + // This is a synchronous communication. This should not be a problem since the + // kernel should answer immediately. + bool GetRouteInfo(std::vector<RoutingRule>* routing_rules) override; + + // Performs the given Verb on the matching rule in the main routing table + // (RT_TABLE_MAIN). + // + // preferred_source can be !IsInitialized(), in which case it will be omitted. + // + // For Verb::kRemove, rule matching is done by (destination_subnet, scope, + // preferred_source, interface_index). Return true if a matching rule is + // found. interface_index can be 0 for wilecard. + // + // For Verb::kAdd, rule matching is done by destination_subnet. If a rule for + // the given destination_subnet already exists, nothing will happen and false + // is returned. + // + // For Verb::kReplace, rule matching is done by destination_subnet. If no + // matching rule is found, a new entry will be created. + bool ChangeRoute(Netlink::Verb verb, + uint32_t table, + const IpRange& destination_subnet, + uint8_t scope, + QuicIpAddress preferred_source, + int32_t interface_index) override; + + // Returns the set of all rules in the routing policy database. + bool GetRuleInfo(std::vector<Netlink::IpRule>* ip_rules) override; + + // Performs the give verb on the matching rule in the routing policy database. + // When deleting a rule, the |source_range| may be unspecified, in which case + // the lowest priority rule from |table| will be removed. When adding a rule, + // the |source_address| must be specified. + bool ChangeRule(Verb verb, uint32_t table, IpRange source_range) override; + + // Sends a netlink message to the kernel. iov and iovlen represents an array + // of struct iovec to be fed into sendmsg. The caller needs to make sure the + // message conform to what's expected by NLMSG_* macros. + // + // This can be useful if more flexibility is needed than the provided + // convenient methods can provide. + bool Send(struct iovec* iov, size_t iovlen) override; + + // Receives a netlink message from the kernel. + // parser will be called on the caller's stack. + // + // This can be useful if more flexibility is needed than the provided + // convenient methods can provide. + // TODO(b/69412655): vectorize this. + bool Recv(uint32_t seq, NetlinkParserInterface* parser) override; + + private: + // Reset the size of recvbuf_ to size. If size is 0, recvbuf_ will be nullptr. + void ResetRecvBuf(size_t size); + + // Opens a netlink socket if not already opened. + bool OpenSocket(); + + // Closes the opened netlink socket. Noop if no netlink socket is opened. + void CloseSocket(); + + KernelInterface* kernel_; + int socket_fd_ = -1; + std::unique_ptr<char[]> recvbuf_ = nullptr; + size_t recvbuf_length_ = 0; + uint32_t seq_; // next msg sequence number +}; + +} // namespace quic + +#endif // QUICHE_QUIC_QBONE_PLATFORM_NETLINK_H_ diff --git a/chromium/net/third_party/quiche/src/quic/qbone/platform/netlink_interface.h b/chromium/net/third_party/quiche/src/quic/qbone/platform/netlink_interface.h new file mode 100644 index 00000000000..447c8b2014f --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/platform/netlink_interface.h @@ -0,0 +1,146 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef QUICHE_QUIC_QBONE_PLATFORM_NETLINK_INTERFACE_H_ +#define QUICHE_QUIC_QBONE_PLATFORM_NETLINK_INTERFACE_H_ + +#include "net/third_party/quiche/src/quic/platform/api/quic_ip_address.h" +#include "net/third_party/quiche/src/quic/qbone/platform/ip_range.h" + +namespace quic { + +constexpr int kHwAddrSize = 6; + +class NetlinkParserInterface { + public: + virtual ~NetlinkParserInterface() {} + virtual void Run(struct nlmsghdr* netlink_message) = 0; +}; + +// An interface providing convenience methods for manipulating IP address and +// routing table using netlink (man 7 netlink) socket. +class NetlinkInterface { + public: + virtual ~NetlinkInterface() = default; + + // Link information returned from GetLinkInfo. + struct LinkInfo { + int index; + uint8_t type; + uint8_t hardware_address[kHwAddrSize]; + uint8_t broadcast_address[kHwAddrSize]; + size_t hardware_address_length; // 0 if no hardware address found + size_t broadcast_address_length; // 0 if no broadcast address found + }; + + // Gets the link information for the interface referred by the given + // interface_name. + virtual bool GetLinkInfo(const string& interface_name, + LinkInfo* link_info) = 0; + + // Address information reported back from GetAddresses. + struct AddressInfo { + QuicIpAddress local_address; + QuicIpAddress interface_address; + uint8_t prefix_length = 0; + uint8_t scope = 0; + }; + + // Gets the addresses for the given interface referred by the given + // interface_index. + virtual bool GetAddresses(int interface_index, + uint8_t unwanted_flags, + std::vector<AddressInfo>* addresses, + int* num_ipv6_nodad_dadfailed_addresses) = 0; + + enum class Verb { + kAdd, + kRemove, + kReplace, + }; + + // Performs the given verb that modifies local addresses on the given + // interface_index. + // + // additional_attributes are RTAs (man 7 rtnelink) that will be sent together + // with the netlink message. Note that rta_len in each RTA is used to decide + // the length of the payload. The caller is responsible for making sure + // payload bytes are accessible after the RTA header. + virtual bool ChangeLocalAddress( + uint32_t interface_index, + Verb verb, + const QuicIpAddress& address, + uint8_t prefix_length, + uint8_t ifa_flags, + uint8_t ifa_scope, + const std::vector<struct rtattr*>& additional_attributes) = 0; + + // Routing rule reported back from GetRouteInfo. + struct RoutingRule { + uint32_t table; + IpRange destination_subnet; + QuicIpAddress preferred_source; + uint8_t scope; + int out_interface; + }; + + struct IpRule { + uint32_t table; + IpRange source_range; + }; + + // Gets the list of routing rules from the main routing table (RT_TABLE_MAIN), + // which is programmable. + virtual bool GetRouteInfo(std::vector<RoutingRule>* routing_rules) = 0; + + // Performs the given Verb on the matching rule in the main routing table + // (RT_TABLE_MAIN). + // + // preferred_source can be !IsInitialized(), in which case it will be omitted. + // + // For Verb::kRemove, rule matching is done by (destination_subnet, scope, + // preferred_source, interface_index). Return true if a matching rule is + // found. interface_index can be 0 for wilecard. + // + // For Verb::kAdd, rule matching is done by destination_subnet. If a rule for + // the given destination_subnet already exists, nothing will happen and false + // is returned. + // + // For Verb::kReplace, rule matching is done by destination_subnet. If no + // matching rule is found, a new entry will be created. + virtual bool ChangeRoute(Verb verb, + uint32_t table, + const IpRange& destination_subnet, + uint8_t scope, + QuicIpAddress preferred_source, + int32_t interface_index) = 0; + + // Returns the set of all rules in the routing policy database. + virtual bool GetRuleInfo(std::vector<IpRule>* ip_rules) = 0; + + // Performs the give verb on the matching rule in the routing policy database. + // When deleting a rule, the |source_range| may be unspecified, in which case + // the lowest priority rule from |table| will be removed. When adding a rule, + // the |source_address| must be specified. + virtual bool ChangeRule(Verb verb, uint32_t table, IpRange source_range) = 0; + + // Sends a netlink message to the kernel. iov and iovlen represents an array + // of struct iovec to be fed into sendmsg. The caller needs to make sure the + // message conform to what's expected by NLMSG_* macros. + // + // This can be useful if more flexibility is needed than the provided + // convenient methods can provide. + virtual bool Send(struct iovec* iov, size_t iovlen) = 0; + + // Receives a netlink message from the kernel. + // parser will be called on the caller's stack. + // + // This can be useful if more flexibility is needed than the provided + // convenient methods can provide. + virtual bool Recv(uint32_t seq, NetlinkParserInterface* parser) = 0; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_QBONE_PLATFORM_NETLINK_INTERFACE_H_ diff --git a/chromium/net/third_party/quiche/src/quic/qbone/platform/netlink_test.cc b/chromium/net/third_party/quiche/src/quic/qbone/platform/netlink_test.cc new file mode 100644 index 00000000000..024e0fb4801 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/platform/netlink_test.cc @@ -0,0 +1,763 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/third_party/quiche/src/quic/qbone/platform/netlink.h" + +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/qbone/platform/mock_kernel.h" +#include "net/third_party/quiche/src/quic/qbone/qbone_constants.h" + +namespace quic { +namespace { + +using ::testing::_; +using ::testing::Contains; +using ::testing::InSequence; +using ::testing::Invoke; +using ::testing::Return; +using ::testing::Unused; + +const int kSocketFd = 101; + +class NetlinkTest : public QuicTest { + protected: + NetlinkTest() { + ON_CALL(mock_kernel_, socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) + .WillByDefault(Invoke([this](Unused, Unused, Unused) { + EXPECT_CALL(mock_kernel_, close(kSocketFd)).WillOnce(Return(0)); + return kSocketFd; + })); + } + + void ExpectNetlinkPacket( + uint16_t type, + uint16_t flags, + const std::function<ssize_t(void* buf, size_t len, int seq)>& + recv_callback, + const std::function<void(const void* buf, size_t len)>& send_callback = + nullptr) { + static int seq = -1; + InSequence s; + + EXPECT_CALL(mock_kernel_, sendmsg(kSocketFd, _, _)) + .WillOnce(Invoke([this, type, flags, send_callback]( + Unused, const struct msghdr* msg, int) { + EXPECT_EQ(sizeof(struct sockaddr_nl), msg->msg_namelen); + auto* nl_addr = + reinterpret_cast<const struct sockaddr_nl*>(msg->msg_name); + EXPECT_EQ(AF_NETLINK, nl_addr->nl_family); + EXPECT_EQ(0, nl_addr->nl_pid); + EXPECT_EQ(0, nl_addr->nl_groups); + + EXPECT_GE(msg->msg_iovlen, 1); + EXPECT_GE(msg->msg_iov[0].iov_len, sizeof(struct nlmsghdr)); + + string buf; + for (int i = 0; i < msg->msg_iovlen; i++) { + buf.append(string(reinterpret_cast<char*>(msg->msg_iov[i].iov_base), + msg->msg_iov[i].iov_len)); + } + + auto* netlink_message = + reinterpret_cast<const struct nlmsghdr*>(buf.c_str()); + EXPECT_EQ(type, netlink_message->nlmsg_type); + EXPECT_EQ(flags, netlink_message->nlmsg_flags); + EXPECT_GE(buf.size(), netlink_message->nlmsg_len); + + if (send_callback != nullptr) { + send_callback(buf.c_str(), buf.size()); + } + + CHECK_EQ(seq, -1); + seq = netlink_message->nlmsg_seq; + return buf.size(); + })); + + EXPECT_CALL(mock_kernel_, + recvfrom(kSocketFd, _, 0, MSG_PEEK | MSG_TRUNC, _, _)) + .WillOnce(Invoke([this, recv_callback](Unused, Unused, Unused, Unused, + struct sockaddr* src_addr, + socklen_t* addrlen) { + auto* nl_addr = reinterpret_cast<struct sockaddr_nl*>(src_addr); + nl_addr->nl_family = AF_NETLINK; + nl_addr->nl_pid = 0; // from kernel + nl_addr->nl_groups = 0; // no multicast + + int ret = recv_callback(reply_packet_, sizeof(reply_packet_), seq); + CHECK_LE(ret, sizeof(reply_packet_)); + return ret; + })); + + EXPECT_CALL(mock_kernel_, recvfrom(kSocketFd, _, _, _, _, _)) + .WillOnce(Invoke([recv_callback](Unused, void* buf, size_t len, Unused, + struct sockaddr* src_addr, + socklen_t* addrlen) { + auto* nl_addr = reinterpret_cast<struct sockaddr_nl*>(src_addr); + nl_addr->nl_family = AF_NETLINK; + nl_addr->nl_pid = 0; // from kernel + nl_addr->nl_groups = 0; // no multicast + + int ret = recv_callback(buf, len, seq); + EXPECT_GE(len, ret); + seq = -1; + return ret; + })); + } + + char reply_packet_[4096]; + MockKernel mock_kernel_; +}; + +void AddRTA(struct nlmsghdr* netlink_message, + uint16_t type, + const void* data, + size_t len) { + auto* next_header_ptr = reinterpret_cast<char*>(netlink_message) + + NLMSG_ALIGN(netlink_message->nlmsg_len); + + auto* rta = reinterpret_cast<struct rtattr*>(next_header_ptr); + rta->rta_type = type; + rta->rta_len = RTA_LENGTH(len); + memcpy(RTA_DATA(rta), data, len); + + netlink_message->nlmsg_len = + NLMSG_ALIGN(netlink_message->nlmsg_len) + RTA_LENGTH(len); +} + +void CreateIfinfomsg(struct nlmsghdr* netlink_message, + const string& interface_name, + uint16_t type, + int index, + unsigned int flags, + unsigned int change, + uint8_t address[], + int address_len, + uint8_t broadcast[], + int broadcast_len) { + auto* interface_info = + reinterpret_cast<struct ifinfomsg*>(NLMSG_DATA(netlink_message)); + interface_info->ifi_family = AF_UNSPEC; + interface_info->ifi_type = type; + interface_info->ifi_index = index; + interface_info->ifi_flags = flags; + interface_info->ifi_change = change; + netlink_message->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); + + // Add address + AddRTA(netlink_message, IFLA_ADDRESS, address, address_len); + + // Add broadcast address + AddRTA(netlink_message, IFLA_BROADCAST, broadcast, broadcast_len); + + // Add name + AddRTA(netlink_message, IFLA_IFNAME, interface_name.c_str(), + interface_name.size()); +} + +struct nlmsghdr* CreateNetlinkMessage(void* buf, // NOLINT + struct nlmsghdr* previous_netlink_message, + uint16_t type, + int seq) { + auto* next_header_ptr = reinterpret_cast<char*>(buf); + if (previous_netlink_message != nullptr) { + next_header_ptr = reinterpret_cast<char*>(previous_netlink_message) + + NLMSG_ALIGN(previous_netlink_message->nlmsg_len); + } + auto* netlink_message = reinterpret_cast<nlmsghdr*>(next_header_ptr); + netlink_message->nlmsg_len = NLMSG_LENGTH(0); + netlink_message->nlmsg_type = type; + netlink_message->nlmsg_flags = NLM_F_MULTI; + netlink_message->nlmsg_pid = 0; // from the kernel + netlink_message->nlmsg_seq = seq; + + return netlink_message; +} + +void CreateIfaddrmsg(struct nlmsghdr* nlm, + int interface_index, + unsigned char prefixlen, + unsigned char flags, + unsigned char scope, + QuicIpAddress ip) { + CHECK(ip.IsInitialized()); + unsigned char family; + switch (ip.address_family()) { + case IpAddressFamily::IP_V4: + family = AF_INET; + break; + case IpAddressFamily::IP_V6: + family = AF_INET6; + break; + default: + QUIC_BUG << absl::StrCat("unexpected address family: ", + ip.address_family()); + family = AF_UNSPEC; + } + auto* msg = reinterpret_cast<struct ifaddrmsg*>(NLMSG_DATA(nlm)); + msg->ifa_family = family; + msg->ifa_prefixlen = prefixlen; + msg->ifa_flags = flags; + msg->ifa_scope = scope; + msg->ifa_index = interface_index; + nlm->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); + + // Add local address + AddRTA(nlm, IFA_LOCAL, ip.ToPackedString().c_str(), + ip.ToPackedString().size()); +} + +void CreateRtmsg(struct nlmsghdr* nlm, + unsigned char family, + unsigned char destination_length, + unsigned char source_length, + unsigned char tos, + unsigned char table, + unsigned char protocol, + unsigned char scope, + unsigned char type, + unsigned int flags, + QuicIpAddress destination, + int interface_index) { + auto* msg = reinterpret_cast<struct rtmsg*>(NLMSG_DATA(nlm)); + msg->rtm_family = family; + msg->rtm_dst_len = destination_length; + msg->rtm_src_len = source_length; + msg->rtm_tos = tos; + msg->rtm_table = table; + msg->rtm_protocol = protocol; + msg->rtm_scope = scope; + msg->rtm_type = type; + msg->rtm_flags = flags; + nlm->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); + + // Add destination + AddRTA(nlm, RTA_DST, destination.ToPackedString().c_str(), + destination.ToPackedString().size()); + + // Add egress interface + AddRTA(nlm, RTA_OIF, &interface_index, sizeof(interface_index)); +} + +TEST_F(NetlinkTest, GetLinkInfoWorks) { + auto netlink = QuicMakeUnique<Netlink>(&mock_kernel_); + + uint8_t hwaddr[] = {'a', 'b', 'c', 'd', 'e', 'f'}; + uint8_t bcaddr[] = {'c', 'b', 'a', 'f', 'e', 'd'}; + + ExpectNetlinkPacket( + RTM_GETLINK, NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST, + [this, &hwaddr, &bcaddr](void* buf, size_t len, int seq) { + int ret = 0; + + struct nlmsghdr* netlink_message = + CreateNetlinkMessage(buf, nullptr, RTM_NEWLINK, seq); + CreateIfinfomsg(netlink_message, "tun0", /* type = */ 1, + /* index = */ 7, + /* flags = */ 0, + /* change = */ 0xFFFFFFFF, hwaddr, 6, bcaddr, 6); + ret += NLMSG_ALIGN(netlink_message->nlmsg_len); + + netlink_message = + CreateNetlinkMessage(buf, netlink_message, NLMSG_DONE, seq); + ret += NLMSG_ALIGN(netlink_message->nlmsg_len); + + return ret; + }); + + Netlink::LinkInfo link_info; + EXPECT_TRUE(netlink->GetLinkInfo("tun0", &link_info)); + + EXPECT_EQ(7, link_info.index); + EXPECT_EQ(1, link_info.type); + + for (int i = 0; i < link_info.hardware_address_length; ++i) { + EXPECT_EQ(hwaddr[i], link_info.hardware_address[i]); + } + for (int i = 0; i < link_info.broadcast_address_length; ++i) { + EXPECT_EQ(bcaddr[i], link_info.broadcast_address[i]); + } +} + +TEST_F(NetlinkTest, GetAddressesWorks) { + auto netlink = QuicMakeUnique<Netlink>(&mock_kernel_); + + QuicUnorderedSet<std::string> addresses = {QuicIpAddress::Any4().ToString(), + QuicIpAddress::Any6().ToString()}; + + ExpectNetlinkPacket( + RTM_GETADDR, NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST, + [this, &addresses](void* buf, size_t len, int seq) { + int ret = 0; + + struct nlmsghdr* nlm = nullptr; + + for (const auto& address : addresses) { + QuicIpAddress ip; + ip.FromString(address); + nlm = CreateNetlinkMessage(buf, nlm, RTM_NEWADDR, seq); + CreateIfaddrmsg(nlm, /* interface_index = */ 7, /* prefixlen = */ 24, + /* flags = */ 0, /* scope = */ RT_SCOPE_UNIVERSE, ip); + + ret += NLMSG_ALIGN(nlm->nlmsg_len); + } + + // Create IPs with unwanted flags. + { + QuicIpAddress ip; + ip.FromString("10.0.0.1"); + nlm = CreateNetlinkMessage(buf, nlm, RTM_NEWADDR, seq); + CreateIfaddrmsg(nlm, /* interface_index = */ 7, /* prefixlen = */ 16, + /* flags = */ IFA_F_OPTIMISTIC, /* scope = */ + RT_SCOPE_UNIVERSE, ip); + + ret += NLMSG_ALIGN(nlm->nlmsg_len); + + ip.FromString("10.0.0.2"); + nlm = CreateNetlinkMessage(buf, nlm, RTM_NEWADDR, seq); + CreateIfaddrmsg(nlm, /* interface_index = */ 7, /* prefixlen = */ 16, + /* flags = */ IFA_F_TENTATIVE, /* scope = */ + RT_SCOPE_UNIVERSE, ip); + + ret += NLMSG_ALIGN(nlm->nlmsg_len); + } + + nlm = CreateNetlinkMessage(buf, nlm, NLMSG_DONE, seq); + ret += NLMSG_ALIGN(nlm->nlmsg_len); + + return ret; + }); + + std::vector<Netlink::AddressInfo> reported_addresses; + int num_ipv6_nodad_dadfailed_addresses = 0; + EXPECT_TRUE(netlink->GetAddresses(7, IFA_F_TENTATIVE | IFA_F_OPTIMISTIC, + &reported_addresses, + &num_ipv6_nodad_dadfailed_addresses)); + + for (const auto& reported_address : reported_addresses) { + EXPECT_TRUE(reported_address.local_address.IsInitialized()); + EXPECT_FALSE(reported_address.interface_address.IsInitialized()); + EXPECT_THAT(addresses, Contains(reported_address.local_address.ToString())); + addresses.erase(reported_address.local_address.ToString()); + + EXPECT_EQ(24, reported_address.prefix_length); + } + + EXPECT_TRUE(addresses.empty()); +} + +TEST_F(NetlinkTest, ChangeLocalAddressAdd) { + auto netlink = QuicMakeUnique<Netlink>(&mock_kernel_); + + QuicIpAddress ip = QuicIpAddress::Any6(); + ExpectNetlinkPacket( + RTM_NEWADDR, NLM_F_ACK | NLM_F_REQUEST, + [](void* buf, size_t len, int seq) { + struct nlmsghdr* netlink_message = + CreateNetlinkMessage(buf, nullptr, NLMSG_ERROR, seq); + auto* err = + reinterpret_cast<struct nlmsgerr*>(NLMSG_DATA(netlink_message)); + // Ack the request + err->error = 0; + netlink_message->nlmsg_len = NLMSG_LENGTH(sizeof(struct nlmsgerr)); + return netlink_message->nlmsg_len; + }, + [ip](const void* buf, size_t len) { + auto* netlink_message = reinterpret_cast<const struct nlmsghdr*>(buf); + auto* ifa = reinterpret_cast<const struct ifaddrmsg*>( + NLMSG_DATA(netlink_message)); + EXPECT_EQ(19, ifa->ifa_prefixlen); + EXPECT_EQ(RT_SCOPE_UNIVERSE, ifa->ifa_scope); + EXPECT_EQ(IFA_F_PERMANENT, ifa->ifa_flags); + EXPECT_EQ(7, ifa->ifa_index); + EXPECT_EQ(AF_INET6, ifa->ifa_family); + + const struct rtattr* rta; + int payload_length = IFA_PAYLOAD(netlink_message); + int num_rta = 0; + for (rta = IFA_RTA(ifa); RTA_OK(rta, payload_length); + rta = RTA_NEXT(rta, payload_length)) { + switch (rta->rta_type) { + case IFA_LOCAL: { + EXPECT_EQ(ip.ToPackedString().size(), RTA_PAYLOAD(rta)); + const auto* raw_address = + reinterpret_cast<const char*>(RTA_DATA(rta)); + ASSERT_EQ(sizeof(in6_addr), RTA_PAYLOAD(rta)); + QuicIpAddress address; + address.FromPackedString(raw_address, RTA_PAYLOAD(rta)); + EXPECT_EQ(ip, address); + break; + } + case IFA_CACHEINFO: { + EXPECT_EQ(sizeof(struct ifa_cacheinfo), RTA_PAYLOAD(rta)); + const auto* cache_info = + reinterpret_cast<const struct ifa_cacheinfo*>(RTA_DATA(rta)); + EXPECT_EQ(8, cache_info->ifa_prefered); // common_typos_disable + EXPECT_EQ(6, cache_info->ifa_valid); + EXPECT_EQ(4, cache_info->cstamp); + EXPECT_EQ(2, cache_info->tstamp); + break; + } + default: + EXPECT_TRUE(false) << "Seeing rtattr that should not exist"; + } + ++num_rta; + } + EXPECT_EQ(2, num_rta); + }); + + struct { + struct rtattr rta; + struct ifa_cacheinfo cache_info; + } additional_rta; + + additional_rta.rta.rta_type = IFA_CACHEINFO; + additional_rta.rta.rta_len = RTA_LENGTH(sizeof(struct ifa_cacheinfo)); + additional_rta.cache_info.ifa_prefered = 8; + additional_rta.cache_info.ifa_valid = 6; + additional_rta.cache_info.cstamp = 4; + additional_rta.cache_info.tstamp = 2; + + EXPECT_TRUE(netlink->ChangeLocalAddress(7, Netlink::Verb::kAdd, ip, 19, + IFA_F_PERMANENT, RT_SCOPE_UNIVERSE, + {&additional_rta.rta})); +} + +TEST_F(NetlinkTest, ChangeLocalAddressRemove) { + auto netlink = QuicMakeUnique<Netlink>(&mock_kernel_); + + QuicIpAddress ip = QuicIpAddress::Any4(); + ExpectNetlinkPacket( + RTM_DELADDR, NLM_F_ACK | NLM_F_REQUEST, + [](void* buf, size_t len, int seq) { + struct nlmsghdr* netlink_message = + CreateNetlinkMessage(buf, nullptr, NLMSG_ERROR, seq); + auto* err = + reinterpret_cast<struct nlmsgerr*>(NLMSG_DATA(netlink_message)); + // Ack the request + err->error = 0; + netlink_message->nlmsg_len = NLMSG_LENGTH(sizeof(struct nlmsgerr)); + return netlink_message->nlmsg_len; + }, + [ip](const void* buf, size_t len) { + auto* netlink_message = reinterpret_cast<const struct nlmsghdr*>(buf); + auto* ifa = reinterpret_cast<const struct ifaddrmsg*>( + NLMSG_DATA(netlink_message)); + EXPECT_EQ(32, ifa->ifa_prefixlen); + EXPECT_EQ(RT_SCOPE_UNIVERSE, ifa->ifa_scope); + EXPECT_EQ(0, ifa->ifa_flags); + EXPECT_EQ(7, ifa->ifa_index); + EXPECT_EQ(AF_INET, ifa->ifa_family); + + const struct rtattr* rta; + int payload_length = IFA_PAYLOAD(netlink_message); + int num_rta = 0; + for (rta = IFA_RTA(ifa); RTA_OK(rta, payload_length); + rta = RTA_NEXT(rta, payload_length)) { + switch (rta->rta_type) { + case IFA_LOCAL: { + const auto* raw_address = + reinterpret_cast<const char*>(RTA_DATA(rta)); + ASSERT_EQ(sizeof(in_addr), RTA_PAYLOAD(rta)); + QuicIpAddress address; + address.FromPackedString(raw_address, RTA_PAYLOAD(rta)); + EXPECT_EQ(ip, address); + break; + } + default: + EXPECT_TRUE(false) << "Seeing rtattr that should not exist"; + } + ++num_rta; + } + EXPECT_EQ(1, num_rta); + }); + + EXPECT_TRUE(netlink->ChangeLocalAddress(7, Netlink::Verb::kRemove, ip, 32, 0, + RT_SCOPE_UNIVERSE, {})); +} + +TEST_F(NetlinkTest, GetRouteInfoWorks) { + auto netlink = QuicMakeUnique<Netlink>(&mock_kernel_); + + QuicIpAddress destination; + ASSERT_TRUE(destination.FromString("f800::2")); + ExpectNetlinkPacket(RTM_GETROUTE, NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST, + [destination](void* buf, size_t len, int seq) { + int ret = 0; + struct nlmsghdr* netlink_message = CreateNetlinkMessage( + buf, nullptr, RTM_NEWROUTE, seq); + CreateRtmsg(netlink_message, AF_INET6, 48, 0, 0, + RT_TABLE_MAIN, RTPROT_STATIC, RT_SCOPE_LINK, + RTN_UNICAST, 0, destination, 7); + ret += NLMSG_ALIGN(netlink_message->nlmsg_len); + + netlink_message = CreateNetlinkMessage( + buf, netlink_message, NLMSG_DONE, seq); + ret += NLMSG_ALIGN(netlink_message->nlmsg_len); + + QUIC_LOG(INFO) << "ret: " << ret; + return ret; + }); + + std::vector<Netlink::RoutingRule> routing_rules; + EXPECT_TRUE(netlink->GetRouteInfo(&routing_rules)); + + ASSERT_EQ(1, routing_rules.size()); + EXPECT_EQ(RT_SCOPE_LINK, routing_rules[0].scope); + EXPECT_EQ(IpRange(destination, 48).ToString(), + routing_rules[0].destination_subnet.ToString()); + EXPECT_FALSE(routing_rules[0].preferred_source.IsInitialized()); + EXPECT_EQ(7, routing_rules[0].out_interface); +} + +TEST_F(NetlinkTest, ChangeRouteAdd) { + auto netlink = QuicMakeUnique<Netlink>(&mock_kernel_); + + QuicIpAddress preferred_ip; + preferred_ip.FromString("ff80:dead:beef::1"); + IpRange subnet; + subnet.FromString("ff80:dead:beef::/48"); + int egress_interface_index = 7; + ExpectNetlinkPacket( + RTM_NEWROUTE, NLM_F_ACK | NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL, + [](void* buf, size_t len, int seq) { + struct nlmsghdr* netlink_message = + CreateNetlinkMessage(buf, nullptr, NLMSG_ERROR, seq); + auto* err = + reinterpret_cast<struct nlmsgerr*>(NLMSG_DATA(netlink_message)); + // Ack the request + err->error = 0; + netlink_message->nlmsg_len = NLMSG_LENGTH(sizeof(struct nlmsgerr)); + return netlink_message->nlmsg_len; + }, + [preferred_ip, subnet, egress_interface_index](const void* buf, + size_t len) { + auto* netlink_message = reinterpret_cast<const struct nlmsghdr*>(buf); + auto* rtm = + reinterpret_cast<const struct rtmsg*>(NLMSG_DATA(netlink_message)); + EXPECT_EQ(AF_INET6, rtm->rtm_family); + EXPECT_EQ(48, rtm->rtm_dst_len); + EXPECT_EQ(0, rtm->rtm_src_len); + EXPECT_EQ(RT_TABLE_MAIN, rtm->rtm_table); + EXPECT_EQ(RTPROT_STATIC, rtm->rtm_protocol); + EXPECT_EQ(RT_SCOPE_LINK, rtm->rtm_scope); + EXPECT_EQ(RTN_UNICAST, rtm->rtm_type); + + const struct rtattr* rta; + int payload_length = RTM_PAYLOAD(netlink_message); + int num_rta = 0; + for (rta = RTM_RTA(rtm); RTA_OK(rta, payload_length); + rta = RTA_NEXT(rta, payload_length)) { + switch (rta->rta_type) { + case RTA_PREFSRC: { + const auto* raw_address = + reinterpret_cast<const char*>(RTA_DATA(rta)); + ASSERT_EQ(sizeof(struct in6_addr), RTA_PAYLOAD(rta)); + QuicIpAddress address; + address.FromPackedString(raw_address, RTA_PAYLOAD(rta)); + EXPECT_EQ(preferred_ip, address); + break; + } + case RTA_OIF: { + ASSERT_EQ(sizeof(int), RTA_PAYLOAD(rta)); + const auto* interface_index = + reinterpret_cast<const int*>(RTA_DATA(rta)); + EXPECT_EQ(egress_interface_index, *interface_index); + break; + } + case RTA_DST: { + const auto* raw_address = + reinterpret_cast<const char*>(RTA_DATA(rta)); + ASSERT_EQ(sizeof(struct in6_addr), RTA_PAYLOAD(rta)); + QuicIpAddress address; + address.FromPackedString(raw_address, RTA_PAYLOAD(rta)); + EXPECT_EQ(subnet.ToString(), + IpRange(address, rtm->rtm_dst_len).ToString()); + break; + } + case RTA_TABLE: { + ASSERT_EQ(*reinterpret_cast<uint32_t*>(RTA_DATA(rta)), + QboneConstants::kQboneRouteTableId); + break; + } + default: + EXPECT_TRUE(false) << "Seeing rtattr that should not be sent"; + } + ++num_rta; + } + EXPECT_EQ(4, num_rta); + }); + EXPECT_TRUE(netlink->ChangeRoute( + Netlink::Verb::kAdd, QboneConstants::kQboneRouteTableId, subnet, + RT_SCOPE_LINK, preferred_ip, egress_interface_index)); +} + +TEST_F(NetlinkTest, ChangeRouteRemove) { + auto netlink = QuicMakeUnique<Netlink>(&mock_kernel_); + + QuicIpAddress preferred_ip; + preferred_ip.FromString("ff80:dead:beef::1"); + IpRange subnet; + subnet.FromString("ff80:dead:beef::/48"); + int egress_interface_index = 7; + ExpectNetlinkPacket( + RTM_DELROUTE, NLM_F_ACK | NLM_F_REQUEST, + [](void* buf, size_t len, int seq) { + struct nlmsghdr* netlink_message = + CreateNetlinkMessage(buf, nullptr, NLMSG_ERROR, seq); + auto* err = + reinterpret_cast<struct nlmsgerr*>(NLMSG_DATA(netlink_message)); + // Ack the request + err->error = 0; + netlink_message->nlmsg_len = NLMSG_LENGTH(sizeof(struct nlmsgerr)); + return netlink_message->nlmsg_len; + }, + [preferred_ip, subnet, egress_interface_index](const void* buf, + size_t len) { + auto* netlink_message = reinterpret_cast<const struct nlmsghdr*>(buf); + auto* rtm = + reinterpret_cast<const struct rtmsg*>(NLMSG_DATA(netlink_message)); + EXPECT_EQ(AF_INET6, rtm->rtm_family); + EXPECT_EQ(48, rtm->rtm_dst_len); + EXPECT_EQ(0, rtm->rtm_src_len); + EXPECT_EQ(RT_TABLE_MAIN, rtm->rtm_table); + EXPECT_EQ(RTPROT_UNSPEC, rtm->rtm_protocol); + EXPECT_EQ(RT_SCOPE_LINK, rtm->rtm_scope); + EXPECT_EQ(RTN_UNICAST, rtm->rtm_type); + + const struct rtattr* rta; + int payload_length = RTM_PAYLOAD(netlink_message); + int num_rta = 0; + for (rta = RTM_RTA(rtm); RTA_OK(rta, payload_length); + rta = RTA_NEXT(rta, payload_length)) { + switch (rta->rta_type) { + case RTA_PREFSRC: { + const auto* raw_address = + reinterpret_cast<const char*>(RTA_DATA(rta)); + ASSERT_EQ(sizeof(struct in6_addr), RTA_PAYLOAD(rta)); + QuicIpAddress address; + address.FromPackedString(raw_address, RTA_PAYLOAD(rta)); + EXPECT_EQ(preferred_ip, address); + break; + } + case RTA_OIF: { + ASSERT_EQ(sizeof(int), RTA_PAYLOAD(rta)); + const auto* interface_index = + reinterpret_cast<const int*>(RTA_DATA(rta)); + EXPECT_EQ(egress_interface_index, *interface_index); + break; + } + case RTA_DST: { + const auto* raw_address = + reinterpret_cast<const char*>(RTA_DATA(rta)); + ASSERT_EQ(sizeof(struct in6_addr), RTA_PAYLOAD(rta)); + QuicIpAddress address; + address.FromPackedString(raw_address, RTA_PAYLOAD(rta)); + EXPECT_EQ(subnet.ToString(), + IpRange(address, rtm->rtm_dst_len).ToString()); + break; + } + case RTA_TABLE: { + ASSERT_EQ(*reinterpret_cast<uint32_t*>(RTA_DATA(rta)), + QboneConstants::kQboneRouteTableId); + break; + } + default: + EXPECT_TRUE(false) << "Seeing rtattr that should not be sent"; + } + ++num_rta; + } + EXPECT_EQ(4, num_rta); + }); + EXPECT_TRUE(netlink->ChangeRoute( + Netlink::Verb::kRemove, QboneConstants::kQboneRouteTableId, subnet, + RT_SCOPE_LINK, preferred_ip, egress_interface_index)); +} + +TEST_F(NetlinkTest, ChangeRouteReplace) { + auto netlink = QuicMakeUnique<Netlink>(&mock_kernel_); + + QuicIpAddress preferred_ip; + preferred_ip.FromString("ff80:dead:beef::1"); + IpRange subnet; + subnet.FromString("ff80:dead:beef::/48"); + int egress_interface_index = 7; + ExpectNetlinkPacket( + RTM_NEWROUTE, NLM_F_ACK | NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE, + [](void* buf, size_t len, int seq) { + struct nlmsghdr* netlink_message = + CreateNetlinkMessage(buf, nullptr, NLMSG_ERROR, seq); + auto* err = + reinterpret_cast<struct nlmsgerr*>(NLMSG_DATA(netlink_message)); + // Ack the request + err->error = 0; + netlink_message->nlmsg_len = NLMSG_LENGTH(sizeof(struct nlmsgerr)); + return netlink_message->nlmsg_len; + }, + [preferred_ip, subnet, egress_interface_index](const void* buf, + size_t len) { + auto* netlink_message = reinterpret_cast<const struct nlmsghdr*>(buf); + auto* rtm = + reinterpret_cast<const struct rtmsg*>(NLMSG_DATA(netlink_message)); + EXPECT_EQ(AF_INET6, rtm->rtm_family); + EXPECT_EQ(48, rtm->rtm_dst_len); + EXPECT_EQ(0, rtm->rtm_src_len); + EXPECT_EQ(RT_TABLE_MAIN, rtm->rtm_table); + EXPECT_EQ(RTPROT_STATIC, rtm->rtm_protocol); + EXPECT_EQ(RT_SCOPE_LINK, rtm->rtm_scope); + EXPECT_EQ(RTN_UNICAST, rtm->rtm_type); + + const struct rtattr* rta; + int payload_length = RTM_PAYLOAD(netlink_message); + int num_rta = 0; + for (rta = RTM_RTA(rtm); RTA_OK(rta, payload_length); + rta = RTA_NEXT(rta, payload_length)) { + switch (rta->rta_type) { + case RTA_PREFSRC: { + const auto* raw_address = + reinterpret_cast<const char*>(RTA_DATA(rta)); + ASSERT_EQ(sizeof(struct in6_addr), RTA_PAYLOAD(rta)); + QuicIpAddress address; + address.FromPackedString(raw_address, RTA_PAYLOAD(rta)); + EXPECT_EQ(preferred_ip, address); + break; + } + case RTA_OIF: { + ASSERT_EQ(sizeof(int), RTA_PAYLOAD(rta)); + const auto* interface_index = + reinterpret_cast<const int*>(RTA_DATA(rta)); + EXPECT_EQ(egress_interface_index, *interface_index); + break; + } + case RTA_DST: { + const auto* raw_address = + reinterpret_cast<const char*>(RTA_DATA(rta)); + ASSERT_EQ(sizeof(struct in6_addr), RTA_PAYLOAD(rta)); + QuicIpAddress address; + address.FromPackedString(raw_address, RTA_PAYLOAD(rta)); + EXPECT_EQ(subnet.ToString(), + IpRange(address, rtm->rtm_dst_len).ToString()); + break; + } + case RTA_TABLE: { + ASSERT_EQ(*reinterpret_cast<uint32_t*>(RTA_DATA(rta)), + QboneConstants::kQboneRouteTableId); + break; + } + default: + EXPECT_TRUE(false) << "Seeing rtattr that should not be sent"; + } + ++num_rta; + } + EXPECT_EQ(4, num_rta); + }); + EXPECT_TRUE(netlink->ChangeRoute( + Netlink::Verb::kReplace, QboneConstants::kQboneRouteTableId, subnet, + RT_SCOPE_LINK, preferred_ip, egress_interface_index)); +} + +} // namespace +} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/qbone/platform/rtnetlink_message.cc b/chromium/net/third_party/quiche/src/quic/qbone/platform/rtnetlink_message.cc new file mode 100644 index 00000000000..f35628a052e --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/platform/rtnetlink_message.cc @@ -0,0 +1,177 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/third_party/quiche/src/quic/qbone/platform/rtnetlink_message.h" + +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" + +namespace quic { + +RtnetlinkMessage::RtnetlinkMessage(uint16_t type, + uint16_t flags, + uint32_t seq, + uint32_t pid, + const void* payload_header, + size_t payload_header_length) { + auto* buf = new uint8_t[NLMSG_SPACE(payload_header_length)]; + memset(buf, 0, NLMSG_SPACE(payload_header_length)); + + auto* message_header = reinterpret_cast<struct nlmsghdr*>(buf); + message_header->nlmsg_len = NLMSG_LENGTH(payload_header_length); + message_header->nlmsg_type = type; + message_header->nlmsg_flags = flags; + message_header->nlmsg_seq = seq; + message_header->nlmsg_pid = pid; + + if (payload_header != nullptr) { + memcpy(NLMSG_DATA(message_header), payload_header, payload_header_length); + } + message_.push_back({buf, NLMSG_SPACE(payload_header_length)}); +} + +RtnetlinkMessage::~RtnetlinkMessage() { + for (const auto& iov : message_) { + delete[] reinterpret_cast<uint8_t*>(iov.iov_base); + } +} + +void RtnetlinkMessage::AppendAttribute(uint16_t type, + const void* data, + uint16_t data_length) { + auto* buf = new uint8_t[RTA_SPACE(data_length)]; + memset(buf, 0, RTA_SPACE(data_length)); + + auto* rta = reinterpret_cast<struct rtattr*>(buf); + static_assert(sizeof(uint16_t) == sizeof(rta->rta_len), + "struct rtattr uses unsigned short, it's no longer 16bits"); + static_assert(sizeof(uint16_t) == sizeof(rta->rta_type), + "struct rtattr uses unsigned short, it's no longer 16bits"); + + rta->rta_len = RTA_LENGTH(data_length); + rta->rta_type = type; + memcpy(RTA_DATA(rta), data, data_length); + + message_.push_back({buf, RTA_SPACE(data_length)}); + AdjustMessageLength(rta->rta_len); +} + +std::unique_ptr<struct iovec[]> RtnetlinkMessage::BuildIoVec() const { + auto message = QuicMakeUnique<struct iovec[]>(message_.size()); + int idx = 0; + for (const auto& vec : message_) { + message[idx++] = vec; + } + return message; +} + +size_t RtnetlinkMessage::IoVecSize() const { + return message_.size(); +} + +void RtnetlinkMessage::AdjustMessageLength(size_t additional_data_length) { + MessageHeader()->nlmsg_len = + NLMSG_ALIGN(MessageHeader()->nlmsg_len) + additional_data_length; +} + +struct nlmsghdr* RtnetlinkMessage::MessageHeader() { + return reinterpret_cast<struct nlmsghdr*>(message_[0].iov_base); +} + +LinkMessage LinkMessage::New(RtnetlinkMessage::Operation request_operation, + uint16_t flags, + uint32_t seq, + uint32_t pid, + const struct ifinfomsg* interface_info_header) { + uint16_t request_type; + switch (request_operation) { + case RtnetlinkMessage::Operation::NEW: + request_type = RTM_NEWLINK; + break; + case RtnetlinkMessage::Operation::DEL: + request_type = RTM_DELLINK; + break; + case RtnetlinkMessage::Operation::GET: + request_type = RTM_GETLINK; + break; + } + bool is_get = request_type == RTM_GETLINK; + + if (is_get) { + struct rtgenmsg g = {AF_UNSPEC}; + return LinkMessage(request_type, flags, seq, pid, &g, sizeof(g)); + } + return LinkMessage(request_type, flags, seq, pid, interface_info_header, + sizeof(struct ifinfomsg)); +} + +AddressMessage AddressMessage::New( + RtnetlinkMessage::Operation request_operation, + uint16_t flags, + uint32_t seq, + uint32_t pid, + const struct ifaddrmsg* interface_address_header) { + uint16_t request_type; + switch (request_operation) { + case RtnetlinkMessage::Operation::NEW: + request_type = RTM_NEWADDR; + break; + case RtnetlinkMessage::Operation::DEL: + request_type = RTM_DELADDR; + break; + case RtnetlinkMessage::Operation::GET: + request_type = RTM_GETADDR; + break; + } + bool is_get = request_type == RTM_GETADDR; + + if (is_get) { + struct rtgenmsg g = {AF_UNSPEC}; + return AddressMessage(request_type, flags, seq, pid, &g, sizeof(g)); + } + return AddressMessage(request_type, flags, seq, pid, interface_address_header, + sizeof(struct ifaddrmsg)); +} + +RouteMessage RouteMessage::New(RtnetlinkMessage::Operation request_operation, + uint16_t flags, + uint32_t seq, + uint32_t pid, + const struct rtmsg* route_message_header) { + uint16_t request_type; + switch (request_operation) { + case RtnetlinkMessage::Operation::NEW: + request_type = RTM_NEWROUTE; + break; + case RtnetlinkMessage::Operation::DEL: + request_type = RTM_DELROUTE; + break; + case RtnetlinkMessage::Operation::GET: + request_type = RTM_GETROUTE; + break; + } + return RouteMessage(request_type, flags, seq, pid, route_message_header, + sizeof(struct rtmsg)); +} + +RuleMessage RuleMessage::New(RtnetlinkMessage::Operation request_operation, + uint16_t flags, + uint32_t seq, + uint32_t pid, + const struct rtmsg* rule_message_header) { + uint16_t request_type; + switch (request_operation) { + case RtnetlinkMessage::Operation::NEW: + request_type = RTM_NEWRULE; + break; + case RtnetlinkMessage::Operation::DEL: + request_type = RTM_DELRULE; + break; + case RtnetlinkMessage::Operation::GET: + request_type = RTM_GETRULE; + break; + } + return RuleMessage(request_type, flags, seq, pid, rule_message_header, + sizeof(rtmsg)); +} +} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/qbone/platform/rtnetlink_message.h b/chromium/net/third_party/quiche/src/quic/qbone/platform/rtnetlink_message.h new file mode 100644 index 00000000000..0412d54e3f4 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/platform/rtnetlink_message.h @@ -0,0 +1,126 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef QUICHE_QUIC_QBONE_PLATFORM_RTNETLINK_MESSAGE_H_ +#define QUICHE_QUIC_QBONE_PLATFORM_RTNETLINK_MESSAGE_H_ + +#include <linux/netlink.h> +#include <linux/rtnetlink.h> +#include <stdint.h> +#include <sys/socket.h> +#include <sys/uio.h> + +#include <memory> +#include <vector> + +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h" + +namespace quic { + +// This base class is used to construct an array struct iovec that represents a +// rtnetlink message as defined in man 7 rtnet. Padding for message header +// alignment to conform NLMSG_* and RTA_* macros is added at the end of each +// iovec::iov_base. +class RtnetlinkMessage { + public: + virtual ~RtnetlinkMessage(); + + enum class Operation { + NEW, + DEL, + GET, + }; + + // Appends a struct rtattr to the message. nlmsg_len and rta_len is handled + // properly. + // Override this to perform check on type. + virtual void AppendAttribute(uint16_t type, + const void* data, + uint16_t data_length); + + // Builds the array of iovec that can be fed into sendmsg directly. + std::unique_ptr<struct iovec[]> BuildIoVec() const; + + // The size of the array of iovec if BuildIovec is called. + size_t IoVecSize() const; + + protected: + // Subclass should add their own message header immediately after the + // nlmsghdr. Make this private to force the creation of such header. + RtnetlinkMessage(uint16_t type, + uint16_t flags, + uint32_t seq, + uint32_t pid, + const void* payload_header, + size_t payload_header_length); + + // Adjusts nlmsg_len in the header assuming additional_data_length is appended + // at the end. + void AdjustMessageLength(size_t additional_data_length); + + private: + // Convenient function for accessing the nlmsghdr. + struct nlmsghdr* MessageHeader(); + + std::vector<struct iovec> message_; +}; + +// Message for manipulating link level configuration as defined in man 7 +// rtnetlink. RTM_NEWLINK, RTM_DELLINK and RTM_GETLINK are supported. +class LinkMessage : public RtnetlinkMessage { + public: + static LinkMessage New(RtnetlinkMessage::Operation request_operation, + uint16_t flags, + uint32_t seq, + uint32_t pid, + const struct ifinfomsg* interface_info_header); + + private: + using RtnetlinkMessage::RtnetlinkMessage; +}; + +// Message for manipulating address level configuration as defined in man 7 +// rtnetlink. RTM_NEWADDR, RTM_NEWADDR and RTM_GETADDR are supported. +class AddressMessage : public RtnetlinkMessage { + public: + static AddressMessage New(RtnetlinkMessage::Operation request_operation, + uint16_t flags, + uint32_t seq, + uint32_t pid, + const struct ifaddrmsg* interface_address_header); + + private: + using RtnetlinkMessage::RtnetlinkMessage; +}; + +// Message for manipulating routing table as defined in man 7 rtnetlink. +// RTM_NEWROUTE, RTM_DELROUTE and RTM_GETROUTE are supported. +class RouteMessage : public RtnetlinkMessage { + public: + static RouteMessage New(RtnetlinkMessage::Operation request_operation, + uint16_t flags, + uint32_t seq, + uint32_t pid, + const struct rtmsg* route_message_header); + + private: + using RtnetlinkMessage::RtnetlinkMessage; +}; + +class RuleMessage : public RtnetlinkMessage { + public: + static RuleMessage New(RtnetlinkMessage::Operation request_operation, + uint16_t flags, + uint32_t seq, + uint32_t pid, + const struct rtmsg* rule_message_header); + + private: + using RtnetlinkMessage::RtnetlinkMessage; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_QBONE_PLATFORM_RTNETLINK_MESSAGE_H_ diff --git a/chromium/net/third_party/quiche/src/quic/qbone/platform/rtnetlink_message_test.cc b/chromium/net/third_party/quiche/src/quic/qbone/platform/rtnetlink_message_test.cc new file mode 100644 index 00000000000..b1292373358 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/platform/rtnetlink_message_test.cc @@ -0,0 +1,228 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/third_party/quiche/src/quic/qbone/platform/rtnetlink_message.h" + +#include <net/if_arp.h> + +#include "net/third_party/quiche/src/quic/platform/api/quic_ip_address.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" + +namespace quic { +namespace { + +using ::testing::StrEq; + +TEST(RtnetlinkMessageTest, LinkMessageCanBeCreatedForGetOperation) { + uint16_t flags = NLM_F_REQUEST | NLM_F_ROOT | NLM_F_MATCH; + uint32_t seq = 42; + uint32_t pid = 7; + auto message = LinkMessage::New(RtnetlinkMessage::Operation::GET, flags, seq, + pid, nullptr); + + // No rtattr appended. + EXPECT_EQ(1, message.IoVecSize()); + + // nlmsghdr is built properly. + auto iov = message.BuildIoVec(); + EXPECT_EQ(NLMSG_SPACE(sizeof(struct rtgenmsg)), iov[0].iov_len); + auto* netlink_message = reinterpret_cast<struct nlmsghdr*>(iov[0].iov_base); + EXPECT_EQ(NLMSG_LENGTH(sizeof(struct rtgenmsg)), netlink_message->nlmsg_len); + EXPECT_EQ(RTM_GETLINK, netlink_message->nlmsg_type); + EXPECT_EQ(flags, netlink_message->nlmsg_flags); + EXPECT_EQ(seq, netlink_message->nlmsg_seq); + EXPECT_EQ(pid, netlink_message->nlmsg_pid); + + // We actually included rtgenmsg instead of the passed in ifinfomsg since this + // is a GET operation. + EXPECT_EQ(NLMSG_LENGTH(sizeof(struct rtgenmsg)), netlink_message->nlmsg_len); +} + +TEST(RtnetlinkMessageTest, LinkMessageCanBeCreatedForNewOperation) { + struct ifinfomsg interface_info_header = {AF_INET, /* pad */ 0, ARPHRD_TUNNEL, + 3, 0, 0xffffffff}; + uint16_t flags = NLM_F_REQUEST | NLM_F_ROOT | NLM_F_MATCH; + uint32_t seq = 42; + uint32_t pid = 7; + auto message = LinkMessage::New(RtnetlinkMessage::Operation::NEW, flags, seq, + pid, &interface_info_header); + + string device_name = "device0"; + message.AppendAttribute(IFLA_IFNAME, device_name.c_str(), device_name.size()); + + // One rtattr appended. + EXPECT_EQ(2, message.IoVecSize()); + + // nlmsghdr is built properly. + auto iov = message.BuildIoVec(); + EXPECT_EQ(NLMSG_ALIGN(NLMSG_LENGTH(sizeof(struct ifinfomsg))), + iov[0].iov_len); + auto* netlink_message = reinterpret_cast<struct nlmsghdr*>(iov[0].iov_base); + EXPECT_EQ(NLMSG_ALIGN(NLMSG_LENGTH(sizeof(struct ifinfomsg))) + + RTA_LENGTH(device_name.size()), + netlink_message->nlmsg_len); + EXPECT_EQ(RTM_NEWLINK, netlink_message->nlmsg_type); + EXPECT_EQ(flags, netlink_message->nlmsg_flags); + EXPECT_EQ(seq, netlink_message->nlmsg_seq); + EXPECT_EQ(pid, netlink_message->nlmsg_pid); + + // ifinfomsg is included properly. + auto* parsed_header = + reinterpret_cast<struct ifinfomsg*>(NLMSG_DATA(netlink_message)); + EXPECT_EQ(interface_info_header.ifi_family, parsed_header->ifi_family); + EXPECT_EQ(interface_info_header.ifi_type, parsed_header->ifi_type); + EXPECT_EQ(interface_info_header.ifi_index, parsed_header->ifi_index); + EXPECT_EQ(interface_info_header.ifi_flags, parsed_header->ifi_flags); + EXPECT_EQ(interface_info_header.ifi_change, parsed_header->ifi_change); + + // rtattr is handled properly. + EXPECT_EQ(RTA_SPACE(device_name.size()), iov[1].iov_len); + auto* rta = reinterpret_cast<struct rtattr*>(iov[1].iov_base); + EXPECT_EQ(IFLA_IFNAME, rta->rta_type); + EXPECT_EQ(RTA_LENGTH(device_name.size()), rta->rta_len); + EXPECT_THAT(device_name, StrEq(string(reinterpret_cast<char*>(RTA_DATA(rta)), + RTA_PAYLOAD(rta)))); +} + +TEST(RtnetlinkMessageTest, AddressMessageCanBeCreatedForGetOperation) { + uint16_t flags = NLM_F_REQUEST | NLM_F_ROOT | NLM_F_MATCH; + uint32_t seq = 42; + uint32_t pid = 7; + auto message = AddressMessage::New(RtnetlinkMessage::Operation::GET, flags, + seq, pid, nullptr); + + // No rtattr appended. + EXPECT_EQ(1, message.IoVecSize()); + + // nlmsghdr is built properly. + auto iov = message.BuildIoVec(); + EXPECT_EQ(NLMSG_SPACE(sizeof(struct rtgenmsg)), iov[0].iov_len); + auto* netlink_message = reinterpret_cast<struct nlmsghdr*>(iov[0].iov_base); + EXPECT_EQ(NLMSG_LENGTH(sizeof(struct rtgenmsg)), netlink_message->nlmsg_len); + EXPECT_EQ(RTM_GETADDR, netlink_message->nlmsg_type); + EXPECT_EQ(flags, netlink_message->nlmsg_flags); + EXPECT_EQ(seq, netlink_message->nlmsg_seq); + EXPECT_EQ(pid, netlink_message->nlmsg_pid); + + // We actually included rtgenmsg instead of the passed in ifinfomsg since this + // is a GET operation. + EXPECT_EQ(NLMSG_LENGTH(sizeof(struct rtgenmsg)), netlink_message->nlmsg_len); +} + +TEST(RtnetlinkMessageTest, AddressMessageCanBeCreatedForNewOperation) { + struct ifaddrmsg interface_address_header = {AF_INET, + /* prefixlen */ 24, + /* flags */ 0, + /* scope */ RT_SCOPE_LINK, + /* index */ 4}; + uint16_t flags = NLM_F_REQUEST | NLM_F_ROOT | NLM_F_MATCH; + uint32_t seq = 42; + uint32_t pid = 7; + auto message = AddressMessage::New(RtnetlinkMessage::Operation::NEW, flags, + seq, pid, &interface_address_header); + + QuicIpAddress ip; + CHECK(ip.FromString("10.0.100.3")); + message.AppendAttribute(IFA_ADDRESS, ip.ToPackedString().c_str(), + ip.ToPackedString().size()); + + // One rtattr is appended. + EXPECT_EQ(2, message.IoVecSize()); + + // nlmsghdr is built properly. + auto iov = message.BuildIoVec(); + EXPECT_EQ(NLMSG_ALIGN(NLMSG_LENGTH(sizeof(struct ifaddrmsg))), + iov[0].iov_len); + auto* netlink_message = reinterpret_cast<struct nlmsghdr*>(iov[0].iov_base); + EXPECT_EQ(NLMSG_ALIGN(NLMSG_LENGTH(sizeof(struct ifaddrmsg))) + + RTA_LENGTH(ip.ToPackedString().size()), + netlink_message->nlmsg_len); + EXPECT_EQ(RTM_NEWADDR, netlink_message->nlmsg_type); + EXPECT_EQ(flags, netlink_message->nlmsg_flags); + EXPECT_EQ(seq, netlink_message->nlmsg_seq); + EXPECT_EQ(pid, netlink_message->nlmsg_pid); + + // ifaddrmsg is included properly. + auto* parsed_header = + reinterpret_cast<struct ifaddrmsg*>(NLMSG_DATA(netlink_message)); + EXPECT_EQ(interface_address_header.ifa_family, parsed_header->ifa_family); + EXPECT_EQ(interface_address_header.ifa_prefixlen, + parsed_header->ifa_prefixlen); + EXPECT_EQ(interface_address_header.ifa_flags, parsed_header->ifa_flags); + EXPECT_EQ(interface_address_header.ifa_scope, parsed_header->ifa_scope); + EXPECT_EQ(interface_address_header.ifa_index, parsed_header->ifa_index); + + // rtattr is handled properly. + EXPECT_EQ(RTA_SPACE(ip.ToPackedString().size()), iov[1].iov_len); + auto* rta = reinterpret_cast<struct rtattr*>(iov[1].iov_base); + EXPECT_EQ(IFA_ADDRESS, rta->rta_type); + EXPECT_EQ(RTA_LENGTH(ip.ToPackedString().size()), rta->rta_len); + EXPECT_THAT( + ip.ToPackedString(), + StrEq(string(reinterpret_cast<char*>(RTA_DATA(rta)), RTA_PAYLOAD(rta)))); +} + +TEST(RtnetlinkMessageTest, RouteMessageCanBeCreatedFromNewOperation) { + struct rtmsg route_message_header = {AF_INET6, + /* rtm_dst_len */ 48, + /* rtm_src_len */ 0, + /* rtm_tos */ 0, + /* rtm_table */ RT_TABLE_MAIN, + /* rtm_protocol */ RTPROT_STATIC, + /* rtm_scope */ RT_SCOPE_LINK, + /* rtm_type */ RTN_LOCAL, + /* rtm_flags */ 0}; + uint16_t flags = NLM_F_REQUEST | NLM_F_ROOT | NLM_F_MATCH; + uint32_t seq = 42; + uint32_t pid = 7; + auto message = RouteMessage::New(RtnetlinkMessage::Operation::NEW, flags, seq, + pid, &route_message_header); + + QuicIpAddress preferred_source; + CHECK(preferred_source.FromString("ff80::1")); + message.AppendAttribute(RTA_PREFSRC, + preferred_source.ToPackedString().c_str(), + preferred_source.ToPackedString().size()); + + // One rtattr is appended. + EXPECT_EQ(2, message.IoVecSize()); + + // nlmsghdr is built properly + auto iov = message.BuildIoVec(); + EXPECT_EQ(NLMSG_ALIGN(NLMSG_LENGTH(sizeof(struct rtmsg))), iov[0].iov_len); + auto* netlink_message = reinterpret_cast<struct nlmsghdr*>(iov[0].iov_base); + EXPECT_EQ(NLMSG_ALIGN(NLMSG_LENGTH(sizeof(struct rtmsg))) + + RTA_LENGTH(preferred_source.ToPackedString().size()), + netlink_message->nlmsg_len); + EXPECT_EQ(RTM_NEWROUTE, netlink_message->nlmsg_type); + EXPECT_EQ(flags, netlink_message->nlmsg_flags); + EXPECT_EQ(seq, netlink_message->nlmsg_seq); + EXPECT_EQ(pid, netlink_message->nlmsg_pid); + + // rtmsg is included properly. + auto* parsed_header = + reinterpret_cast<struct rtmsg*>(NLMSG_DATA(netlink_message)); + EXPECT_EQ(route_message_header.rtm_family, parsed_header->rtm_family); + EXPECT_EQ(route_message_header.rtm_dst_len, parsed_header->rtm_dst_len); + EXPECT_EQ(route_message_header.rtm_src_len, parsed_header->rtm_src_len); + EXPECT_EQ(route_message_header.rtm_tos, parsed_header->rtm_tos); + EXPECT_EQ(route_message_header.rtm_table, parsed_header->rtm_table); + EXPECT_EQ(route_message_header.rtm_protocol, parsed_header->rtm_protocol); + EXPECT_EQ(route_message_header.rtm_scope, parsed_header->rtm_scope); + EXPECT_EQ(route_message_header.rtm_type, parsed_header->rtm_type); + EXPECT_EQ(route_message_header.rtm_flags, parsed_header->rtm_flags); + + // rtattr is handled properly. + EXPECT_EQ(RTA_SPACE(preferred_source.ToPackedString().size()), + iov[1].iov_len); + auto* rta = reinterpret_cast<struct rtattr*>(iov[1].iov_base); + EXPECT_EQ(RTA_PREFSRC, rta->rta_type); + EXPECT_EQ(RTA_LENGTH(preferred_source.ToPackedString().size()), rta->rta_len); + EXPECT_THAT( + preferred_source.ToPackedString(), + StrEq(string(reinterpret_cast<char*>(RTA_DATA(rta)), RTA_PAYLOAD(rta)))); +} + +} // namespace +} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/qbone/platform/tcp_packet.cc b/chromium/net/third_party/quiche/src/quic/qbone/platform/tcp_packet.cc new file mode 100644 index 00000000000..56fa88ab2d3 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/platform/tcp_packet.cc @@ -0,0 +1,125 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/third_party/quiche/src/quic/qbone/platform/tcp_packet.h" + +#include <netinet/ip6.h> + +#include "net/third_party/quiche/src/quic/platform/api/quic_endian.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/qbone/platform/internet_checksum.h" + +namespace quic { +namespace { + +constexpr size_t kIPv6AddressSize = sizeof(in6_addr); +constexpr size_t kTcpTtl = 64; + +struct TCPv6Packet { + ip6_hdr ip_header; + tcphdr tcp_header; +}; + +struct TCPv6PseudoHeader { + uint32_t payload_size{}; + uint8_t zeros[3] = {0, 0, 0}; + uint8_t next_header = IPPROTO_TCP; +}; + +} // namespace + +void CreateTcpResetPacket( + quic::QuicStringPiece original_packet, + const std::function<void(quic::QuicStringPiece)>& cb) { + // By the time this method is called, original_packet should be fairly + // strongly validated. However, it's better to be more paranoid than not, so + // here are a bunch of very obvious checks. + if (QUIC_PREDICT_FALSE(original_packet.size() < sizeof(ip6_hdr))) { + return; + } + auto* ip6_header = reinterpret_cast<const ip6_hdr*>(original_packet.data()); + if (QUIC_PREDICT_FALSE(ip6_header->ip6_vfc >> 4 != 6)) { + return; + } + if (QUIC_PREDICT_FALSE(ip6_header->ip6_nxt != IPPROTO_TCP)) { + return; + } + if (QUIC_PREDICT_FALSE(QuicEndian::NetToHost16(ip6_header->ip6_plen) < + sizeof(tcphdr))) { + return; + } + auto* tcp_header = reinterpret_cast<const tcphdr*>(ip6_header + 1); + + // Now that the original packet has been confirmed to be well-formed, it's + // time to make the TCP RST packet. + TCPv6Packet tcp_packet{}; + + const size_t payload_size = sizeof(tcphdr); + + // Set version to 6. + tcp_packet.ip_header.ip6_vfc = 0x6 << 4; + // Set the payload size, protocol and TTL. + tcp_packet.ip_header.ip6_plen = QuicEndian::HostToNet16(payload_size); + tcp_packet.ip_header.ip6_nxt = IPPROTO_TCP; + tcp_packet.ip_header.ip6_hops = kTcpTtl; + // Since the TCP RST is impersonating the endpoint, flip the source and + // destination addresses from the original packet. + tcp_packet.ip_header.ip6_src = ip6_header->ip6_dst; + tcp_packet.ip_header.ip6_dst = ip6_header->ip6_src; + + // The same is true about the TCP ports + tcp_packet.tcp_header.dest = tcp_header->source; + tcp_packet.tcp_header.source = tcp_header->dest; + + // There are no extensions in this header, so size is trivial + tcp_packet.tcp_header.doff = sizeof(tcphdr) >> 2; + // Checksum is 0 before it is computed + tcp_packet.tcp_header.check = 0; + + // Per RFC 793, TCP RST comes in one of 3 flavors: + // + // * connection CLOSED + // * connection in non-synchronized state (LISTEN, SYN-SENT, SYN-RECEIVED) + // * connection in synchronized state (ESTABLISHED, FIN-WAIT-1, etc.) + // + // QBONE is acting like a firewall, so the RFC text of interest is the CLOSED + // state. Note, however, that it is possible for a connection to actually be + // in the FIN-WAIT-1 state on the remote end, but the processing logic does + // not change. + tcp_packet.tcp_header.rst = 1; + + // If the incoming segment has an ACK field, the reset takes its sequence + // number from the ACK field of the segment, + if (tcp_header->ack) { + tcp_packet.tcp_header.seq = tcp_header->ack_seq; + } else { + // Otherwise the reset has sequence number zero and the ACK field is set to + // the sum of the sequence number and segment length of the incoming segment + tcp_packet.tcp_header.ack = 1; + tcp_packet.tcp_header.seq = 0; + tcp_packet.tcp_header.ack_seq = + QuicEndian::HostToNet32(QuicEndian::NetToHost32(tcp_header->seq) + 1); + } + + TCPv6PseudoHeader pseudo_header{}; + pseudo_header.payload_size = QuicEndian::HostToNet32(payload_size); + + InternetChecksum checksum; + // Pseudoheader. + checksum.Update(tcp_packet.ip_header.ip6_src.s6_addr, kIPv6AddressSize); + checksum.Update(tcp_packet.ip_header.ip6_dst.s6_addr, kIPv6AddressSize); + checksum.Update(reinterpret_cast<char*>(&pseudo_header), + sizeof(pseudo_header)); + // TCP header. + checksum.Update(reinterpret_cast<const char*>(&tcp_packet.tcp_header), + sizeof(tcp_packet.tcp_header)); + // There is no body. + tcp_packet.tcp_header.check = checksum.Value(); + + const char* packet = reinterpret_cast<char*>(&tcp_packet); + + cb(QuicStringPiece(packet, sizeof(tcp_packet))); +} + +} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/qbone/platform/tcp_packet.h b/chromium/net/third_party/quiche/src/quic/qbone/platform/tcp_packet.h new file mode 100644 index 00000000000..cf33f03709d --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/platform/tcp_packet.h @@ -0,0 +1,25 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef QUICHE_QUIC_QBONE_PLATFORM_TCP_PACKET_H_ +#define QUICHE_QUIC_QBONE_PLATFORM_TCP_PACKET_H_ + +#include <netinet/in.h> +#include <netinet/tcp.h> + +#include <functional> + +#include "net/third_party/quiche/src/quic/platform/api/quic_ip_address.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" + +namespace quic { + +// Creates an TCPv6 RST packet, returning a packed string representation of the +// packet to |cb|. +void CreateTcpResetPacket(quic::QuicStringPiece original_packet, + const std::function<void(quic::QuicStringPiece)>& cb); + +} // namespace quic + +#endif // QUICHE_QUIC_QBONE_PLATFORM_TCP_PACKET_H_ diff --git a/chromium/net/third_party/quiche/src/quic/qbone/platform/tcp_packet_test.cc b/chromium/net/third_party/quiche/src/quic/qbone/platform/tcp_packet_test.cc new file mode 100644 index 00000000000..53a2c3f9c01 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/platform/tcp_packet_test.cc @@ -0,0 +1,116 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/third_party/quiche/src/quic/qbone/platform/tcp_packet.h" + +#include <netinet/ip6.h> + +#include <cstdint> + +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h" + +namespace quic { +namespace { + +// clang-format off +constexpr uint8_t kReferenceTCPSYNPacket[] = { + // START IPv6 Header + // IPv6 with zero ToS and flow label + 0x60, 0x00, 0x00, 0x00, + // Payload is 40 bytes + 0x00, 0x28, + // Next header is TCP (6) + 0x06, + // Hop limit is 64 + 0x40, + // Source address of ::1 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + // Destination address of ::1 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + // END IPv6 Header + // START TCPv6 Header + // Source port + 0xac, 0x1e, + // Destination port + 0x27, 0x0f, + // Sequence number + 0x4b, 0x01, 0xe8, 0x99, + // Acknowledgement Sequence number, + 0x00, 0x00, 0x00, 0x00, + // Offset + 0xa0, + // Flags + 0x02, + // Window + 0xaa, 0xaa, + // Checksum + 0x2e, 0x21, + // Urgent + 0x00, 0x00, + // END TCPv6 Header + // Options + 0x02, 0x04, 0xff, 0xc4, 0x04, 0x02, 0x08, 0x0a, + 0x1b, 0xb8, 0x52, 0xa1, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x03, 0x03, 0x07, +}; + +constexpr uint8_t kReferenceTCPRSTPacket[] = { + // START IPv6 Header + // IPv6 with zero ToS and flow label + 0x60, 0x00, 0x00, 0x00, + // Payload is 20 bytes + 0x00, 0x14, + // Next header is TCP (6) + 0x06, + // Hop limit is 64 + 0x40, + // Source address of ::1 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + // Destination address of ::1 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + // END IPv6 Header + // START TCPv6 Header + // Source port + 0x27, 0x0f, + // Destination port + 0xac, 0x1e, + // Sequence number + 0x00, 0x00, 0x00, 0x00, + // Acknowledgement Sequence number, + 0x4b, 0x01, 0xe8, 0x9a, + // Offset + 0x50, + // Flags + 0x14, + // Window + 0x00, 0x00, + // Checksum + 0xa9, 0x05, + // Urgent + 0x00, 0x00, + // END TCPv6 Header +}; +// clang-format on + +} // namespace + +TEST(TcpPacketTest, CreatedPacketMatchesReference) { + QuicStringPiece syn = + QuicStringPiece(reinterpret_cast<const char*>(kReferenceTCPSYNPacket), + sizeof(kReferenceTCPSYNPacket)); + QuicStringPiece expected_packet = + QuicStringPiece(reinterpret_cast<const char*>(kReferenceTCPRSTPacket), + sizeof(kReferenceTCPRSTPacket)); + CreateTcpResetPacket(syn, [&expected_packet](QuicStringPiece packet) { + QUIC_LOG(INFO) << QuicTextUtils::HexDump(packet); + ASSERT_EQ(packet, expected_packet); + }); +} + +} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/qbone/qbone_client.cc b/chromium/net/third_party/quiche/src/quic/qbone/qbone_client.cc new file mode 100644 index 00000000000..f062d3f92e5 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/qbone_client.cc @@ -0,0 +1,98 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/third_party/quiche/src/quic/qbone/qbone_client.h" + +#include "net/third_party/quiche/src/quic/core/quic_epoll_alarm_factory.h" +#include "net/third_party/quiche/src/quic/core/quic_epoll_connection_helper.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_epoll.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_exported_stats.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/qbone/qbone_stream.h" + +namespace quic { +namespace { +std::unique_ptr<QuicClientBase::NetworkHelper> CreateNetworkHelper( + QuicEpollServer* epoll_server, + QboneClient* client) { + std::unique_ptr<QuicClientBase::NetworkHelper> helper = + QuicMakeUnique<QuicClientEpollNetworkHelper>(epoll_server, client); + testing::testvalue::Adjust("QboneClient/network_helper", &helper); + return helper; +} +} // namespace + +QboneClient::QboneClient(QuicSocketAddress server_address, + const QuicServerId& server_id, + const ParsedQuicVersionVector& supported_versions, + QuicSession::Visitor* session_owner, + const QuicConfig& config, + QuicEpollServer* epoll_server, + std::unique_ptr<ProofVerifier> proof_verifier, + QbonePacketWriter* qbone_writer, + QboneClientControlStream::Handler* qbone_handler) + : QuicClientBase( + server_id, + supported_versions, + config, + new QuicEpollConnectionHelper(epoll_server, QuicAllocator::SIMPLE), + new QuicEpollAlarmFactory(epoll_server), + CreateNetworkHelper(epoll_server, this), + std::move(proof_verifier)), + qbone_writer_(qbone_writer), + qbone_handler_(qbone_handler), + session_owner_(session_owner) { + set_server_address(server_address); + crypto_config()->set_alpn("qbone"); +} + +QboneClient::~QboneClient() { + ResetSession(); +} + +QboneClientSession* QboneClient::qbone_session() { + return static_cast<QboneClientSession*>(QuicClientBase::session()); +} + +void QboneClient::ProcessPacketFromNetwork(QuicStringPiece packet) { + qbone_session()->ProcessPacketFromNetwork(packet); +} + +int QboneClient::GetNumSentClientHellosFromSession() { + return qbone_session()->GetNumSentClientHellos(); +} + +int QboneClient::GetNumReceivedServerConfigUpdatesFromSession() { + return qbone_session()->GetNumReceivedServerConfigUpdates(); +} + +void QboneClient::ResendSavedData() { + // no op. +} + +void QboneClient::ClearDataToResend() { + // no op. +} + +bool QboneClient::HasActiveRequests() { + return qbone_session()->HasActiveRequests(); +} + +class QboneClientSessionWithConnection : public QboneClientSession { + public: + using QboneClientSession::QboneClientSession; + + ~QboneClientSessionWithConnection() override { delete connection(); } +}; + +// Takes ownership of |connection|. +std::unique_ptr<QuicSession> QboneClient::CreateQuicClientSession( + const ParsedQuicVersionVector& supported_versions, + QuicConnection* connection) { + return QuicMakeUnique<QboneClientSessionWithConnection>( + connection, crypto_config(), session_owner(), *config(), + supported_versions, server_id(), qbone_writer_, qbone_handler_); +} + +} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/qbone/qbone_client.h b/chromium/net/third_party/quiche/src/quic/qbone/qbone_client.h new file mode 100644 index 00000000000..a0fe4fc70dd --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/qbone_client.h @@ -0,0 +1,74 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef QUICHE_QUIC_QBONE_QBONE_CLIENT_H_ +#define QUICHE_QUIC_QBONE_QBONE_CLIENT_H_ + +#include "net/third_party/quiche/src/quic/qbone/qbone_client_interface.h" +#include "net/third_party/quiche/src/quic/qbone/qbone_client_session.h" +#include "net/third_party/quiche/src/quic/qbone/qbone_packet_writer.h" +#include "net/third_party/quiche/src/quic/tools/quic_client_base.h" +#include "net/third_party/quiche/src/quic/tools/quic_client_epoll_network_helper.h" + +namespace quic { +// A QboneClient encapsulates connecting to a server via an epoll server +// and setting up a Qbone tunnel. See the QboneTestClient in qbone_client_test +// for usage. +class QboneClient : public QuicClientBase, public QboneClientInterface { + public: + // Note that the epoll server, qbone writer, and handler are owned + // by the caller. + QboneClient(QuicSocketAddress server_address, + const QuicServerId& server_id, + const ParsedQuicVersionVector& supported_versions, + QuicSession::Visitor* session_owner, + const QuicConfig& config, + QuicEpollServer* epoll_server, + std::unique_ptr<ProofVerifier> proof_verifier, + QbonePacketWriter* qbone_writer, + QboneClientControlStream::Handler* qbone_handler); + ~QboneClient() override; + QboneClientSession* qbone_session(); + + // From QboneClientInterface. Accepts a given packet from the network and + // sends the packet down to the QBONE connection. + void ProcessPacketFromNetwork(QuicStringPiece packet) override; + + protected: + int GetNumSentClientHellosFromSession() override; + int GetNumReceivedServerConfigUpdatesFromSession() override; + + // This client does not resend saved data. This will be a no-op. + void ResendSavedData() override; + + // This client does not resend saved data. This will be a no-op. + void ClearDataToResend() override; + + // Takes ownership of |connection|. + std::unique_ptr<QuicSession> CreateQuicClientSession( + const ParsedQuicVersionVector& supported_versions, + QuicConnection* connection) override; + + QbonePacketWriter* qbone_writer() { return qbone_writer_; } + + QboneClientControlStream::Handler* qbone_control_handler() { + return qbone_handler_; + } + + QuicSession::Visitor* session_owner() { + return session_owner_; + } + + bool HasActiveRequests() override; + + private: + QbonePacketWriter* qbone_writer_; + QboneClientControlStream::Handler* qbone_handler_; + + QuicSession::Visitor* session_owner_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_QBONE_QBONE_CLIENT_H_ diff --git a/chromium/net/third_party/quiche/src/quic/qbone/qbone_client_interface.h b/chromium/net/third_party/quiche/src/quic/qbone/qbone_client_interface.h new file mode 100644 index 00000000000..28d88ac24ad --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/qbone_client_interface.h @@ -0,0 +1,25 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef QUICHE_QUIC_QBONE_QBONE_CLIENT_INTERFACE_H_ +#define QUICHE_QUIC_QBONE_QBONE_CLIENT_INTERFACE_H_ + +#include <cstdint> + +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" + +namespace quic { + +// An interface that includes methods to interact with a QBONE client. +class QboneClientInterface { + public: + virtual ~QboneClientInterface() {} + // Accepts a given packet from the network and sends the packet down to the + // QBONE connection. + virtual void ProcessPacketFromNetwork(QuicStringPiece packet) = 0; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_QBONE_QBONE_CLIENT_INTERFACE_H_ diff --git a/chromium/net/third_party/quiche/src/quic/qbone/qbone_client_session.cc b/chromium/net/third_party/quiche/src/quic/qbone/qbone_client_session.cc new file mode 100644 index 00000000000..c2972ea5dae --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/qbone_client_session.cc @@ -0,0 +1,87 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/third_party/quiche/src/quic/qbone/qbone_client_session.h" + +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/qbone/qbone_constants.h" + +namespace quic { + +QboneClientSession::QboneClientSession( + QuicConnection* connection, + QuicCryptoClientConfig* quic_crypto_client_config, + QuicSession::Visitor* owner, + const QuicConfig& config, + const ParsedQuicVersionVector& supported_versions, + const QuicServerId& server_id, + QbonePacketWriter* writer, + QboneClientControlStream::Handler* handler) + : QboneSessionBase(connection, owner, config, supported_versions, writer), + server_id_(server_id), + quic_crypto_client_config_(quic_crypto_client_config), + handler_(handler) {} + +QboneClientSession::~QboneClientSession() {} + +std::unique_ptr<QuicCryptoStream> QboneClientSession::CreateCryptoStream() { + return QuicMakeUnique<QuicCryptoClientStream>( + server_id_, this, nullptr, quic_crypto_client_config_, this); +} + +void QboneClientSession::Initialize() { + // Initialize must be called first, as that's what generates the crypto + // stream. + QboneSessionBase::Initialize(); + static_cast<QuicCryptoClientStreamBase*>(GetMutableCryptoStream()) + ->CryptoConnect(); + // Register the reserved control stream. + QuicStreamId next_id = GetNextOutgoingBidirectionalStreamId(); + DCHECK_EQ(next_id, QboneConstants::GetControlStreamId( + connection()->transport_version())); + auto control_stream = + QuicMakeUnique<QboneClientControlStream>(this, handler_); + control_stream_ = control_stream.get(); + RegisterStaticStream(std::move(control_stream)); +} + +int QboneClientSession::GetNumSentClientHellos() const { + return static_cast<const QuicCryptoClientStreamBase*>(GetCryptoStream()) + ->num_sent_client_hellos(); +} + +int QboneClientSession::GetNumReceivedServerConfigUpdates() const { + return static_cast<const QuicCryptoClientStreamBase*>(GetCryptoStream()) + ->num_scup_messages_received(); +} + +bool QboneClientSession::SendServerRequest(const QboneServerRequest& request) { + if (!control_stream_) { + QUIC_BUG << "Cannot send server request before control stream is created."; + return false; + } + return control_stream_->SendRequest(request); +} + +void QboneClientSession::ProcessPacketFromNetwork(QuicStringPiece packet) { + SendPacketToPeer(packet); +} + +void QboneClientSession::ProcessPacketFromPeer(QuicStringPiece packet) { + writer_->WritePacketToNetwork(packet.data(), packet.size()); +} + +void QboneClientSession::OnProofValid( + const QuicCryptoClientConfig::CachedState& cached) {} + +void QboneClientSession::OnProofVerifyDetailsAvailable( + const ProofVerifyDetails& verify_details) {} + +bool QboneClientSession::HasActiveRequests() const { + return (stream_map().size() - num_incoming_static_streams() - + num_outgoing_static_streams()) > 0; +} + +} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/qbone/qbone_client_session.h b/chromium/net/third_party/quiche/src/quic/qbone/qbone_client_session.h new file mode 100644 index 00000000000..5dcf2acfca4 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/qbone_client_session.h @@ -0,0 +1,76 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef QUICHE_QUIC_QBONE_QBONE_CLIENT_SESSION_H_ +#define QUICHE_QUIC_QBONE_QBONE_CLIENT_SESSION_H_ + +#include "net/third_party/quiche/src/quic/core/quic_crypto_client_stream.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/qbone/qbone_control.pb.h" +#include "net/third_party/quiche/src/quic/qbone/qbone_control_stream.h" +#include "net/third_party/quiche/src/quic/qbone/qbone_packet_writer.h" +#include "net/third_party/quiche/src/quic/qbone/qbone_session_base.h" + +namespace quic { + +class QUIC_EXPORT_PRIVATE QboneClientSession + : public QboneSessionBase, + public QuicCryptoClientStream::ProofHandler { + public: + QboneClientSession(QuicConnection* connection, + QuicCryptoClientConfig* quic_crypto_client_config, + QuicSession::Visitor* owner, + const QuicConfig& config, + const ParsedQuicVersionVector& supported_versions, + const QuicServerId& server_id, + QbonePacketWriter* writer, + QboneClientControlStream::Handler* handler); + QboneClientSession(const QboneClientSession&) = delete; + QboneClientSession& operator=(const QboneClientSession&) = delete; + ~QboneClientSession() override; + + // QuicSession overrides. This will initiate the crypto stream. + void Initialize() override; + + // Returns the number of client hello messages that have been sent on the + // crypto stream. If the handshake has completed then this is one greater + // than the number of round-trips needed for the handshake. + int GetNumSentClientHellos() const; + int GetNumReceivedServerConfigUpdates() const; + + bool SendServerRequest(const QboneServerRequest& request); + + void ProcessPacketFromNetwork(QuicStringPiece packet) override; + void ProcessPacketFromPeer(QuicStringPiece packet) override; + + // Returns true if there are active requests on this session. + bool HasActiveRequests() const; + + protected: + // QboneSessionBase interface implementation. + std::unique_ptr<QuicCryptoStream> CreateCryptoStream() override; + + // ProofHandler interface implementation. + void OnProofValid(const QuicCryptoClientConfig::CachedState& cached) override; + void OnProofVerifyDetailsAvailable( + const ProofVerifyDetails& verify_details) override; + + QuicServerId server_id() { return server_id_; } + QuicCryptoClientConfig* crypto_client_config() { + return quic_crypto_client_config_; + } + + private: + QuicServerId server_id_; + // Config for QUIC crypto client stream, used by the client. + QuicCryptoClientConfig* quic_crypto_client_config_; + // Passed to the control stream. + QboneClientControlStream::Handler* handler_; + // The unowned control stream. + QboneClientControlStream* control_stream_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_QBONE_QBONE_CLIENT_SESSION_H_ diff --git a/chromium/net/third_party/quiche/src/quic/qbone/qbone_client_test.cc b/chromium/net/third_party/quiche/src/quic/qbone/qbone_client_test.cc new file mode 100644 index 00000000000..2902b19f0e1 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/qbone_client_test.cc @@ -0,0 +1,259 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Sets up a dispatcher and sends requests via the QboneClient. + +#include "net/third_party/quiche/src/quic/qbone/qbone_client.h" + +#include "net/third_party/quiche/src/quic/core/quic_alarm_factory.h" +#include "net/third_party/quiche/src/quic/core/quic_default_packet_writer.h" +#include "net/third_party/quiche/src/quic/core/quic_dispatcher.h" +#include "net/third_party/quiche/src/quic/core/quic_epoll_alarm_factory.h" +#include "net/third_party/quiche/src/quic/core/quic_epoll_connection_helper.h" +#include "net/third_party/quiche/src/quic/core/quic_packet_reader.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_mutex.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_port_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test_loopback.h" +#include "net/quic/platform/impl/quic_socket_utils.h" +#include "net/third_party/quiche/src/quic/qbone/qbone_constants.h" +#include "net/third_party/quiche/src/quic/qbone/qbone_packet_processor_test_tools.h" +#include "net/third_party/quiche/src/quic/qbone/qbone_server_session.h" +#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_server_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/server_thread.h" +#include "net/third_party/quiche/src/quic/tools/quic_memory_cache_backend.h" +#include "net/third_party/quiche/src/quic/tools/quic_server.h" + +namespace quic { +namespace test { +namespace { + +string TestPacketIn(const string& body) { + return PrependIPv6HeaderForTest(body, 5); +} + +string TestPacketOut(const string& body) { + return PrependIPv6HeaderForTest(body, 4); +} + +class DataSavingQbonePacketWriter : public QbonePacketWriter { + public: + void WritePacketToNetwork(const char* packet, size_t size) override { + QuicWriterMutexLock lock(&mu_); + data_.push_back(string(packet, size)); + } + + std::vector<string> data() { + QuicWriterMutexLock lock(&mu_); + return data_; + } + + private: + QuicMutex mu_; + std::vector<string> data_; +}; + +// A subclass of a qbone session that will own the connection passed in. +class ConnectionOwningQboneServerSession : public QboneServerSession { + public: + ConnectionOwningQboneServerSession( + const ParsedQuicVersionVector& supported_versions, + QuicConnection* connection, + Visitor* owner, + const QuicConfig& config, + const QuicCryptoServerConfig* quic_crypto_server_config, + QuicCompressedCertsCache* compressed_certs_cache, + QbonePacketWriter* writer) + : QboneServerSession(supported_versions, + connection, + owner, + config, + quic_crypto_server_config, + compressed_certs_cache, + writer, + TestLoopback6(), + TestLoopback6(), + 64, + nullptr), + connection_(connection) {} + + private: + // Note that we don't expect the QboneServerSession or any of its parent + // classes to do anything with the connection_ in their destructors. + std::unique_ptr<QuicConnection> connection_; +}; + +class QuicQboneDispatcher : public QuicDispatcher { + public: + QuicQboneDispatcher( + const QuicConfig* config, + const QuicCryptoServerConfig* crypto_config, + QuicVersionManager* version_manager, + std::unique_ptr<QuicConnectionHelperInterface> helper, + std::unique_ptr<QuicCryptoServerStream::Helper> session_helper, + std::unique_ptr<QuicAlarmFactory> alarm_factory, + QbonePacketWriter* writer) + : QuicDispatcher(config, + crypto_config, + version_manager, + std::move(helper), + std::move(session_helper), + std::move(alarm_factory), + kQuicDefaultConnectionIdLength), + writer_(writer) {} + + QuicSession* CreateQuicSession( + QuicConnectionId id, + const QuicSocketAddress& client, + QuicStringPiece alpn, + const quic::ParsedQuicVersion& version) override { + CHECK_EQ(alpn, "qbone"); + QuicConnection* connection = + new QuicConnection(id, client, helper(), alarm_factory(), writer(), + /* owns_writer= */ false, Perspective::IS_SERVER, + ParsedQuicVersionVector{version}); + // The connection owning wrapper owns the connection created. + QboneServerSession* session = new ConnectionOwningQboneServerSession( + GetSupportedVersions(), connection, this, config(), crypto_config(), + compressed_certs_cache(), writer_); + session->Initialize(); + return session; + } + + QuicConnectionId GenerateNewServerConnectionId( + ParsedQuicVersion version, + QuicConnectionId connection_id) const override { + char connection_id_bytes[kQuicDefaultConnectionIdLength] = {}; + return QuicConnectionId(connection_id_bytes, sizeof(connection_id_bytes)); + } + + private: + QbonePacketWriter* writer_; +}; + +class QboneTestServer : public QuicServer { + public: + explicit QboneTestServer(std::unique_ptr<ProofSource> proof_source) + : QuicServer(std::move(proof_source), &response_cache_) {} + QuicDispatcher* CreateQuicDispatcher() override { + QuicEpollAlarmFactory alarm_factory(epoll_server()); + return new QuicQboneDispatcher( + &config(), &crypto_config(), version_manager(), + std::unique_ptr<QuicEpollConnectionHelper>( + new QuicEpollConnectionHelper(epoll_server(), + QuicAllocator::BUFFER_POOL)), + std::unique_ptr<QuicCryptoServerStream::Helper>( + new QboneCryptoServerStreamHelper()), + std::unique_ptr<QuicEpollAlarmFactory>( + new QuicEpollAlarmFactory(epoll_server())), + &writer_); + } + + std::vector<string> data() { return writer_.data(); } + + void WaitForDataSize(int n) { + while (data().size() != n) { + } + } + + private: + quic::QuicMemoryCacheBackend response_cache_; + DataSavingQbonePacketWriter writer_; +}; + +class QboneTestClient : public QboneClient { + public: + QboneTestClient(QuicSocketAddress server_address, + const QuicServerId& server_id, + const ParsedQuicVersionVector& supported_versions, + QuicEpollServer* epoll_server, + std::unique_ptr<ProofVerifier> proof_verifier) + : QboneClient(server_address, + server_id, + supported_versions, + /*session_owner=*/nullptr, + QuicConfig(), + epoll_server, + std::move(proof_verifier), + &qbone_writer_, + nullptr) {} + + ~QboneTestClient() override {} + + void SendData(const string& data) { + qbone_session()->ProcessPacketFromNetwork(data); + } + + void WaitForWriteToFlush() { + while (connected() && session()->HasDataToWrite()) { + WaitForEvents(); + } + } + + void WaitForDataSize(int n) { + while (data().size() != n) { + WaitForEvents(); + } + } + + std::vector<string> data() { return qbone_writer_.data(); } + + private: + DataSavingQbonePacketWriter qbone_writer_; +}; + +TEST(QboneClientTest, SendDataFromClient) { + SetQuicReloadableFlag(quic_use_parse_public_header, true); + auto server = new QboneTestServer(crypto_test_utils::ProofSourceForTesting()); + QuicSocketAddress server_address(TestLoopback(), + QuicPickServerPortForTestsOrDie()); + ServerThread server_thread(server, server_address); + server_thread.Initialize(); + server_thread.Start(); + + QuicEpollServer epoll_server; + QboneTestClient client( + server_address, + QuicServerId("test.example.com", server_address.port(), false), + AllSupportedVersions(), &epoll_server, + crypto_test_utils::ProofVerifierForTesting()); + ASSERT_TRUE(client.Initialize()); + ASSERT_TRUE(client.Connect()); + ASSERT_TRUE(client.WaitForCryptoHandshakeConfirmed()); + client.SendData(TestPacketIn("hello")); + client.SendData(TestPacketIn("world")); + client.WaitForWriteToFlush(); + server->WaitForDataSize(2); + EXPECT_THAT(server->data()[0], testing::Eq(TestPacketOut("hello"))); + EXPECT_THAT(server->data()[1], testing::Eq(TestPacketOut("world"))); + auto server_session = + static_cast<QboneServerSession*>(QuicServerPeer::GetDispatcher(server) + ->session_map() + .begin() + ->second.get()); + string long_data(QboneConstants::kMaxQbonePacketBytes - sizeof(ip6_hdr) - 1, + 'A'); + // Pretend the server gets data. + server_thread.Schedule([&server_session, &long_data]() { + server_session->ProcessPacketFromNetwork( + TestPacketIn("Somethingsomething")); + server_session->ProcessPacketFromNetwork(TestPacketIn(long_data)); + server_session->ProcessPacketFromNetwork(TestPacketIn(long_data)); + }); + client.WaitForDataSize(3); + EXPECT_THAT(client.data()[0], + testing::Eq(TestPacketOut("Somethingsomething"))); + EXPECT_THAT(client.data()[1], testing::Eq(TestPacketOut(long_data))); + EXPECT_THAT(client.data()[2], testing::Eq(TestPacketOut(long_data))); + + client.Disconnect(); + server_thread.Quit(); + server_thread.Join(); +} + +} // namespace +} // namespace test +} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/qbone/qbone_constants.cc b/chromium/net/third_party/quiche/src/quic/qbone/qbone_constants.cc new file mode 100644 index 00000000000..a83f74e4d1d --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/qbone_constants.cc @@ -0,0 +1,36 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/third_party/quiche/src/quic/qbone/qbone_constants.h" + +#include "net/third_party/quiche/src/quic/core/quic_utils.h" + +namespace quic { + +constexpr char QboneConstants::kQboneAlpn[]; +const QuicByteCount QboneConstants::kMaxQbonePacketBytes; +const uint32_t QboneConstants::kQboneRouteTableId; + +QuicStreamId QboneConstants::GetControlStreamId(QuicTransportVersion version) { + return QuicUtils::GetFirstBidirectionalStreamId(version, + Perspective::IS_CLIENT); +} + +const QuicIpAddress* QboneConstants::TerminatorLocalAddress() { + static auto* terminator_address = []() { + QuicIpAddress* address = new QuicIpAddress; + // 0x71 0x62 0x6f 0x6e 0x65 is 'qbone' in ascii. + address->FromString("fe80::71:626f:6e65"); + return address; + }(); + return terminator_address; +} + +const IpRange* QboneConstants::TerminatorLocalAddressRange() { + static auto* range = + new quic::IpRange(*quic::QboneConstants::TerminatorLocalAddress(), 128); + return range; +} + +} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/qbone/qbone_constants.h b/chromium/net/third_party/quiche/src/quic/qbone/qbone_constants.h new file mode 100644 index 00000000000..1fa2688f518 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/qbone_constants.h @@ -0,0 +1,32 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef QUICHE_QUIC_QBONE_QBONE_CONSTANTS_H_ +#define QUICHE_QUIC_QBONE_QBONE_CONSTANTS_H_ + +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/core/quic_versions.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ip_address.h" +#include "net/third_party/quiche/src/quic/qbone/platform/ip_range.h" + +namespace quic { + +struct QboneConstants { + // Qbone's ALPN + static constexpr char kQboneAlpn[] = "qbone"; + // The maximum number of bytes allowed in a qbone packet. + static const QuicByteCount kMaxQbonePacketBytes = 2000; + // The table id for Qbone's routing table. 'bone' in ascii. + static const uint32_t kQboneRouteTableId = 0x626F6E65; + // The stream ID of the control channel. + static QuicStreamId GetControlStreamId(QuicTransportVersion version); + // The link-local address of the Terminator + static const QuicIpAddress* TerminatorLocalAddress(); + // The IPRange containing the TerminatorLocalAddress + static const IpRange* TerminatorLocalAddressRange(); +}; + +} // namespace quic + +#endif // QUICHE_QUIC_QBONE_QBONE_CONSTANTS_H_ diff --git a/chromium/net/third_party/quiche/src/quic/qbone/qbone_control.proto b/chromium/net/third_party/quiche/src/quic/qbone/qbone_control.proto new file mode 100644 index 00000000000..f0090d6cf8a --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/qbone_control.proto @@ -0,0 +1,13 @@ +syntax = "proto2"; + +option optimize_for = LITE_RUNTIME; + +package quic; + +message QboneServerRequest { + extensions 1000 to max; +}; + +message QboneClientRequest { + extensions 1000 to max; +}; diff --git a/chromium/net/third_party/quiche/src/quic/qbone/qbone_control_placeholder.proto b/chromium/net/third_party/quiche/src/quic/qbone/qbone_control_placeholder.proto new file mode 100644 index 00000000000..375b015df02 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/qbone_control_placeholder.proto @@ -0,0 +1,20 @@ +syntax = "proto2"; + +option optimize_for = LITE_RUNTIME; + +package quic; + +import "net/third_party/quiche/src/quic/qbone/qbone_control.proto"; + +// These provide fields for QboneServerRequest and QboneClientRequest that are +// used to test the control channel. Once the control channel actually has real +// data to pass they can be removed. +// TODO(b/62139999): Remove this file in favor of testing actual configuration. + +extend QboneServerRequest { + optional string server_placeholder = 179838467; +} + +extend QboneClientRequest { + optional string client_placeholder = 179838467; +} diff --git a/chromium/net/third_party/quiche/src/quic/qbone/qbone_control_stream.cc b/chromium/net/third_party/quiche/src/quic/qbone/qbone_control_stream.cc new file mode 100644 index 00000000000..6272f3ff883 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/qbone_control_stream.cc @@ -0,0 +1,67 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/third_party/quiche/src/quic/qbone/qbone_control_stream.h" + +#include "net/third_party/quiche/src/quic/core/quic_session.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" +#include "net/third_party/quiche/src/quic/qbone/qbone_constants.h" + +namespace quic { + +namespace { +static constexpr size_t kRequestSizeBytes = sizeof(uint16_t); +} // namespace + +QboneControlStreamBase::QboneControlStreamBase(QuicSession* session) + : QuicStream(QboneConstants::GetControlStreamId( + session->connection()->transport_version()), + session, + /*is_static=*/true, + BIDIRECTIONAL), + pending_message_size_(0) {} + +void QboneControlStreamBase::OnDataAvailable() { + sequencer()->Read(&buffer_); + while (true) { + if (pending_message_size_ == 0) { + // Start of a message. + if (buffer_.size() < kRequestSizeBytes) { + return; + } + memcpy(&pending_message_size_, buffer_.data(), kRequestSizeBytes); + buffer_.erase(0, kRequestSizeBytes); + } + // Continuation of a message. + if (buffer_.size() < pending_message_size_) { + return; + } + string tmp = buffer_.substr(0, pending_message_size_); + buffer_.erase(0, pending_message_size_); + pending_message_size_ = 0; + OnMessage(tmp); + } +} + +bool QboneControlStreamBase::SendMessage(const proto2::Message& proto) { + string tmp; + if (!proto.SerializeToString(&tmp)) { + QUIC_BUG << "Failed to serialize QboneControlRequest"; + return false; + } + if (tmp.size() > kuint16max) { + QUIC_BUG << "QboneControlRequest too large: " << tmp.size() << " > " + << kuint16max; + return false; + } + uint16_t size = tmp.size(); + char size_str[kRequestSizeBytes]; + memcpy(size_str, &size, kRequestSizeBytes); + WriteOrBufferData(QuicStringPiece(size_str, kRequestSizeBytes), false, + nullptr); + WriteOrBufferData(tmp, false, nullptr); + return true; +} + +} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/qbone/qbone_control_stream.h b/chromium/net/third_party/quiche/src/quic/qbone/qbone_control_stream.h new file mode 100644 index 00000000000..6e3dead9d8e --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/qbone_control_stream.h @@ -0,0 +1,75 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef QUICHE_QUIC_QBONE_QBONE_CONTROL_STREAM_H_ +#define QUICHE_QUIC_QBONE_QBONE_CONTROL_STREAM_H_ + +#include "net/third_party/quiche/src/quic/core/quic_stream.h" +#include "net/third_party/quiche/src/quic/qbone/qbone_control.pb.h" + +namespace quic { + +class QboneSessionBase; + +class QUIC_EXPORT_PRIVATE QboneControlStreamBase : public QuicStream { + public: + explicit QboneControlStreamBase(QuicSession* session); + + void OnDataAvailable() override; + + protected: + virtual void OnMessage(const string& data) = 0; + bool SendMessage(const proto2::Message& proto); + + private: + uint16_t pending_message_size_; + string buffer_; +}; + +template <class T> +class QUIC_EXPORT_PRIVATE QboneControlHandler { + public: + virtual ~QboneControlHandler() { } + + virtual void OnControlRequest(const T& request) = 0; + virtual void OnControlError() = 0; +}; + +template <class Incoming, class Outgoing> +class QUIC_EXPORT_PRIVATE QboneControlStream : public QboneControlStreamBase { + public: + using Handler = QboneControlHandler<Incoming>; + + QboneControlStream(QuicSession* session, Handler* handler) + : QboneControlStreamBase(session), handler_(handler) {} + + bool SendRequest(const Outgoing& request) { return SendMessage(request); } + + protected: + void OnMessage(const string& data) override { + Incoming request; + if (!request.ParseFromString(data)) { + QUIC_LOG(ERROR) << "Failed to parse incoming request"; + if (handler_ != nullptr) { + handler_->OnControlError(); + } + return; + } + if (handler_ != nullptr) { + handler_->OnControlRequest(request); + } + } + + private: + Handler* handler_; +}; + +using QboneServerControlStream = + QboneControlStream<QboneServerRequest, QboneClientRequest>; +using QboneClientControlStream = + QboneControlStream<QboneClientRequest, QboneServerRequest>; + +} // namespace quic + +#endif // QUICHE_QUIC_QBONE_QBONE_CONTROL_STREAM_H_ diff --git a/chromium/net/third_party/quiche/src/quic/qbone/qbone_packet_exchanger.cc b/chromium/net/third_party/quiche/src/quic/qbone/qbone_packet_exchanger.cc new file mode 100644 index 00000000000..3f3a5f9cea6 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/qbone_packet_exchanger.cc @@ -0,0 +1,70 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/third_party/quiche/src/quic/qbone/qbone_packet_exchanger.h" + +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" + +namespace quic { + +bool QbonePacketExchanger::ReadAndDeliverPacket( + QboneClientInterface* qbone_client) { + bool blocked = false; + string error; + std::unique_ptr<QuicData> packet = ReadPacket(&blocked, &error); + if (packet == nullptr) { + if (!blocked) { + visitor_->OnReadError(error); + } + return false; + } + qbone_client->ProcessPacketFromNetwork(packet->AsStringPiece()); + return true; +} + +void QbonePacketExchanger::WritePacketToNetwork(const char* packet, + size_t size) { + bool blocked = false; + string error; + if (packet_queue_.empty() && !write_blocked_) { + if (WritePacket(packet, size, &blocked, &error)) { + return; + } + if (!blocked) { + visitor_->OnWriteError(error); + return; + } + write_blocked_ = true; + } + + // Drop the packet on the floor if the queue if full. + if (packet_queue_.size() >= max_pending_packets_) { + return; + } + + auto data_copy = new char[size]; + memcpy(data_copy, packet, size); + packet_queue_.push_back( + QuicMakeUnique<QuicData>(data_copy, size, /* owns_buffer = */ true)); +} + +void QbonePacketExchanger::SetWritable() { + write_blocked_ = false; + while (!packet_queue_.empty()) { + bool blocked = false; + string error; + if (WritePacket(packet_queue_.front()->data(), + packet_queue_.front()->length(), &blocked, &error)) { + packet_queue_.pop_front(); + } else { + if (!blocked) { + visitor_->OnWriteError(error); + } + write_blocked_ = blocked; + return; + } + } +} + +} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/qbone/qbone_packet_exchanger.h b/chromium/net/third_party/quiche/src/quic/qbone/qbone_packet_exchanger.h new file mode 100644 index 00000000000..8620a492fdb --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/qbone_packet_exchanger.h @@ -0,0 +1,80 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef QUICHE_QUIC_QBONE_QBONE_PACKET_EXCHANGER_H_ +#define QUICHE_QUIC_QBONE_QBONE_PACKET_EXCHANGER_H_ + +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/qbone/qbone_client_interface.h" +#include "net/third_party/quiche/src/quic/qbone/qbone_packet_writer.h" + +namespace quic { + +// Handles reading and writing on the local network and exchange packets between +// the local network with a Qbone connection. +class QbonePacketExchanger : public QbonePacketWriter { + public: + // The owner might want to receive notifications when read or write fails. + class Visitor { + public: + virtual ~Visitor() {} + virtual void OnReadError(const string& error) {} + virtual void OnWriteError(const string& error) {} + }; + // Does not take ownership of visitor. + QbonePacketExchanger(Visitor* visitor, size_t max_pending_packets) + : visitor_(visitor), max_pending_packets_(max_pending_packets) {} + + QbonePacketExchanger(const QbonePacketExchanger&) = delete; + QbonePacketExchanger& operator=(const QbonePacketExchanger&) = delete; + + QbonePacketExchanger(QbonePacketExchanger&&) = delete; + QbonePacketExchanger& operator=(QbonePacketExchanger&&) = delete; + + ~QbonePacketExchanger() = default; + + // Returns true if there may be more packets to read. + // Implementations handles the actual raw read and delivers the packet to + // qbone_client. + bool ReadAndDeliverPacket(QboneClientInterface* qbone_client); + + // From QbonePacketWriter. + // Writes a packet to the local network. If the write would be blocked, the + // packet will be queued if the queue is smaller than max_pending_packets_. + void WritePacketToNetwork(const char* packet, size_t size) override; + + // The caller signifies that the local network is no longer blocked. + void SetWritable(); + + private: + // The actual implementation that reads a packet from the local network. + // Returns the packet if one is successfully read. This might nullptr when a) + // there is no packet to read, b) the read failed. In the former case, blocked + // is set to true. error contains the error message. + virtual std::unique_ptr<QuicData> ReadPacket(bool* blocked, + string* error) = 0; + + // The actual implementation that writes a packet to the local network. + // Returns true if the write succeeds. blocked will be set to true if the + // write failure is caused by the local network being blocked. error contains + // the error message. + virtual bool WritePacket(const char* packet, + size_t size, + bool* blocked, + string* error) = 0; + + std::list<std::unique_ptr<QuicData>> packet_queue_; + + Visitor* visitor_; + + // The maximum number of packets that could be queued up when writing to local + // network is blocked. + size_t max_pending_packets_; + + bool write_blocked_ = false; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_QBONE_QBONE_PACKET_EXCHANGER_H_ diff --git a/chromium/net/third_party/quiche/src/quic/qbone/qbone_packet_exchanger_test.cc b/chromium/net/third_party/quiche/src/quic/qbone/qbone_packet_exchanger_test.cc new file mode 100644 index 00000000000..4e63b994721 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/qbone_packet_exchanger_test.cc @@ -0,0 +1,253 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/third_party/quiche/src/quic/qbone/qbone_packet_exchanger.h" + +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/qbone/mock_qbone_client.h" + +namespace quic { +namespace { + +using ::testing::StrEq; +using ::testing::StrictMock; + +const size_t kMaxPendingPackets = 2; + +class MockVisitor : public QbonePacketExchanger::Visitor { + public: + MOCK_METHOD1(OnReadError, void(const string&)); + MOCK_METHOD1(OnWriteError, void(const string&)); +}; + +class FakeQbonePacketExchanger : public QbonePacketExchanger { + public: + using QbonePacketExchanger::QbonePacketExchanger; + + // Adds a packet to the end of list of packets to be returned by ReadPacket. + // When the list is empty, ReadPacket returns nullptr to signify error as + // defined by QbonePacketExchanger. If SetReadError is not called or called + // with empty error string, ReadPacket sets blocked to true. + void AddPacketToBeRead(std::unique_ptr<QuicData> packet) { + packets_to_be_read_.push_back(std::move(packet)); + } + + // Sets the error to be returned by ReadPacket when the list of packets is + // empty. If error is empty string, blocked is set by ReadPacket. + void SetReadError(const string& error) { read_error_ = error; } + + // Force WritePacket to fail with the given status. WritePacket returns true + // when blocked == true and error is empty. + void ForceWriteFailure(bool blocked, const string& error) { + write_blocked_ = blocked; + write_error_ = error; + } + + // Packets that have been successfully written by WritePacket. + const std::vector<string>& packets_written() const { + return packets_written_; + } + + private: + // Implements QbonePacketExchanger::ReadPacket. + std::unique_ptr<QuicData> ReadPacket(bool* blocked, string* error) override { + *blocked = false; + + if (packets_to_be_read_.empty()) { + *blocked = read_error_.empty(); + *error = read_error_; + return nullptr; + } + + std::unique_ptr<QuicData> packet = std::move(packets_to_be_read_.front()); + packets_to_be_read_.pop_front(); + return packet; + } + + // Implements QbonePacketExchanger::WritePacket. + bool WritePacket(const char* packet, + size_t size, + bool* blocked, + string* error) override { + *blocked = false; + + if (write_blocked_ || !write_error_.empty()) { + *blocked = write_blocked_; + *error = write_error_; + return false; + } + + packets_written_.push_back(string(packet, size)); + return true; + } + + string read_error_; + std::list<std::unique_ptr<QuicData>> packets_to_be_read_; + + string write_error_; + bool write_blocked_ = false; + std::vector<string> packets_written_; +}; + +TEST(QbonePacketExchangerTest, + ReadAndDeliverPacketDeliversPacketToQboneClient) { + StrictMock<MockVisitor> visitor; + FakeQbonePacketExchanger exchanger(&visitor, kMaxPendingPackets); + StrictMock<MockQboneClient> client; + + string packet = "data"; + exchanger.AddPacketToBeRead( + QuicMakeUnique<QuicData>(packet.data(), packet.length())); + EXPECT_CALL(client, ProcessPacketFromNetwork(StrEq("data"))); + + EXPECT_TRUE(exchanger.ReadAndDeliverPacket(&client)); +} + +TEST(QbonePacketExchangerTest, + ReadAndDeliverPacketNotifiesVisitorOnReadFailure) { + MockVisitor visitor; + FakeQbonePacketExchanger exchanger(&visitor, kMaxPendingPackets); + MockQboneClient client; + + // Force read error. + string io_error = "I/O error"; + exchanger.SetReadError(io_error); + EXPECT_CALL(visitor, OnReadError(StrEq(io_error))).Times(1); + + EXPECT_FALSE(exchanger.ReadAndDeliverPacket(&client)); +} + +TEST(QbonePacketExchangerTest, + ReadAndDeliverPacketDoesNotNotifyVisitorOnBlockedIO) { + MockVisitor visitor; + FakeQbonePacketExchanger exchanger(&visitor, kMaxPendingPackets); + MockQboneClient client; + + // No more packets to read. + EXPECT_FALSE(exchanger.ReadAndDeliverPacket(&client)); +} + +TEST(QbonePacketExchangerTest, + WritePacketToNetworkWritesDirectlyToNetworkWhenNotBlocked) { + MockVisitor visitor; + FakeQbonePacketExchanger exchanger(&visitor, kMaxPendingPackets); + MockQboneClient client; + + string packet = "data"; + exchanger.WritePacketToNetwork(packet.data(), packet.length()); + + ASSERT_EQ(exchanger.packets_written().size(), 1); + EXPECT_THAT(exchanger.packets_written()[0], StrEq(packet)); +} + +TEST(QbonePacketExchangerTest, + WritePacketToNetworkQueuesPacketsAndProcessThemLater) { + MockVisitor visitor; + FakeQbonePacketExchanger exchanger(&visitor, kMaxPendingPackets); + MockQboneClient client; + + // Force write to be blocked so that packets are queued. + exchanger.ForceWriteFailure(true, ""); + std::vector<string> packets = {"packet0", "packet1"}; + for (int i = 0; i < packets.size(); i++) { + exchanger.WritePacketToNetwork(packets[i].data(), packets[i].length()); + } + + // Nothing should have been written because of blockage. + ASSERT_TRUE(exchanger.packets_written().empty()); + + // Remove blockage and start proccessing queued packets. + exchanger.ForceWriteFailure(false, ""); + exchanger.SetWritable(); + + // Queued packets are processed. + ASSERT_EQ(exchanger.packets_written().size(), 2); + for (int i = 0; i < packets.size(); i++) { + EXPECT_THAT(exchanger.packets_written()[i], StrEq(packets[i])); + } +} + +TEST(QbonePacketExchangerTest, + SetWritableContinuesProcessingPacketIfPreviousCallBlocked) { + MockVisitor visitor; + FakeQbonePacketExchanger exchanger(&visitor, kMaxPendingPackets); + MockQboneClient client; + + // Force write to be blocked so that packets are queued. + exchanger.ForceWriteFailure(true, ""); + std::vector<string> packets = {"packet0", "packet1"}; + for (int i = 0; i < packets.size(); i++) { + exchanger.WritePacketToNetwork(packets[i].data(), packets[i].length()); + } + + // Nothing should have been written because of blockage. + ASSERT_TRUE(exchanger.packets_written().empty()); + + // Start processing packets, but since writes are still blocked, nothing + // should have been written. + exchanger.SetWritable(); + ASSERT_TRUE(exchanger.packets_written().empty()); + + // Remove blockage and start processing packets again. + exchanger.ForceWriteFailure(false, ""); + exchanger.SetWritable(); + + ASSERT_EQ(exchanger.packets_written().size(), 2); + for (int i = 0; i < packets.size(); i++) { + EXPECT_THAT(exchanger.packets_written()[i], StrEq(packets[i])); + } +} + +TEST(QbonePacketExchangerTest, WritePacketToNetworkDropsPacketIfQueueIfFull) { + std::vector<string> packets = {"packet0", "packet1", "packet2"}; + size_t queue_size = packets.size() - 1; + MockVisitor visitor; + // exchanger has smaller queue than number of packets. + FakeQbonePacketExchanger exchanger(&visitor, queue_size); + MockQboneClient client; + + exchanger.ForceWriteFailure(true, ""); + for (int i = 0; i < packets.size(); i++) { + exchanger.WritePacketToNetwork(packets[i].data(), packets[i].length()); + } + + // Blocked writes cause packets to be queued or dropped. + ASSERT_TRUE(exchanger.packets_written().empty()); + + exchanger.ForceWriteFailure(false, ""); + exchanger.SetWritable(); + + ASSERT_EQ(exchanger.packets_written().size(), queue_size); + for (int i = 0; i < queue_size; i++) { + EXPECT_THAT(exchanger.packets_written()[i], StrEq(packets[i])); + } +} + +TEST(QbonePacketExchangerTest, WriteErrorsGetNotified) { + MockVisitor visitor; + FakeQbonePacketExchanger exchanger(&visitor, kMaxPendingPackets); + MockQboneClient client; + string packet = "data"; + + // Write error is delivered to visitor during WritePacketToNetwork. + string io_error = "I/O error"; + exchanger.ForceWriteFailure(false, io_error); + EXPECT_CALL(visitor, OnWriteError(StrEq(io_error))).Times(1); + exchanger.WritePacketToNetwork(packet.data(), packet.length()); + ASSERT_TRUE(exchanger.packets_written().empty()); + + // Write error is delivered to visitor during SetWritable. + exchanger.ForceWriteFailure(true, ""); + exchanger.WritePacketToNetwork(packet.data(), packet.length()); + + string sys_error = "sys error"; + exchanger.ForceWriteFailure(false, sys_error); + EXPECT_CALL(visitor, OnWriteError(StrEq(sys_error))).Times(1); + exchanger.SetWritable(); + ASSERT_TRUE(exchanger.packets_written().empty()); +} + +} // namespace +} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/qbone/qbone_packet_processor.cc b/chromium/net/third_party/quiche/src/quic/qbone/qbone_packet_processor.cc new file mode 100644 index 00000000000..db7a1382a8f --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/qbone_packet_processor.cc @@ -0,0 +1,269 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/third_party/quiche/src/quic/qbone/qbone_packet_processor.h" + +#include <cstring> + +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_endian.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/qbone/platform/icmp_packet.h" +#include "net/third_party/quiche/src/quic/qbone/platform/internet_checksum.h" +#include "net/third_party/quiche/src/quic/qbone/platform/tcp_packet.h" + +namespace { + +constexpr size_t kIPv6AddressSize = 16; +constexpr size_t kIPv6MinPacketSize = 1280; +constexpr size_t kIcmpTtl = 64; +constexpr size_t kICMPv6DestinationUnreachableDueToSourcePolicy = 5; + +} // namespace + +namespace quic { + +const QuicIpAddress QbonePacketProcessor::kInvalidIpAddress = + QuicIpAddress::Any6(); + +QbonePacketProcessor::QbonePacketProcessor(QuicIpAddress self_ip, + QuicIpAddress client_ip, + size_t client_ip_subnet_length, + OutputInterface* output, + StatsInterface* stats) + : client_ip_(client_ip), + output_(output), + stats_(stats), + filter_(new Filter) { + memcpy(self_ip_.s6_addr, self_ip.ToPackedString().data(), kIPv6AddressSize); + DCHECK_LE(client_ip_subnet_length, kIPv6AddressSize * 8); + client_ip_subnet_length_ = client_ip_subnet_length; + + DCHECK(IpAddressFamily::IP_V6 == self_ip.address_family()); + DCHECK(IpAddressFamily::IP_V6 == client_ip.address_family()); + DCHECK(self_ip != kInvalidIpAddress); +} + +QbonePacketProcessor::OutputInterface::~OutputInterface() {} +QbonePacketProcessor::StatsInterface::~StatsInterface() {} +QbonePacketProcessor::Filter::~Filter() {} + +QbonePacketProcessor::ProcessingResult +QbonePacketProcessor::Filter::FilterPacket(Direction direction, + QuicStringPiece full_packet, + QuicStringPiece payload, + icmp6_hdr* icmp_header, + OutputInterface* output) { + return ProcessingResult::OK; +} + +void QbonePacketProcessor::ProcessPacket(string* packet, Direction direction) { + if (QUIC_PREDICT_FALSE(!IsValid())) { + QUIC_BUG << "QuicPacketProcessor is invoked in an invalid state."; + stats_->OnPacketDroppedSilently(direction); + return; + } + + uint8_t transport_protocol; + char* transport_data; + icmp6_hdr icmp_header; + memset(&icmp_header, 0, sizeof(icmp_header)); + ProcessingResult result = ProcessIPv6HeaderAndFilter( + packet, direction, &transport_protocol, &transport_data, &icmp_header); + + switch (result) { + case ProcessingResult::OK: + switch (direction) { + case Direction::FROM_CLIENT: + output_->SendPacketToNetwork(*packet); + break; + case Direction::FROM_NETWORK: + output_->SendPacketToClient(*packet); + break; + } + stats_->OnPacketForwarded(direction); + break; + case ProcessingResult::SILENT_DROP: + stats_->OnPacketDroppedSilently(direction); + break; + case ProcessingResult::DEFER: + stats_->OnPacketDeferred(direction); + break; + case ProcessingResult::ICMP: + SendIcmpResponse(&icmp_header, *packet, direction); + stats_->OnPacketDroppedWithIcmp(direction); + break; + case ProcessingResult::ICMP_AND_TCP_RESET: + SendIcmpResponse(&icmp_header, *packet, direction); + stats_->OnPacketDroppedWithIcmp(direction); + SendTcpReset(*packet, direction); + stats_->OnPacketDroppedWithTcpReset(direction); + break; + } +} + +QbonePacketProcessor::ProcessingResult +QbonePacketProcessor::ProcessIPv6HeaderAndFilter(string* packet, + Direction direction, + uint8_t* transport_protocol, + char** transport_data, + icmp6_hdr* icmp_header) { + ProcessingResult result = ProcessIPv6Header( + packet, direction, transport_protocol, transport_data, icmp_header); + + if (result == ProcessingResult::OK) { + char* packet_data = &*packet->begin(); + size_t header_size = *transport_data - packet_data; + // Sanity-check the bounds. + if (packet_data >= *transport_data || header_size > packet->size() || + header_size < kIPv6HeaderSize) { + QUIC_BUG << "Invalid pointers encountered in " + "QbonePacketProcessor::ProcessPacket. Dropping the packet"; + return ProcessingResult::SILENT_DROP; + } + + result = filter_->FilterPacket( + direction, *packet, + QuicStringPiece(*transport_data, packet->size() - header_size), + icmp_header, output_); + } + + // Do not send ICMP error messages in response to ICMP errors. + if (result == ProcessingResult::ICMP) { + const uint8_t* header = reinterpret_cast<const uint8_t*>(packet->data()); + + constexpr size_t kIPv6NextHeaderOffset = 6; + constexpr size_t kIcmpMessageTypeOffset = kIPv6HeaderSize + 0; + constexpr size_t kIcmpMessageTypeMaxError = 127; + if ( + // Check size. + packet->size() >= (kIPv6HeaderSize + kICMPv6HeaderSize) && + // Check that the packet is in fact ICMP. + header[kIPv6NextHeaderOffset] == IPPROTO_ICMPV6 && + // Check that ICMP message type is an error. + header[kIcmpMessageTypeOffset] < kIcmpMessageTypeMaxError) { + result = ProcessingResult::SILENT_DROP; + } + } + + return result; +} + +QbonePacketProcessor::ProcessingResult QbonePacketProcessor::ProcessIPv6Header( + string* packet, + Direction direction, + uint8_t* transport_protocol, + char** transport_data, + icmp6_hdr* icmp_header) { + // Check if the packet is big enough to have IPv6 header. + if (packet->size() < kIPv6HeaderSize) { + QUIC_DVLOG(1) << "Dropped malformed packet: IPv6 header too short"; + return ProcessingResult::SILENT_DROP; + } + + // Check version field. + ip6_hdr* header = reinterpret_cast<ip6_hdr*>(&*packet->begin()); + if (header->ip6_vfc >> 4 != 6) { + QUIC_DVLOG(1) << "Dropped malformed packet: IP version is not IPv6"; + return ProcessingResult::SILENT_DROP; + } + + // Check payload size. + const size_t declared_payload_size = + QuicEndian::NetToHost16(header->ip6_plen); + const size_t actual_payload_size = packet->size() - kIPv6HeaderSize; + if (declared_payload_size != actual_payload_size) { + QUIC_DVLOG(1) + << "Dropped malformed packet: incorrect packet length specified"; + return ProcessingResult::SILENT_DROP; + } + + // Check that the address of the client is in the packet. + QuicIpAddress address_to_check; + uint8_t address_reject_code; + bool ip_parse_result; + switch (direction) { + case Direction::FROM_CLIENT: + // Expect the source IP to match the client. + ip_parse_result = address_to_check.FromPackedString( + reinterpret_cast<const char*>(&header->ip6_src), + sizeof(header->ip6_src)); + address_reject_code = kICMPv6DestinationUnreachableDueToSourcePolicy; + break; + case Direction::FROM_NETWORK: + // Expect the destination IP to match the client. + ip_parse_result = address_to_check.FromPackedString( + reinterpret_cast<const char*>(&header->ip6_dst), + sizeof(header->ip6_src)); + address_reject_code = ICMP6_DST_UNREACH_NOROUTE; + break; + } + DCHECK(ip_parse_result); + if (!client_ip_.InSameSubnet(address_to_check, client_ip_subnet_length_)) { + QUIC_DVLOG(1) + << "Dropped packet: source/destination address is not client's"; + icmp_header->icmp6_type = ICMP6_DST_UNREACH; + icmp_header->icmp6_code = address_reject_code; + return ProcessingResult::ICMP; + } + + // Check and decrement TTL. + if (header->ip6_hops <= 1) { + icmp_header->icmp6_type = ICMP6_TIME_EXCEEDED; + icmp_header->icmp6_code = ICMP6_TIME_EXCEED_TRANSIT; + return ProcessingResult::ICMP; + } + header->ip6_hops--; + + // Check and extract IP headers. + switch (header->ip6_nxt) { + case IPPROTO_TCP: + case IPPROTO_UDP: + case IPPROTO_ICMPV6: + *transport_protocol = header->ip6_nxt; + *transport_data = (&*packet->begin()) + kIPv6HeaderSize; + break; + default: + icmp_header->icmp6_type = ICMP6_PARAM_PROB; + icmp_header->icmp6_code = ICMP6_PARAMPROB_NEXTHEADER; + return ProcessingResult::ICMP; + } + + return ProcessingResult::OK; +} + +void QbonePacketProcessor::SendIcmpResponse(icmp6_hdr* icmp_header, + QuicStringPiece original_packet, + Direction original_direction) { + in6_addr dst; + // TODO(b/70339814): ensure this is actually a unicast address. + memcpy(dst.s6_addr, &original_packet[8], kIPv6AddressSize); + + CreateIcmpPacket(self_ip_, dst, *icmp_header, original_packet, + [this, original_direction](QuicStringPiece packet) { + SendResponse(original_direction, packet); + }); +} + +void QbonePacketProcessor::SendTcpReset(QuicStringPiece original_packet, + Direction original_direction) { + CreateTcpResetPacket(original_packet, + [this, original_direction](QuicStringPiece packet) { + SendResponse(original_direction, packet); + }); +} + +void QbonePacketProcessor::SendResponse(Direction original_direction, + QuicStringPiece packet) { + switch (original_direction) { + case Direction::FROM_CLIENT: + output_->SendPacketToClient(packet); + break; + case Direction::FROM_NETWORK: + output_->SendPacketToNetwork(packet); + break; + } +} + +} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/qbone/qbone_packet_processor.h b/chromium/net/third_party/quiche/src/quic/qbone/qbone_packet_processor.h new file mode 100644 index 00000000000..44767715c61 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/qbone_packet_processor.h @@ -0,0 +1,198 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef QUICHE_QUIC_QBONE_QBONE_PACKET_PROCESSOR_H_ +#define QUICHE_QUIC_QBONE_QBONE_PACKET_PROCESSOR_H_ + +#include <netinet/icmp6.h> +#include <netinet/ip6.h> + +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ip_address.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" + +namespace quic { + +enum : size_t { + kIPv6HeaderSize = 40, + kICMPv6HeaderSize = sizeof(icmp6_hdr), + kTotalICMPv6HeaderSize = kIPv6HeaderSize + kICMPv6HeaderSize, +}; + +// QBONE packet processor accepts packets destined in either direction +// (client-to-network or network-to-client). It inspects them and makes +// decisions on whether they should be forwarded or dropped, replying with ICMP +// messages as appropriate. +class QbonePacketProcessor { + public: + enum class Direction { + // Packet is going from the QBONE client into the network behind the QBONE. + FROM_CLIENT = 0, + // Packet is going from the network begin QBONE to the client. + FROM_NETWORK = 1 + }; + + enum class ProcessingResult { + OK = 0, + SILENT_DROP = 1, + ICMP = 2, + // Equivalent to |SILENT_DROP| at the moment, but indicates that the + // downstream filter has buffered the packet and deferred its processing. + // The packet may be emitted at a later time. + DEFER = 3, + // In addition to sending an ICMP message, also send a TCP RST. This option + // requires the incoming packet to have been a valid TCP packet, as a TCP + // RST requires information from the current connection state to be + // well-formed. + ICMP_AND_TCP_RESET = 4, + }; + + class OutputInterface { + public: + virtual ~OutputInterface(); + + virtual void SendPacketToClient(QuicStringPiece packet) = 0; + virtual void SendPacketToNetwork(QuicStringPiece packet) = 0; + }; + + class StatsInterface { + public: + virtual ~StatsInterface(); + + virtual void OnPacketForwarded(Direction direction) = 0; + virtual void OnPacketDroppedSilently(Direction direction) = 0; + virtual void OnPacketDroppedWithIcmp(Direction direction) = 0; + virtual void OnPacketDroppedWithTcpReset(Direction direction) = 0; + virtual void OnPacketDeferred(Direction direction) = 0; + }; + + // Allows to implement a custom packet filter on top of the filtering done by + // the packet processor itself. + class Filter { + public: + virtual ~Filter(); + // The main interface function. The following arguments are supplied: + // - |direction|, to indicate direction of the packet. + // - |full_packet|, which includes the IPv6 header and possibly the IPv6 + // options that were understood by the processor. + // - |payload|, the contents of the IPv6 packet, i.e. a TCP, a UDP or an + // ICMP packet. + // - |icmp_header|, an output argument which allows the filter to specify + // the ICMP message with which the packet is to be rejected. + // The method is called only on packets which were already verified as valid + // IPv6 packets. + // + // The implementer of this method has four options to return: + // - OK will cause the filter to pass the packet through + // - SILENT_DROP will cause the filter to drop the packet silently + // - ICMP will cause the filter to drop the packet and send an ICMP + // response. + // - DEFER will cause the packet to be not forwarded; the filter is + // responsible for sending (or not sending) it later using |output|. + // + // Note that |output| should not be used except in the DEFER case, as the + // processor will perform the necessary writes itself. + virtual ProcessingResult FilterPacket(Direction direction, + QuicStringPiece full_packet, + QuicStringPiece payload, + icmp6_hdr* icmp_header, + OutputInterface* output); + + protected: + // Helper methods that allow to easily extract information that is required + // for filtering from the |ipv6_header| argument. All of those assume that + // the header is of valid size, which is true for everything passed into + // FilterPacket(). + inline uint8_t TransportProtocolFromHeader(QuicStringPiece ipv6_header) { + return ipv6_header[6]; + } + inline QuicIpAddress SourceIpFromHeader(QuicStringPiece ipv6_header) { + QuicIpAddress address; + address.FromPackedString(&ipv6_header[8], + QuicIpAddress::kIPv6AddressSize); + return address; + } + inline QuicIpAddress DestinationIpFromHeader(QuicStringPiece ipv6_header) { + QuicIpAddress address; + address.FromPackedString(&ipv6_header[24], + QuicIpAddress::kIPv6AddressSize); + return address; + } + }; + + // |self_ip| is the IP address from which the processor will originate ICMP + // messages. |client_ip| is the expected IP address of the client, used for + // packet validation. + // + // |output| and |stats| are the visitor interfaces used by the processor. + // |output| gets notified whenever the processor decides to send a packet, and + // |stats| gets notified about any decisions that processor makes, without a + // reference to which packet that decision was made about. + QbonePacketProcessor(QuicIpAddress self_ip, + QuicIpAddress client_ip, + size_t client_ip_subnet_length, + OutputInterface* output, + StatsInterface* stats); + QbonePacketProcessor(const QbonePacketProcessor&) = delete; + QbonePacketProcessor& operator=(const QbonePacketProcessor&) = delete; + + // Accepts an IPv6 packet and handles it accordingly by either forwarding it, + // replying with an ICMP packet or silently dropping it. |packet| will be + // modified in the process, by having the TTL field decreased. + void ProcessPacket(string* packet, Direction direction); + + void set_filter(std::unique_ptr<Filter> filter) { + filter_ = std::move(filter); + } + + void set_client_ip(QuicIpAddress client_ip) { client_ip_ = client_ip; } + void set_client_ip_subnet_length(size_t client_ip_subnet_length) { + client_ip_subnet_length_ = client_ip_subnet_length; + } + + static const QuicIpAddress kInvalidIpAddress; + + protected: + // Processes the header and returns what should be done with the packet. + // After that, calls an external packet filter if registered. TTL of the + // packet may be decreased in the process. + ProcessingResult ProcessIPv6HeaderAndFilter(string* packet, + Direction direction, + uint8_t* transport_protocol, + char** transport_data, + icmp6_hdr* icmp_header); + + void SendIcmpResponse(icmp6_hdr* icmp_header, + QuicStringPiece original_packet, + Direction original_direction); + + void SendTcpReset(QuicStringPiece original_packet, + Direction original_direction); + + inline bool IsValid() const { return client_ip_ != kInvalidIpAddress; } + + // IP address of the server. Used to send ICMP messages. + in6_addr self_ip_; + // IP address range of the VPN client. + QuicIpAddress client_ip_; + size_t client_ip_subnet_length_; + + OutputInterface* output_; + StatsInterface* stats_; + std::unique_ptr<Filter> filter_; + + private: + // Performs basic sanity and permission checks on the packet, and decreases + // the TTL. + ProcessingResult ProcessIPv6Header(string* packet, + Direction direction, + uint8_t* transport_protocol, + char** transport_data, + icmp6_hdr* icmp_header); + + void SendResponse(Direction original_direction, QuicStringPiece packet); +}; + +} // namespace quic +#endif // QUICHE_QUIC_QBONE_QBONE_PACKET_PROCESSOR_H_ diff --git a/chromium/net/third_party/quiche/src/quic/qbone/qbone_packet_processor_test.cc b/chromium/net/third_party/quiche/src/quic/qbone/qbone_packet_processor_test.cc new file mode 100644 index 00000000000..256f5b0f142 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/qbone_packet_processor_test.cc @@ -0,0 +1,283 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/third_party/quiche/src/quic/qbone/qbone_packet_processor.h" + +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/qbone/qbone_packet_processor_test_tools.h" + +namespace quic { +namespace { + +using Direction = QbonePacketProcessor::Direction; +using ProcessingResult = QbonePacketProcessor::ProcessingResult; +using OutputInterface = QbonePacketProcessor::OutputInterface; +using ::testing::_; +using ::testing::Return; + +// clang-format off +static const char kReferenceClientPacketData[] = { + // IPv6 with zero TOS and flow label. + 0x60, 0x00, 0x00, 0x00, + // Payload size is 8 bytes. + 0x00, 0x08, + // Next header is UDP + 17, + // TTL is 50. + 50, + // IP address of the sender is fd00:0:0:1::1 + 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + // IP address of the receiver is fd00:0:0:5::1 + 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + // Source port 12345 + 0x30, 0x39, + // Destination port 443 + 0x01, 0xbb, + // UDP content length is zero + 0x00, 0x00, + // Checksum is not actually checked in any of the tests, so we leave it as + // zero + 0x00, 0x00, +}; + +static const char kReferenceNetworkPacketData[] = { + // IPv6 with zero TOS and flow label. + 0x60, 0x00, 0x00, 0x00, + // Payload size is 8 bytes. + 0x00, 0x08, + // Next header is UDP + 17, + // TTL is 50. + 50, + // IP address of the sender is fd00:0:0:5::1 + 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + // IP address of the receiver is fd00:0:0:1::1 + 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + // Source port 443 + 0x01, 0xbb, + // Destination port 12345 + 0x30, 0x39, + // UDP content length is zero + 0x00, 0x00, + // Checksum is not actually checked in any of the tests, so we leave it as + // zero + 0x00, 0x00, +}; + +static const char kReferenceClientSubnetPacketData[] = { + // IPv6 with zero TOS and flow label. + 0x60, 0x00, 0x00, 0x00, + // Payload size is 8 bytes. + 0x00, 0x08, + // Next header is UDP + 17, + // TTL is 50. + 50, + // IP address of the sender is fd00:0:0:2::1, which is within the /62 of the + // client. + 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + // IP address of the receiver is fd00:0:0:5::1 + 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + // Source port 12345 + 0x30, 0x39, + // Destination port 443 + 0x01, 0xbb, + // UDP content length is zero + 0x00, 0x00, + // Checksum is not actually checked in any of the tests, so we leave it as + // zero + 0x00, 0x00, +}; + +// clang-format on + +static const QuicStringPiece kReferenceClientPacket( + kReferenceClientPacketData, + arraysize(kReferenceClientPacketData)); + +static const QuicStringPiece kReferenceNetworkPacket( + kReferenceNetworkPacketData, + arraysize(kReferenceNetworkPacketData)); + +static const QuicStringPiece kReferenceClientSubnetPacket( + kReferenceClientSubnetPacketData, + arraysize(kReferenceClientSubnetPacketData)); + +MATCHER_P(IsIcmpMessage, + icmp_type, + "Checks whether the argument is an ICMP message of supplied type") { + if (arg.size() < kTotalICMPv6HeaderSize) { + return false; + } + + return arg[40] == icmp_type; +} + +class MockPacketFilter : public QbonePacketProcessor::Filter { + public: + MOCK_METHOD5(FilterPacket, + ProcessingResult(Direction, + QuicStringPiece, + QuicStringPiece, + icmp6_hdr*, + OutputInterface*)); +}; + +class QbonePacketProcessorTest : public QuicTest { + protected: + QbonePacketProcessorTest() { + CHECK(client_ip_.FromString("fd00:0:0:1::1")); + CHECK(self_ip_.FromString("fd00:0:0:4::1")); + CHECK(network_ip_.FromString("fd00:0:0:5::1")); + + processor_ = QuicMakeUnique<QbonePacketProcessor>( + self_ip_, client_ip_, /*client_ip_subnet_length=*/62, &output_, + &stats_); + } + + void SendPacketFromClient(QuicStringPiece packet) { + string packet_buffer(packet.data(), packet.size()); + processor_->ProcessPacket(&packet_buffer, Direction::FROM_CLIENT); + } + + void SendPacketFromNetwork(QuicStringPiece packet) { + string packet_buffer(packet.data(), packet.size()); + processor_->ProcessPacket(&packet_buffer, Direction::FROM_NETWORK); + } + + QuicIpAddress client_ip_; + QuicIpAddress self_ip_; + QuicIpAddress network_ip_; + + std::unique_ptr<QbonePacketProcessor> processor_; + testing::StrictMock<MockPacketProcessorOutput> output_; + testing::StrictMock<MockPacketProcessorStats> stats_; +}; + +TEST_F(QbonePacketProcessorTest, EmptyPacket) { + EXPECT_CALL(stats_, OnPacketDroppedSilently(Direction::FROM_CLIENT)); + SendPacketFromClient(""); + + EXPECT_CALL(stats_, OnPacketDroppedSilently(Direction::FROM_NETWORK)); + SendPacketFromNetwork(""); +} + +TEST_F(QbonePacketProcessorTest, RandomGarbage) { + EXPECT_CALL(stats_, OnPacketDroppedSilently(Direction::FROM_CLIENT)); + SendPacketFromClient(string(1280, 'a')); + + EXPECT_CALL(stats_, OnPacketDroppedSilently(Direction::FROM_NETWORK)); + SendPacketFromNetwork(string(1280, 'a')); +} + +TEST_F(QbonePacketProcessorTest, RandomGarbageWithCorrectLengthFields) { + string packet(40, 'a'); + packet[4] = 0; + packet[5] = 0; + + EXPECT_CALL(stats_, OnPacketDroppedWithIcmp(Direction::FROM_CLIENT)); + EXPECT_CALL(output_, SendPacketToClient(IsIcmpMessage(ICMP6_DST_UNREACH))); + SendPacketFromClient(packet); +} + +TEST_F(QbonePacketProcessorTest, GoodPacketFromClient) { + EXPECT_CALL(stats_, OnPacketForwarded(Direction::FROM_CLIENT)); + EXPECT_CALL(output_, SendPacketToNetwork(_)); + SendPacketFromClient(kReferenceClientPacket); +} + +TEST_F(QbonePacketProcessorTest, GoodPacketFromClientSubnet) { + EXPECT_CALL(stats_, OnPacketForwarded(Direction::FROM_CLIENT)); + EXPECT_CALL(output_, SendPacketToNetwork(_)); + SendPacketFromClient(kReferenceClientSubnetPacket); +} + +TEST_F(QbonePacketProcessorTest, GoodPacketFromNetwork) { + EXPECT_CALL(stats_, OnPacketForwarded(Direction::FROM_NETWORK)); + EXPECT_CALL(output_, SendPacketToClient(_)); + SendPacketFromNetwork(kReferenceNetworkPacket); +} + +TEST_F(QbonePacketProcessorTest, GoodPacketFromNetworkWrongDirection) { + EXPECT_CALL(stats_, OnPacketDroppedWithIcmp(Direction::FROM_CLIENT)); + EXPECT_CALL(output_, SendPacketToClient(IsIcmpMessage(ICMP6_DST_UNREACH))); + SendPacketFromClient(kReferenceNetworkPacket); +} + +TEST_F(QbonePacketProcessorTest, TtlExpired) { + string packet(kReferenceNetworkPacket); + packet[7] = 1; + + EXPECT_CALL(stats_, OnPacketDroppedWithIcmp(Direction::FROM_NETWORK)); + EXPECT_CALL(output_, SendPacketToNetwork(IsIcmpMessage(ICMP6_TIME_EXCEEDED))); + SendPacketFromNetwork(packet); +} + +TEST_F(QbonePacketProcessorTest, UnknownProtocol) { + string packet(kReferenceNetworkPacket); + packet[6] = IPPROTO_SCTP; + + EXPECT_CALL(stats_, OnPacketDroppedWithIcmp(Direction::FROM_NETWORK)); + EXPECT_CALL(output_, SendPacketToNetwork(IsIcmpMessage(ICMP6_PARAM_PROB))); + SendPacketFromNetwork(packet); +} + +TEST_F(QbonePacketProcessorTest, FilterFromClient) { + auto filter = QuicMakeUnique<MockPacketFilter>(); + EXPECT_CALL(*filter, FilterPacket(_, _, _, _, _)) + .WillRepeatedly(Return(ProcessingResult::SILENT_DROP)); + processor_->set_filter(std::move(filter)); + + EXPECT_CALL(stats_, OnPacketDroppedSilently(Direction::FROM_CLIENT)); + SendPacketFromClient(kReferenceClientPacket); +} + +class TestFilter : public QbonePacketProcessor::Filter { + public: + TestFilter(QuicIpAddress client_ip, QuicIpAddress network_ip) + : client_ip_(client_ip), network_ip_(network_ip) {} + ProcessingResult FilterPacket(Direction direction, + QuicStringPiece full_packet, + QuicStringPiece payload, + icmp6_hdr* icmp_header, + OutputInterface* output) override { + EXPECT_EQ(kIPv6HeaderSize, full_packet.size() - payload.size()); + EXPECT_EQ(IPPROTO_UDP, TransportProtocolFromHeader(full_packet)); + EXPECT_EQ(client_ip_, SourceIpFromHeader(full_packet)); + EXPECT_EQ(network_ip_, DestinationIpFromHeader(full_packet)); + + called_++; + return ProcessingResult::SILENT_DROP; + } + + int called() const { return called_; } + + private: + int called_ = 0; + + QuicIpAddress client_ip_; + QuicIpAddress network_ip_; +}; + +// Verify that the parameters are passed correctly into the filter, and that the +// helper functions of the filter class work. +TEST_F(QbonePacketProcessorTest, FilterHelperFunctions) { + auto filter_owned = QuicMakeUnique<TestFilter>(client_ip_, network_ip_); + TestFilter* filter = filter_owned.get(); + processor_->set_filter(std::move(filter_owned)); + + EXPECT_CALL(stats_, OnPacketDroppedSilently(Direction::FROM_CLIENT)); + SendPacketFromClient(kReferenceClientPacket); + ASSERT_EQ(1, filter->called()); +} + +} // namespace +} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/qbone/qbone_packet_processor_test_tools.cc b/chromium/net/third_party/quiche/src/quic/qbone/qbone_packet_processor_test_tools.cc new file mode 100644 index 00000000000..9f9b623be92 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/qbone_packet_processor_test_tools.cc @@ -0,0 +1,28 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/third_party/quiche/src/quic/qbone/qbone_packet_processor_test_tools.h" + +#include <netinet/ip6.h> + +namespace quic { + +string PrependIPv6HeaderForTest(const string& body, int hops) { + ip6_hdr header; + memset(&header, 0, sizeof(header)); + + header.ip6_vfc = 6 << 4; + header.ip6_plen = htons(body.size()); + header.ip6_nxt = IPPROTO_UDP; + header.ip6_hops = hops; + header.ip6_src = in6addr_loopback; + header.ip6_dst = in6addr_loopback; + + string packet(sizeof(header) + body.size(), '\0'); + memcpy(&packet[0], &header, sizeof(header)); + memcpy(&packet[sizeof(header)], body.data(), body.size()); + return packet; +} + +} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/qbone/qbone_packet_processor_test_tools.h b/chromium/net/third_party/quiche/src/quic/qbone/qbone_packet_processor_test_tools.h new file mode 100644 index 00000000000..646dc427c43 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/qbone_packet_processor_test_tools.h @@ -0,0 +1,37 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef QUICHE_QUIC_QBONE_QBONE_PACKET_PROCESSOR_TEST_TOOLS_H_ +#define QUICHE_QUIC_QBONE_QBONE_PACKET_PROCESSOR_TEST_TOOLS_H_ + +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/qbone/qbone_packet_processor.h" + +namespace quic { + +class MockPacketProcessorOutput : public QbonePacketProcessor::OutputInterface { + public: + MockPacketProcessorOutput() {} + + MOCK_METHOD1(SendPacketToClient, void(QuicStringPiece)); + MOCK_METHOD1(SendPacketToNetwork, void(QuicStringPiece)); +}; + +class MockPacketProcessorStats : public QbonePacketProcessor::StatsInterface { + public: + MockPacketProcessorStats() {} + + MOCK_METHOD1(OnPacketForwarded, void(QbonePacketProcessor::Direction)); + MOCK_METHOD1(OnPacketDroppedSilently, void(QbonePacketProcessor::Direction)); + MOCK_METHOD1(OnPacketDroppedWithIcmp, void(QbonePacketProcessor::Direction)); + MOCK_METHOD1(OnPacketDroppedWithTcpReset, + void(QbonePacketProcessor::Direction)); + MOCK_METHOD1(OnPacketDeferred, void(QbonePacketProcessor::Direction)); +}; + +string PrependIPv6HeaderForTest(const string& body, int hops); + +} // namespace quic + +#endif // QUICHE_QUIC_QBONE_QBONE_PACKET_PROCESSOR_TEST_TOOLS_H_ diff --git a/chromium/net/third_party/quiche/src/quic/qbone/qbone_packet_writer.h b/chromium/net/third_party/quiche/src/quic/qbone/qbone_packet_writer.h new file mode 100644 index 00000000000..1ed8a46fa04 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/qbone_packet_writer.h @@ -0,0 +1,24 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef QUICHE_QUIC_QBONE_QBONE_PACKET_WRITER_H_ +#define QUICHE_QUIC_QBONE_QBONE_PACKET_WRITER_H_ + +#include <cstring> + +namespace quic { + +// QbonePacketWriter expects only one function to be defined, +// WritePacketToNetwork, which is called when a packet is received via QUIC +// and should be sent out on the network. This is the complete packet, +// and not just a fragment. +class QbonePacketWriter { + public: + virtual ~QbonePacketWriter() {} + virtual void WritePacketToNetwork(const char* packet, size_t size) = 0; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_QBONE_QBONE_PACKET_WRITER_H_ diff --git a/chromium/net/third_party/quiche/src/quic/qbone/qbone_server_session.cc b/chromium/net/third_party/quiche/src/quic/qbone/qbone_server_session.cc new file mode 100644 index 00000000000..37cdd4cbe6f --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/qbone_server_session.cc @@ -0,0 +1,94 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/third_party/quiche/src/quic/qbone/qbone_server_session.h" + +#include "net/third_party/quiche/src/quic/core/quic_connection_id.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/qbone/qbone_constants.h" + +namespace quic { + +bool QboneCryptoServerStreamHelper::CanAcceptClientHello( + const CryptoHandshakeMessage& chlo, + const QuicSocketAddress& client_address, + const QuicSocketAddress& peer_address, + const QuicSocketAddress& self_address, + string* error_details) const { + absl::string_view alpn; + chlo.GetStringPiece(quic::kALPN, &alpn); + if (alpn != QboneConstants::kQboneAlpn) { + *error_details = "ALPN-indicated protocol is not qbone"; + return false; + } + return true; +} + +QboneServerSession::QboneServerSession( + const quic::ParsedQuicVersionVector& supported_versions, + QuicConnection* connection, + Visitor* owner, + const QuicConfig& config, + const QuicCryptoServerConfig* quic_crypto_server_config, + QuicCompressedCertsCache* compressed_certs_cache, + QbonePacketWriter* writer, + QuicIpAddress self_ip, + QuicIpAddress client_ip, + size_t client_ip_subnet_length, + QboneServerControlStream::Handler* handler) + : QboneSessionBase(connection, owner, config, supported_versions, writer), + processor_(self_ip, client_ip, client_ip_subnet_length, this, this), + quic_crypto_server_config_(quic_crypto_server_config), + compressed_certs_cache_(compressed_certs_cache), + handler_(handler) {} + +QboneServerSession::~QboneServerSession() {} + +std::unique_ptr<QuicCryptoStream> QboneServerSession::CreateCryptoStream() { + return QuicMakeUnique<QuicCryptoServerStream>(quic_crypto_server_config_, + compressed_certs_cache_, this, + &stream_helper_); +} + +void QboneServerSession::Initialize() { + QboneSessionBase::Initialize(); + // Register the reserved control stream. + auto control_stream = + QuicMakeUnique<QboneServerControlStream>(this, handler_); + control_stream_ = control_stream.get(); + RegisterStaticStream(std::move(control_stream)); +} + +bool QboneServerSession::SendClientRequest(const QboneClientRequest& request) { + if (!control_stream_) { + QUIC_BUG << "Cannot send client request before control stream is created."; + return false; + } + return control_stream_->SendRequest(request); +} + +void QboneServerSession::ProcessPacketFromNetwork(QuicStringPiece packet) { + string buffer = string(packet); + processor_.ProcessPacket(&buffer, + QbonePacketProcessor::Direction::FROM_NETWORK); +} + +void QboneServerSession::ProcessPacketFromPeer(QuicStringPiece packet) { + string buffer = string(packet); + processor_.ProcessPacket(&buffer, + QbonePacketProcessor::Direction::FROM_CLIENT); +} + +void QboneServerSession::SendPacketToClient(QuicStringPiece packet) { + SendPacketToPeer(packet); +} + +void QboneServerSession::SendPacketToNetwork(QuicStringPiece packet) { + DCHECK(writer_ != nullptr); + writer_->WritePacketToNetwork(packet.data(), packet.size()); +} + +} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/qbone/qbone_server_session.h b/chromium/net/third_party/quiche/src/quic/qbone/qbone_server_session.h new file mode 100644 index 00000000000..9536f872de5 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/qbone_server_session.h @@ -0,0 +1,92 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef QUICHE_QUIC_QBONE_QBONE_SERVER_SESSION_H_ +#define QUICHE_QUIC_QBONE_QBONE_SERVER_SESSION_H_ + +#include "net/third_party/quiche/src/quic/core/quic_crypto_server_stream.h" +#include "net/third_party/quiche/src/quic/core/quic_crypto_stream.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/qbone/qbone_control.pb.h" +#include "net/third_party/quiche/src/quic/qbone/qbone_control_stream.h" +#include "net/third_party/quiche/src/quic/qbone/qbone_packet_processor.h" +#include "net/third_party/quiche/src/quic/qbone/qbone_packet_writer.h" +#include "net/third_party/quiche/src/quic/qbone/qbone_session_base.h" + +namespace quic { + +// A helper class is used by the QuicCryptoServerStream. +class QboneCryptoServerStreamHelper : public QuicCryptoServerStream::Helper { + public: + // This will look for the qbone alpn. + bool CanAcceptClientHello(const CryptoHandshakeMessage& chlo, + const QuicSocketAddress& client_address, + const QuicSocketAddress& peer_address, + const QuicSocketAddress& self_address, + string* error_details) const override; +}; + +class QUIC_EXPORT_PRIVATE QboneServerSession + : public QboneSessionBase, + public QbonePacketProcessor::OutputInterface, + public QbonePacketProcessor::StatsInterface { + public: + QboneServerSession(const quic::ParsedQuicVersionVector& supported_versions, + QuicConnection* connection, + Visitor* owner, + const QuicConfig& config, + const QuicCryptoServerConfig* quic_crypto_server_config, + QuicCompressedCertsCache* compressed_certs_cache, + QbonePacketWriter* writer, + QuicIpAddress self_ip, + QuicIpAddress client_ip, + size_t client_ip_subnet_length, + QboneServerControlStream::Handler* handler); + QboneServerSession(const QboneServerSession&) = delete; + QboneServerSession& operator=(const QboneServerSession&) = delete; + ~QboneServerSession() override; + + void Initialize() override; + + virtual bool SendClientRequest(const QboneClientRequest& request); + + void ProcessPacketFromNetwork(QuicStringPiece packet) override; + void ProcessPacketFromPeer(QuicStringPiece packet) override; + + // QbonePacketProcessor::OutputInterface implementation. + void SendPacketToClient(QuicStringPiece packet) override; + void SendPacketToNetwork(QuicStringPiece packet) override; + + // QbonePacketProcessor::StatsInterface implementation. + void OnPacketForwarded(QbonePacketProcessor::Direction direction) override {} + void OnPacketDroppedSilently( + QbonePacketProcessor::Direction direction) override {} + void OnPacketDroppedWithIcmp( + QbonePacketProcessor::Direction direction) override {} + void OnPacketDroppedWithTcpReset( + QbonePacketProcessor::Direction direction) override {} + void OnPacketDeferred(QbonePacketProcessor::Direction direction) override {} + + protected: + // QboneSessionBase interface implementation. + std::unique_ptr<QuicCryptoStream> CreateCryptoStream() override; + // The packet processor. + QbonePacketProcessor processor_; + + private: + // Config for QUIC crypto server stream, used by the server. + const QuicCryptoServerConfig* quic_crypto_server_config_; + // Used by QUIC crypto server stream to track most recently compressed certs. + QuicCompressedCertsCache* compressed_certs_cache_; + // This helper is needed when create QuicCryptoServerStream. + QboneCryptoServerStreamHelper stream_helper_; + // Passed to the control stream. + QboneServerControlStream::Handler* handler_; + // The unowned control stream. + QboneServerControlStream* control_stream_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_QBONE_QBONE_SERVER_SESSION_H_ diff --git a/chromium/net/third_party/quiche/src/quic/qbone/qbone_session_base.cc b/chromium/net/third_party/quiche/src/quic/qbone/qbone_session_base.cc new file mode 100644 index 00000000000..d88197540e7 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/qbone_session_base.cc @@ -0,0 +1,150 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/third_party/quiche/src/quic/qbone/qbone_session_base.h" + +#include "net/third_party/quiche/src/quic/core/quic_data_reader.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_exported_stats.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/qbone/qbone_constants.h" + +namespace quic { + +#define ENDPOINT \ + (perspective() == Perspective::IS_SERVER ? "Server: " : "Client: ") + +QboneSessionBase::QboneSessionBase( + QuicConnection* connection, + Visitor* owner, + const QuicConfig& config, + const ParsedQuicVersionVector& supported_versions, + QbonePacketWriter* writer) + : QuicSession(connection, + owner, + config, + supported_versions, + /*num_expected_unidirectional_static_streams = */ 0) { + set_writer(writer); + const uint32_t max_streams = + (std::numeric_limits<uint32_t>::max() / kMaxAvailableStreamsMultiplier) - + 1; + this->config()->SetMaxIncomingBidirectionalStreamsToSend(max_streams); + if (VersionHasIetfQuicFrames(transport_version())) { + ConfigureMaxIncomingDynamicStreamsToSend(max_streams); + } +} + +QboneSessionBase::~QboneSessionBase() { + // Clear out the streams before leaving this destructor to avoid calling + // QuicSession::UnregisterStreamPriority + stream_map().clear(); + closed_streams()->clear(); +} + +void QboneSessionBase::Initialize() { + crypto_stream_ = CreateCryptoStream(); + QuicSession::Initialize(); +} + +const QuicCryptoStream* QboneSessionBase::GetCryptoStream() const { + return crypto_stream_.get(); +} + +QuicCryptoStream* QboneSessionBase::GetMutableCryptoStream() { + return crypto_stream_.get(); +} + +QuicStream* QboneSessionBase::CreateOutgoingStream() { + return ActivateDataStream( + CreateDataStream(GetNextOutgoingUnidirectionalStreamId())); +} + +void QboneSessionBase::CloseStream(QuicStreamId stream_id) { + if (IsClosedStream(stream_id)) { + // When CloseStream has been called recursively (via + // QuicStream::OnClose), the stream is already closed so return. + return; + } + QuicSession::CloseStream(stream_id); +} + +void QboneSessionBase::OnStreamFrame(const QuicStreamFrame& frame) { + if (frame.offset == 0 && frame.fin && frame.data_length > 0) { + ++num_ephemeral_packets_; + ProcessPacketFromPeer( + QuicStringPiece(frame.data_buffer, frame.data_length)); + flow_controller()->AddBytesConsumed(frame.data_length); + return; + } + QuicSession::OnStreamFrame(frame); +} + +QuicStream* QboneSessionBase::CreateIncomingStream(QuicStreamId id) { + return ActivateDataStream(CreateDataStream(id)); +} + +QuicStream* QboneSessionBase::CreateIncomingStream(PendingStream* /*pending*/) { + QUIC_NOTREACHED(); + return nullptr; +} + +bool QboneSessionBase::ShouldKeepConnectionAlive() const { + // Qbone connections stay alive until they're explicitly closed. + return true; +} + +std::unique_ptr<QuicStream> QboneSessionBase::CreateDataStream( + QuicStreamId id) { + if (crypto_stream_ == nullptr || !crypto_stream_->encryption_established()) { + // Encryption not active so no stream created + return nullptr; + } + + if (IsIncomingStream(id)) { + ++num_streamed_packets_; + return QuicMakeUnique<QboneReadOnlyStream>(id, this); + } + + return QuicMakeUnique<QboneWriteOnlyStream>(id, this); +} + +QuicStream* QboneSessionBase::ActivateDataStream( + std::unique_ptr<QuicStream> stream) { + // Transfer ownership of the data stream to the session via ActivateStream(). + QuicStream* raw = stream.get(); + if (stream) { + // Make QuicSession take ownership of the stream. + ActivateStream(std::move(stream)); + } + return raw; +} + +void QboneSessionBase::SendPacketToPeer(QuicStringPiece packet) { + // Qbone streams are ephemeral. + QuicStream* stream = CreateOutgoingStream(); + if (!stream) { + QUIC_BUG << "Failed to create an outgoing QBONE stream."; + return; + } + + QboneWriteOnlyStream* qbone_stream = + static_cast<QboneWriteOnlyStream*>(stream); + qbone_stream->WritePacketToQuicStream(packet); +} + +uint64_t QboneSessionBase::GetNumEphemeralPackets() const { + return num_ephemeral_packets_; +} + +uint64_t QboneSessionBase::GetNumStreamedPackets() const { + return num_streamed_packets_; +} + +void QboneSessionBase::set_writer(QbonePacketWriter* writer) { + writer_ = writer; + testing::testvalue::Adjust("quic_QbonePacketWriter", &writer_); +} + +} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/qbone/qbone_session_base.h b/chromium/net/third_party/quiche/src/quic/qbone/qbone_session_base.h new file mode 100644 index 00000000000..5bfc9be440d --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/qbone_session_base.h @@ -0,0 +1,93 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef QUICHE_QUIC_QBONE_QBONE_SESSION_BASE_H_ +#define QUICHE_QUIC_QBONE_QBONE_SESSION_BASE_H_ + +#include "net/third_party/quiche/src/quic/core/quic_crypto_server_stream.h" +#include "net/third_party/quiche/src/quic/core/quic_crypto_stream.h" +#include "net/third_party/quiche/src/quic/core/quic_error_codes.h" +#include "net/third_party/quiche/src/quic/core/quic_session.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/qbone/qbone_packet_writer.h" +#include "net/third_party/quiche/src/quic/qbone/qbone_stream.h" + +namespace quic { + +class QUIC_EXPORT_PRIVATE QboneSessionBase : public QuicSession { + public: + QboneSessionBase(QuicConnection* connection, + Visitor* owner, + const QuicConfig& config, + const ParsedQuicVersionVector& supported_versions, + QbonePacketWriter* writer); + QboneSessionBase(const QboneSessionBase&) = delete; + QboneSessionBase& operator=(const QboneSessionBase&) = delete; + ~QboneSessionBase() override; + + // Overrides from QuicSession. + // This will ensure that the crypto session is created. + void Initialize() override; + // This will ensure that we keep track of stream ids that can be + // write blocked. + void CloseStream(QuicStreamId stream_id) override; + // This will check if the packet is wholly contained. + void OnStreamFrame(const QuicStreamFrame& frame) override; + + virtual void ProcessPacketFromNetwork(QuicStringPiece packet) = 0; + virtual void ProcessPacketFromPeer(QuicStringPiece packet) = 0; + + // Returns the number of qbone network packets that were received + // that fit into a single QuicStreamFrame and elided the creation of + // a QboneReadOnlyStream. + uint64_t GetNumEphemeralPackets() const; + + // Returns the number of qbone network packets that were via + // multiple packets, requiring the creation of a QboneReadOnlyStream. + uint64_t GetNumStreamedPackets() const; + + void set_writer(QbonePacketWriter* writer); + + protected: + virtual std::unique_ptr<QuicCryptoStream> CreateCryptoStream() = 0; + + // QuicSession interface implementation. + QuicCryptoStream* GetMutableCryptoStream() override; + const QuicCryptoStream* GetCryptoStream() const override; + QuicStream* CreateIncomingStream(QuicStreamId id) override; + QuicStream* CreateIncomingStream(PendingStream* pending) override; + bool ShouldKeepConnectionAlive() const override; + + bool MaybeIncreaseLargestPeerStreamId(const QuicStreamId stream_id) override { + return true; + } + + QuicStream* CreateOutgoingStream(); + std::unique_ptr<QuicStream> CreateDataStream(QuicStreamId id); + // Activates a QuicStream. The session takes ownership of the stream, but + // returns an unowned pointer to the stream for convenience. + QuicStream* ActivateDataStream(std::unique_ptr<QuicStream> stream); + + // Accepts a given packet from the network and writes it out + // to the QUIC stream. This will create an ephemeral stream per + // packet. This function will return true if a stream was created + // and the packet sent. It will return false if the stream could not + // be created. + void SendPacketToPeer(QuicStringPiece packet); + + QbonePacketWriter* writer_; + + private: + // Used for the crypto handshake. + std::unique_ptr<QuicCryptoStream> crypto_stream_; + + uint64_t num_ephemeral_packets_ = 0; + uint64_t num_streamed_packets_ = 0; + QuicUnorderedSet<QuicStreamId> reliable_streams_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_QBONE_QBONE_SESSION_BASE_H_ diff --git a/chromium/net/third_party/quiche/src/quic/qbone/qbone_session_test.cc b/chromium/net/third_party/quiche/src/quic/qbone/qbone_session_test.cc new file mode 100644 index 00000000000..be598a927ff --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/qbone_session_test.cc @@ -0,0 +1,520 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/third_party/quiche/src/quic/core/proto/crypto_server_config_proto.h" +#include "net/third_party/quiche/src/quic/core/quic_alarm_factory.h" +#include "net/third_party/quiche/src/quic/core/quic_epoll_alarm_factory.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_port_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test_loopback.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h" +#include "net/third_party/quiche/src/quic/qbone/qbone_client_session.h" +#include "net/third_party/quiche/src/quic/qbone/qbone_constants.h" +#include "net/third_party/quiche/src/quic/qbone/qbone_control_placeholder.pb.h" +#include "net/third_party/quiche/src/quic/qbone/qbone_packet_processor_test_tools.h" +#include "net/third_party/quiche/src/quic/qbone/qbone_server_session.h" +#include "net/third_party/quiche/src/quic/test_tools/mock_clock.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_session_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" + +namespace quic { +namespace test { +namespace { + +using ::testing::_; +using ::testing::Contains; +using ::testing::ElementsAre; +using ::testing::Eq; +using ::testing::Invoke; +using ::testing::NiceMock; +using ::testing::Not; + +string TestPacketIn(const string& body) { + return PrependIPv6HeaderForTest(body, 5); +} + +string TestPacketOut(const string& body) { + return PrependIPv6HeaderForTest(body, 4); +} + +// Used by QuicCryptoServerConfig to provide server credentials, returning a +// canned response equal to |success|. +class FakeProofSource : public ProofSource { + public: + explicit FakeProofSource(bool success) : success_(success) {} + + // ProofSource override. + void GetProof(const QuicSocketAddress& server_address, + const string& hostname, + const string& server_config, + QuicTransportVersion transport_version, + QuicStringPiece chlo_hash, + std::unique_ptr<Callback> callback) override { + QuicReferenceCountedPointer<ProofSource::Chain> chain = + GetCertChain(server_address, hostname); + QuicCryptoProof proof; + if (success_) { + proof.signature = "Signature"; + proof.leaf_cert_scts = "Time"; + } + callback->Run(success_, chain, proof, nullptr /* details */); + } + + QuicReferenceCountedPointer<Chain> GetCertChain( + const QuicSocketAddress& server_address, + const string& hostname) override { + if (!success_) { + return QuicReferenceCountedPointer<Chain>(); + } + std::vector<string> certs; + certs.push_back("Required to establish handshake"); + return QuicReferenceCountedPointer<ProofSource::Chain>( + new ProofSource::Chain(certs)); + } + + void ComputeTlsSignature( + const QuicSocketAddress& server_address, + const string& hostname, + uint16_t signature_algorithm, + QuicStringPiece in, + std::unique_ptr<SignatureCallback> callback) override { + callback->Run(true, "Signature"); + } + + private: + // Whether or not obtaining proof source succeeds. + bool success_; +}; + +// Used by QuicCryptoClientConfig to verify server credentials, returning a +// canned response of QUIC_SUCCESS if |success| is true. +class FakeProofVerifier : public ProofVerifier { + public: + explicit FakeProofVerifier(bool success) : success_(success) {} + + // ProofVerifier override + QuicAsyncStatus VerifyProof( + const string& hostname, + const uint16_t port, + const string& server_config, + QuicTransportVersion transport_version, + QuicStringPiece chlo_hash, + const std::vector<string>& certs, + const string& cert_sct, + const string& signature, + const ProofVerifyContext* context, + string* error_details, + std::unique_ptr<ProofVerifyDetails>* verify_details, + std::unique_ptr<ProofVerifierCallback> callback) override { + return success_ ? QUIC_SUCCESS : QUIC_FAILURE; + } + + QuicAsyncStatus VerifyCertChain( + const string& hostname, + const std::vector<string>& certs, + const std::string& ocsp_response, + const std::string& cert_sct, + const ProofVerifyContext* context, + string* error_details, + std::unique_ptr<ProofVerifyDetails>* details, + std::unique_ptr<ProofVerifierCallback> callback) override { + return success_ ? QUIC_SUCCESS : QUIC_FAILURE; + } + + std::unique_ptr<ProofVerifyContext> CreateDefaultContext() override { + return nullptr; + } + + private: + // Whether or not proof verification succeeds. + bool success_; +}; + +class DataSavingQbonePacketWriter : public QbonePacketWriter { + public: + void WritePacketToNetwork(const char* packet, size_t size) override { + data_.push_back(string(packet, size)); + } + + const std::vector<string>& data() { return data_; } + + private: + std::vector<string> data_; +}; + +template <class T> +class DataSavingQboneControlHandler : public QboneControlHandler<T> { + public: + void OnControlRequest(const T& request) override { data_.push_back(request); } + + void OnControlError() override { error_ = true; } + + const std::vector<T>& data() { return data_; } + bool error() { return error_; } + + private: + std::vector<T> data_; + bool error_ = false; +}; + +// Single-threaded scheduled task runner based on a MockClock. +// +// Simulates asynchronous execution on a single thread by holding scheduled +// tasks until Run() is called. Performs no synchronization, assumes that +// Schedule() and Run() are called on the same thread. +class FakeTaskRunner { + public: + explicit FakeTaskRunner(MockQuicConnectionHelper* helper) + : tasks_([this](const TaskType& l, const TaskType& r) { + // Items at a later time should run after items at an earlier time. + // Priority queue comparisons should return true if l appears after r. + return l->time() > r->time(); + }), + helper_(helper) {} + + // Runs all tasks in time order. Executes tasks scheduled at + // the same in an arbitrary order. + void Run() { + while (!tasks_.empty()) { + tasks_.top()->Run(); + tasks_.pop(); + } + } + + private: + class InnerTask { + public: + InnerTask(std::function<void()> task, QuicTime time) + : task_(std::move(task)), time_(time) {} + + void Cancel() { cancelled_ = true; } + + void Run() { + if (!cancelled_) { + task_(); + } + } + + QuicTime time() const { return time_; } + + private: + bool cancelled_ = false; + std::function<void()> task_; + QuicTime time_; + }; + + public: + // Schedules a function to run immediately and advances the time. + void Schedule(std::function<void()> task) { + tasks_.push(std::shared_ptr<InnerTask>( + new InnerTask(std::move(task), helper_->GetClock()->Now()))); + helper_->AdvanceTime(QuicTime::Delta::FromMilliseconds(1)); + } + + private: + using TaskType = std::shared_ptr<InnerTask>; + std::priority_queue<TaskType, + std::vector<TaskType>, + std::function<bool(const TaskType&, const TaskType&)>> + tasks_; + MockQuicConnectionHelper* helper_; +}; + +class QboneSessionTest : public QuicTest { + public: + QboneSessionTest() : runner_(&helper_), compressed_certs_cache_(100) {} + + ~QboneSessionTest() override { + delete client_connection_; + delete server_connection_; + } + + const MockClock* GetClock() const { + return static_cast<const MockClock*>(helper_.GetClock()); + } + + // The parameters are used to control whether the handshake will success or + // not. + void CreateClientAndServerSessions(bool client_handshake_success = true, + bool server_handshake_success = true, + bool send_qbone_alpn = true) { + // Quic crashes if packets are sent at time 0, and the clock defaults to 0. + helper_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1000)); + alarm_factory_ = QuicMakeUnique<QuicEpollAlarmFactory>(&epoll_server_); + client_writer_ = QuicMakeUnique<DataSavingQbonePacketWriter>(); + server_writer_ = QuicMakeUnique<DataSavingQbonePacketWriter>(); + client_handler_ = + QuicMakeUnique<DataSavingQboneControlHandler<QboneClientRequest>>(); + server_handler_ = + QuicMakeUnique<DataSavingQboneControlHandler<QboneServerRequest>>(); + QuicSocketAddress server_address(TestLoopback(), + QuicPickServerPortForTestsOrDie()); + QuicSocketAddress client_address; + if (server_address.host().address_family() == IpAddressFamily::IP_V4) { + client_address = QuicSocketAddress(QuicIpAddress::Any4(), 0); + } else { + client_address = QuicSocketAddress(QuicIpAddress::Any6(), 0); + } + + { + client_connection_ = new QuicConnection( + TestConnectionId(), server_address, &helper_, alarm_factory_.get(), + new NiceMock<MockPacketWriter>(), true, Perspective::IS_CLIENT, + ParsedVersionOfIndex(AllSupportedVersions(), 0)); + client_connection_->SetSelfAddress(client_address); + QuicConfig config; + client_crypto_config_ = QuicMakeUnique<QuicCryptoClientConfig>( + QuicMakeUnique<FakeProofVerifier>(client_handshake_success)); + if (send_qbone_alpn) { + client_crypto_config_->set_alpn("qbone"); + } + client_peer_ = QuicMakeUnique<QboneClientSession>( + client_connection_, client_crypto_config_.get(), + /*owner=*/nullptr, config, + ParsedVersionOfIndex(AllSupportedVersions(), 0), + QuicServerId("test.example.com", 1234, false), client_writer_.get(), + client_handler_.get()); + } + + { + server_connection_ = new QuicConnection( + TestConnectionId(), client_address, &helper_, alarm_factory_.get(), + new NiceMock<MockPacketWriter>(), true, Perspective::IS_SERVER, + ParsedVersionOfIndex(AllSupportedVersions(), 0)); + server_connection_->SetSelfAddress(server_address); + QuicConfig config; + server_crypto_config_ = QuicMakeUnique<QuicCryptoServerConfig>( + "TESTING", QuicRandom::GetInstance(), + std::unique_ptr<FakeProofSource>( + new FakeProofSource(server_handshake_success)), + KeyExchangeSource::Default()); + QuicCryptoServerConfig::ConfigOptions options; + QuicServerConfigProtobuf primary_config = + server_crypto_config_->GenerateConfig(QuicRandom::GetInstance(), + GetClock(), options); + std::unique_ptr<CryptoHandshakeMessage> message( + server_crypto_config_->AddConfig(std::move(primary_config), + GetClock()->WallNow())); + + server_peer_ = QuicMakeUnique<QboneServerSession>( + AllSupportedVersions(), server_connection_, nullptr, config, + server_crypto_config_.get(), &compressed_certs_cache_, + server_writer_.get(), TestLoopback6(), TestLoopback6(), 64, + server_handler_.get()); + } + + // Hook everything up! + MockPacketWriter* client_writer = static_cast<MockPacketWriter*>( + QuicConnectionPeer::GetWriter(client_peer_->connection())); + ON_CALL(*client_writer, WritePacket(_, _, _, _, _)) + .WillByDefault(Invoke([this](const char* buffer, size_t buf_len, + const QuicIpAddress& self_address, + const QuicSocketAddress& peer_address, + PerPacketOptions* options) { + char* copy = new char[1024 * 1024]; + memcpy(copy, buffer, buf_len); + runner_.Schedule([this, copy, buf_len] { + QuicReceivedPacket packet(copy, buf_len, GetClock()->Now()); + server_peer_->ProcessUdpPacket(server_connection_->self_address(), + client_connection_->self_address(), + packet); + delete[] copy; + }); + return WriteResult(WRITE_STATUS_OK, buf_len); + })); + MockPacketWriter* server_writer = static_cast<MockPacketWriter*>( + QuicConnectionPeer::GetWriter(server_peer_->connection())); + ON_CALL(*server_writer, WritePacket(_, _, _, _, _)) + .WillByDefault(Invoke([this](const char* buffer, size_t buf_len, + const QuicIpAddress& self_address, + const QuicSocketAddress& peer_address, + PerPacketOptions* options) { + char* copy = new char[1024 * 1024]; + memcpy(copy, buffer, buf_len); + runner_.Schedule([this, copy, buf_len] { + QuicReceivedPacket packet(copy, buf_len, GetClock()->Now()); + client_peer_->ProcessUdpPacket(client_connection_->self_address(), + server_connection_->self_address(), + packet); + delete[] copy; + }); + return WriteResult(WRITE_STATUS_OK, buf_len); + })); + } + + void StartHandshake() { + server_peer_->Initialize(); + client_peer_->Initialize(); + runner_.Run(); + } + + // Test handshake establishment and sending/receiving of data for two + // directions. + void TestStreamConnection() { + ASSERT_TRUE(server_peer_->IsCryptoHandshakeConfirmed()); + ASSERT_TRUE(client_peer_->IsCryptoHandshakeConfirmed()); + ASSERT_TRUE(server_peer_->IsEncryptionEstablished()); + ASSERT_TRUE(client_peer_->IsEncryptionEstablished()); + + // Create an outgoing stream from the client and say hello. + QUIC_LOG(INFO) << "Sending client -> server"; + client_peer_->ProcessPacketFromNetwork(TestPacketIn("hello")); + client_peer_->ProcessPacketFromNetwork(TestPacketIn("world")); + runner_.Run(); + // The server should see the data, the client hasn't received + // anything yet. + EXPECT_THAT(server_writer_->data(), + ElementsAre(TestPacketOut("hello"), TestPacketOut("world"))); + EXPECT_TRUE(client_writer_->data().empty()); + EXPECT_EQ(0u, server_peer_->GetNumActiveStreams()); + EXPECT_EQ(0u, client_peer_->GetNumActiveStreams()); + + // Let's pretend some service responds. + QUIC_LOG(INFO) << "Sending server -> client"; + server_peer_->ProcessPacketFromNetwork(TestPacketIn("Hello Again")); + server_peer_->ProcessPacketFromNetwork(TestPacketIn("Again")); + runner_.Run(); + EXPECT_THAT(server_writer_->data(), + ElementsAre(TestPacketOut("hello"), TestPacketOut("world"))); + EXPECT_THAT( + client_writer_->data(), + ElementsAre(TestPacketOut("Hello Again"), TestPacketOut("Again"))); + EXPECT_EQ(0u, server_peer_->GetNumActiveStreams()); + EXPECT_EQ(0u, client_peer_->GetNumActiveStreams()); + + // Try to send long payloads that are larger than the QUIC MTU but + // smaller than the QBONE max size. + // This should trigger the non-ephemeral stream code path. + string long_data(QboneConstants::kMaxQbonePacketBytes - sizeof(ip6_hdr) - 1, + 'A'); + QUIC_LOG(INFO) << "Sending server -> client long data"; + server_peer_->ProcessPacketFromNetwork(TestPacketIn(long_data)); + runner_.Run(); + EXPECT_THAT(client_writer_->data(), Contains(TestPacketOut(long_data))); + EXPECT_THAT(server_writer_->data(), + Not(Contains(TestPacketOut(long_data)))); + EXPECT_EQ(0u, server_peer_->GetNumActiveStreams()); + EXPECT_EQ(0u, client_peer_->GetNumActiveStreams()); + + QUIC_LOG(INFO) << "Sending client -> server long data"; + client_peer_->ProcessPacketFromNetwork(TestPacketIn(long_data)); + runner_.Run(); + EXPECT_THAT(server_writer_->data(), Contains(TestPacketOut(long_data))); + EXPECT_THAT(client_peer_->GetNumSentClientHellos(), Eq(2)); + EXPECT_THAT(client_peer_->GetNumReceivedServerConfigUpdates(), Eq(0)); + EXPECT_THAT(client_peer_->GetNumEphemeralPackets(), Eq(2)); + EXPECT_THAT(client_peer_->GetNumStreamedPackets(), Eq(1)); + EXPECT_THAT(server_peer_->GetNumEphemeralPackets(), Eq(2)); + EXPECT_THAT(server_peer_->GetNumStreamedPackets(), Eq(1)); + + // All streams are ephemeral and should be gone. + EXPECT_EQ(0u, server_peer_->GetNumActiveStreams()); + EXPECT_EQ(0u, client_peer_->GetNumActiveStreams()); + } + + // Test that client and server are not connected after handshake failure. + void TestDisconnectAfterFailedHandshake() { + EXPECT_FALSE(client_peer_->IsEncryptionEstablished()); + EXPECT_FALSE(client_peer_->IsCryptoHandshakeConfirmed()); + + EXPECT_FALSE(server_peer_->IsEncryptionEstablished()); + EXPECT_FALSE(server_peer_->IsCryptoHandshakeConfirmed()); + } + + protected: + QuicEpollServer epoll_server_; + std::unique_ptr<QuicAlarmFactory> alarm_factory_; + FakeTaskRunner runner_; + MockQuicConnectionHelper helper_; + QuicConnection* client_connection_; + QuicConnection* server_connection_; + QuicCompressedCertsCache compressed_certs_cache_; + + std::unique_ptr<QuicCryptoClientConfig> client_crypto_config_; + std::unique_ptr<QuicCryptoServerConfig> server_crypto_config_; + std::unique_ptr<DataSavingQbonePacketWriter> client_writer_; + std::unique_ptr<DataSavingQbonePacketWriter> server_writer_; + std::unique_ptr<DataSavingQboneControlHandler<QboneClientRequest>> + client_handler_; + std::unique_ptr<DataSavingQboneControlHandler<QboneServerRequest>> + server_handler_; + + std::unique_ptr<QboneServerSession> server_peer_; + std::unique_ptr<QboneClientSession> client_peer_; +}; + +TEST_F(QboneSessionTest, StreamConnection) { + CreateClientAndServerSessions(); + StartHandshake(); + TestStreamConnection(); +} + +TEST_F(QboneSessionTest, ClientRejection) { + CreateClientAndServerSessions(false /*client_handshake_success*/, + true /*server_handshake_success*/, + true /*send_qbone_alpn*/); + StartHandshake(); + TestDisconnectAfterFailedHandshake(); +} + +TEST_F(QboneSessionTest, BadAlpn) { + CreateClientAndServerSessions(true /*client_handshake_success*/, + true /*server_handshake_success*/, + false /*send_qbone_alpn*/); + StartHandshake(); + TestDisconnectAfterFailedHandshake(); +} + +TEST_F(QboneSessionTest, ServerRejection) { + CreateClientAndServerSessions(true /*client_handshake_success*/, + false /*server_handshake_success*/, + true /*send_qbone_alpn*/); + StartHandshake(); + TestDisconnectAfterFailedHandshake(); +} + +// Test that data streams are not created before handshake. +TEST_F(QboneSessionTest, CannotCreateDataStreamBeforeHandshake) { + CreateClientAndServerSessions(); + EXPECT_QUIC_BUG(client_peer_->ProcessPacketFromNetwork(TestPacketIn("hello")), + "Failed to create an outgoing QBONE stream"); + EXPECT_QUIC_BUG(server_peer_->ProcessPacketFromNetwork(TestPacketIn("hello")), + "Failed to create an outgoing QBONE stream"); + EXPECT_EQ(0u, server_peer_->GetNumActiveStreams()); + EXPECT_EQ(0u, client_peer_->GetNumActiveStreams()); +} + +TEST_F(QboneSessionTest, ControlRequests) { + CreateClientAndServerSessions(); + StartHandshake(); + EXPECT_TRUE(client_handler_->data().empty()); + EXPECT_FALSE(client_handler_->error()); + EXPECT_TRUE(server_handler_->data().empty()); + EXPECT_FALSE(server_handler_->error()); + + QboneClientRequest client_request; + client_request.SetExtension(client_placeholder, "hello from the server"); + EXPECT_TRUE(server_peer_->SendClientRequest(client_request)); + runner_.Run(); + ASSERT_FALSE(client_handler_->data().empty()); + EXPECT_THAT(client_handler_->data()[0].GetExtension(client_placeholder), + Eq("hello from the server")); + EXPECT_FALSE(client_handler_->error()); + + QboneServerRequest server_request; + server_request.SetExtension(server_placeholder, "hello from the client"); + EXPECT_TRUE(client_peer_->SendServerRequest(server_request)); + runner_.Run(); + ASSERT_FALSE(server_handler_->data().empty()); + EXPECT_THAT(server_handler_->data()[0].GetExtension(server_placeholder), + Eq("hello from the client")); + EXPECT_FALSE(server_handler_->error()); +} + +} // namespace +} // namespace test +} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/qbone/qbone_stream.cc b/chromium/net/third_party/quiche/src/quic/qbone/qbone_stream.cc new file mode 100644 index 00000000000..b7ac0078561 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/qbone_stream.cc @@ -0,0 +1,61 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/third_party/quiche/src/quic/qbone/qbone_stream.h" + +#include "net/third_party/quiche/src/quic/core/quic_data_reader.h" +#include "net/third_party/quiche/src/quic/core/quic_data_writer.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h" +#include "net/third_party/quiche/src/quic/qbone/qbone_constants.h" +#include "net/third_party/quiche/src/quic/qbone/qbone_session_base.h" + +namespace quic { + +QboneWriteOnlyStream::QboneWriteOnlyStream(QuicStreamId id, + QuicSession* session) + : QuicStream(id, session, /*is_static=*/false, WRITE_UNIDIRECTIONAL) { + // QBONE uses a LIFO queue to try to always make progress. An individual + // packet may persist for upto to 10 seconds in memory. + MaybeSetTtl(QuicTime::Delta::FromSeconds(10)); +} + +void QboneWriteOnlyStream::WritePacketToQuicStream(QuicStringPiece packet) { + // Streams are one way and ephemeral. This function should only be + // called once. + WriteOrBufferData(packet, /* fin= */ true, nullptr); +} + +QboneReadOnlyStream::QboneReadOnlyStream(QuicStreamId id, + QboneSessionBase* session) + : QuicStream(id, + session, + /*is_static=*/false, + READ_UNIDIRECTIONAL), + session_(session) { + // QBONE uses a LIFO queue to try to always make progress. An individual + // packet may persist for upto to 10 seconds in memory. + MaybeSetTtl(QuicTime::Delta::FromSeconds(10)); +} + +QboneReadOnlyStream::~QboneReadOnlyStream() {} + +void QboneReadOnlyStream::OnDataAvailable() { + // Read in data and buffer it, attempt to frame to see if there's a packet. + sequencer()->Read(&buffer_); + if (sequencer()->IsClosed()) { + session_->ProcessPacketFromPeer(buffer_); + OnFinRead(); + return; + } + if (buffer_.size() > QboneConstants::kMaxQbonePacketBytes) { + if (!rst_sent()) { + Reset(QUIC_BAD_APPLICATION_PAYLOAD); + } + StopReading(); + } +} + +} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/qbone/qbone_stream.h b/chromium/net/third_party/quiche/src/quic/qbone/qbone_stream.h new file mode 100644 index 00000000000..73680051643 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/qbone_stream.h @@ -0,0 +1,55 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef QUICHE_QUIC_QBONE_QBONE_STREAM_H_ +#define QUICHE_QUIC_QBONE_QBONE_STREAM_H_ + +#include "net/third_party/quiche/src/quic/core/quic_session.h" +#include "net/third_party/quiche/src/quic/core/quic_stream.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" + +namespace quic { + +class QboneSessionBase; + +// QboneWriteOnlyStream is responsible for sending data for a single +// packet to the other side. +// Note that the stream will be created HalfClosed (reads will be closed). +class QUIC_EXPORT_PRIVATE QboneWriteOnlyStream : public QuicStream { + public: + QboneWriteOnlyStream(QuicStreamId id, QuicSession* session); + + // QuicStream implementation. Qbone writers are ephemeral and don't + // read any data. + void OnDataAvailable() override {} + + // Write a network packet over the quic stream. + void WritePacketToQuicStream(QuicStringPiece packet); +}; + +// QboneReadOnlyStream will be used if we find an incoming stream that +// isn't fully contained. It will buffer the data when available and +// attempt to parse it as a packet to send to the network when a FIN +// is found. +// Note that the stream will be created HalfClosed (writes will be closed). +class QUIC_EXPORT_PRIVATE QboneReadOnlyStream : public QuicStream { + public: + QboneReadOnlyStream(QuicStreamId id, QboneSessionBase* session); + + ~QboneReadOnlyStream() override; + + // QuicStream overrides. + // OnDataAvailable is called when there is data in the quic stream buffer. + // This will copy the buffer locally and attempt to parse it to write out + // packets to the network. + void OnDataAvailable() override; + + private: + string buffer_; + QboneSessionBase* session_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_QBONE_QBONE_STREAM_H_ diff --git a/chromium/net/third_party/quiche/src/quic/qbone/qbone_stream_test.cc b/chromium/net/third_party/quiche/src/quic/qbone/qbone_stream_test.cc new file mode 100644 index 00000000000..1920aa55ee6 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/qbone_stream_test.cc @@ -0,0 +1,249 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/third_party/quiche/src/quic/qbone/qbone_stream.h" + +#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h" +#include "net/third_party/quiche/src/quic/core/quic_session.h" +#include "net/third_party/quiche/src/quic/core/quic_simple_buffer_allocator.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test_loopback.h" +#include "net/third_party/quiche/src/quic/qbone/qbone_constants.h" +#include "net/third_party/quiche/src/quic/qbone/qbone_session_base.h" +#include "net/third_party/quiche/src/quic/test_tools/mock_clock.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" +#include "net/third_party/quiche/src/spdy/core/spdy_protocol.h" + +namespace quic { + +namespace { + +using ::testing::_; +using ::testing::StrictMock; + +// MockQuicSession that does not create streams and writes data from +// QuicStream to a string. +class MockQuicSession : public QboneSessionBase { + public: + MockQuicSession(QuicConnection* connection, const QuicConfig& config) + : QboneSessionBase(connection, + nullptr /*visitor*/, + config, + CurrentSupportedVersions(), + nullptr /*writer*/) {} + + ~MockQuicSession() override {} + + // Writes outgoing data from QuicStream to a string. + QuicConsumedData WritevData(QuicStream* stream, + QuicStreamId id, + size_t write_length, + QuicStreamOffset offset, + StreamSendingState state) override { + if (!writable_) { + return QuicConsumedData(0, false); + } + + return QuicConsumedData(write_length, state != StreamSendingState::NO_FIN); + } + + QboneReadOnlyStream* CreateIncomingStream(QuicStreamId id) override { + return nullptr; + } + + const QuicCryptoStream* GetCryptoStream() const override { return nullptr; } + QuicCryptoStream* GetMutableCryptoStream() override { return nullptr; } + + // Called by QuicStream when they want to close stream. + MOCK_METHOD3(SendRstStream, + void(QuicStreamId, QuicRstStreamErrorCode, QuicStreamOffset)); + + // Sets whether data is written to buffer, or else if this is write blocked. + void set_writable(bool writable) { writable_ = writable; } + + // Tracks whether the stream is write blocked and its priority. + void RegisterReliableStream(QuicStreamId stream_id) { + // The priority effectively does not matter. Put all streams on the same + // priority. + write_blocked_streams()->RegisterStream( + stream_id, + /*is_static_stream=*/false, + /* precedence= */ spdy::SpdyStreamPrecedence(3)); + } + + // The session take ownership of the stream. + void ActivateReliableStream(std::unique_ptr<QuicStream> stream) { + ActivateStream(std::move(stream)); + } + + std::unique_ptr<QuicCryptoStream> CreateCryptoStream() override { + return nullptr; + } + + MOCK_METHOD1(ProcessPacketFromPeer, void(QuicStringPiece)); + MOCK_METHOD1(ProcessPacketFromNetwork, void(QuicStringPiece)); + + private: + // Whether data is written to write_buffer_. + bool writable_ = true; +}; + +// Packet writer that does nothing. This is required for QuicConnection but +// isn't used for writing data. +class DummyPacketWriter : public QuicPacketWriter { + public: + DummyPacketWriter() {} + + // QuicPacketWriter overrides. + WriteResult WritePacket(const char* buffer, + size_t buf_len, + const QuicIpAddress& self_address, + const QuicSocketAddress& peer_address, + PerPacketOptions* options) override { + return WriteResult(WRITE_STATUS_ERROR, 0); + } + + bool IsWriteBlocked() const override { return false; }; + + void SetWritable() override {} + + QuicByteCount GetMaxPacketSize( + const QuicSocketAddress& peer_address) const override { + return 0; + } + + bool SupportsReleaseTime() const override { return false; } + + bool IsBatchMode() const override { return false; } + + char* GetNextWriteLocation(const QuicIpAddress& self_address, + const QuicSocketAddress& peer_address) override { + return nullptr; + } + + WriteResult Flush() override { return WriteResult(WRITE_STATUS_OK, 0); } +}; + +class QboneReadOnlyStreamTest : public ::testing::Test, + public QuicConnectionHelperInterface { + public: + void CreateReliableQuicStream() { + // Arbitrary values for QuicConnection. + Perspective perspective = Perspective::IS_SERVER; + bool owns_writer = true; + + alarm_factory_ = QuicMakeUnique<test::MockAlarmFactory>(); + + connection_.reset(new QuicConnection( + test::TestConnectionId(0), QuicSocketAddress(TestLoopback(), 0), + this /*QuicConnectionHelperInterface*/, alarm_factory_.get(), + new DummyPacketWriter(), owns_writer, perspective, + ParsedVersionOfIndex(CurrentSupportedVersions(), 0))); + clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1)); + session_ = QuicMakeUnique<StrictMock<MockQuicSession>>(connection_.get(), + QuicConfig()); + stream_ = new QboneReadOnlyStream(kStreamId, session_.get()); + session_->ActivateReliableStream( + std::unique_ptr<QboneReadOnlyStream>(stream_)); + } + + ~QboneReadOnlyStreamTest() override {} + + const QuicClock* GetClock() const override { return &clock_; } + + QuicRandom* GetRandomGenerator() override { + return QuicRandom::GetInstance(); + } + + QuicBufferAllocator* GetStreamSendBufferAllocator() override { + return &buffer_allocator_; + } + + protected: + // The QuicSession will take the ownership. + QboneReadOnlyStream* stream_; + std::unique_ptr<StrictMock<MockQuicSession>> session_; + std::unique_ptr<QuicAlarmFactory> alarm_factory_; + std::unique_ptr<QuicConnection> connection_; + // Used to implement the QuicConnectionHelperInterface. + SimpleBufferAllocator buffer_allocator_; + MockClock clock_; + const QuicStreamId kStreamId = QuicUtils::GetFirstUnidirectionalStreamId( + CurrentSupportedVersions()[0].transport_version, + Perspective::IS_CLIENT); +}; + +// Read an entire string. +TEST_F(QboneReadOnlyStreamTest, ReadDataWhole) { + string packet = "Stuff"; + CreateReliableQuicStream(); + QuicStreamFrame frame(kStreamId, true, 0, packet); + EXPECT_CALL(*session_, ProcessPacketFromPeer("Stuff")); + stream_->OnStreamFrame(frame); +} + +// Test buffering. +TEST_F(QboneReadOnlyStreamTest, ReadBuffered) { + CreateReliableQuicStream(); + string packet = "Stuf"; + { + QuicStreamFrame frame(kStreamId, false, 0, packet); + stream_->OnStreamFrame(frame); + } + // We didn't write 5 bytes yet... + + packet = "f"; + EXPECT_CALL(*session_, ProcessPacketFromPeer("Stuff")); + { + QuicStreamFrame frame(kStreamId, true, 4, packet); + stream_->OnStreamFrame(frame); + } +} + +TEST_F(QboneReadOnlyStreamTest, ReadOutOfOrder) { + CreateReliableQuicStream(); + string packet = "f"; + { + QuicStreamFrame frame(kStreamId, true, 4, packet); + stream_->OnStreamFrame(frame); + } + + packet = "S"; + { + QuicStreamFrame frame(kStreamId, false, 0, packet); + stream_->OnStreamFrame(frame); + } + + packet = "tuf"; + EXPECT_CALL(*session_, ProcessPacketFromPeer("Stuff")); + { + QuicStreamFrame frame(kStreamId, false, 1, packet); + stream_->OnStreamFrame(frame); + } +} + +// Test buffering too many bytes. +TEST_F(QboneReadOnlyStreamTest, ReadBufferedTooLarge) { + CreateReliableQuicStream(); + string packet = "0123456789"; + int iterations = (QboneConstants::kMaxQbonePacketBytes / packet.size()) + 2; + EXPECT_CALL(*session_, + SendRstStream(kStreamId, QUIC_BAD_APPLICATION_PAYLOAD, _)); + for (int i = 0; i < iterations; ++i) { + QuicStreamFrame frame(kStreamId, i == (iterations - 1), i * packet.size(), + packet); + if (!stream_->reading_stopped()) { + stream_->OnStreamFrame(frame); + } + } + // We should have nothing written to the network and the stream + // should have stopped reading. + EXPECT_TRUE(stream_->reading_stopped()); +} + +} // namespace + +} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_crypto_helpers.cc b/chromium/net/third_party/quiche/src/quic/quartc/quartc_crypto_helpers.cc index 1446ab352d5..4f39271436e 100644 --- a/chromium/net/third_party/quiche/src/quic/quartc/quartc_crypto_helpers.cc +++ b/chromium/net/third_party/quiche/src/quic/quartc/quartc_crypto_helpers.cc @@ -72,14 +72,6 @@ InsecureProofVerifier::CreateDefaultContext() { return nullptr; } -QuicConnectionId QuartcCryptoServerStreamHelper::GenerateConnectionIdForReject( - QuicTransportVersion /*version*/, - QuicConnectionId /*connection_id*/) const { - // TODO(b/124399417): Request a zero-length connection id here when the QUIC - // server perspective supports it. - return QuicUtils::CreateRandomConnectionId(); -} - bool QuartcCryptoServerStreamHelper::CanAcceptClientHello( const CryptoHandshakeMessage& /*message*/, const QuicSocketAddress& /*client_address*/, diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_crypto_helpers.h b/chromium/net/third_party/quiche/src/quic/quartc/quartc_crypto_helpers.h index 1436aeb41f0..cdf14a040eb 100644 --- a/chromium/net/third_party/quiche/src/quic/quartc/quartc_crypto_helpers.h +++ b/chromium/net/third_party/quiche/src/quic/quartc/quartc_crypto_helpers.h @@ -101,10 +101,6 @@ class InsecureProofVerifier : public ProofVerifier { // Implementation of the server-side crypto stream helper. class QuartcCryptoServerStreamHelper : public QuicCryptoServerStream::Helper { public: - QuicConnectionId GenerateConnectionIdForReject( - QuicTransportVersion version, - QuicConnectionId connection_id) const override; - bool CanAcceptClientHello(const CryptoHandshakeMessage& message, const QuicSocketAddress& client_address, const QuicSocketAddress& peer_address, diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_dispatcher.h b/chromium/net/third_party/quiche/src/quic/quartc/quartc_dispatcher.h index d1128d61afe..06e799ea98a 100644 --- a/chromium/net/third_party/quiche/src/quic/quartc/quartc_dispatcher.h +++ b/chromium/net/third_party/quiche/src/quic/quartc/quartc_dispatcher.h @@ -45,6 +45,9 @@ class QuartcDispatcher : public QuicDispatcher, QuicStringPiece alpn, const ParsedQuicVersion& version) override; + // TODO(b/124399417): Override GenerateNewServerConnectionId and request a + // zero-length connection id when the QUIC server perspective supports it. + // QuartcPacketTransport::Delegate overrides. void OnTransportCanWrite() override; void OnTransportReceived(const char* data, size_t data_len) override; diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_factory.cc b/chromium/net/third_party/quiche/src/quic/quartc/quartc_factory.cc index 0c11bd0d85f..6742b895b33 100644 --- a/chromium/net/third_party/quiche/src/quic/quartc/quartc_factory.cc +++ b/chromium/net/third_party/quiche/src/quic/quartc/quartc_factory.cc @@ -8,6 +8,7 @@ #include "net/third_party/quiche/src/quic/core/quic_utils.h" #include "net/third_party/quiche/src/quic/core/tls_client_handshaker.h" #include "net/third_party/quiche/src/quic/core/tls_server_handshaker.h" +#include "net/third_party/quiche/src/quic/core/uber_received_packet_manager.h" #include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" #include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h" #include "net/third_party/quiche/src/quic/quartc/quartc_connection_helper.h" @@ -43,6 +44,13 @@ std::unique_ptr<QuartcSession> CreateQuartcClientSession( dummy_id, dummy_address, connection_helper, alarm_factory, writer.get(), Perspective::IS_CLIENT, supported_versions); + // Quartc sets its own ack delay; get that ack delay and copy it over + // to the QuicConfig so that it can be properly advertised to the peer + // via transport parameter negotiation. + quic_config.SetMaxAckDelayToSendMs(quic_connection->received_packet_manager() + .max_ack_delay() + .ToMilliseconds()); + return QuicMakeUnique<QuartcClientSession>( std::move(quic_connection), quic_config, supported_versions, clock, std::move(writer), @@ -195,6 +203,8 @@ std::unique_ptr<QuicConnection> CreateQuicConnection( QuicSentPacketManager& sent_packet_manager = quic_connection->sent_packet_manager(); + UberReceivedPacketManager& received_packet_manager = + quic_connection->received_packet_manager(); // Default delayed ack time is 25ms. // If data packets are sent less often (e.g. because p-time was modified), @@ -206,7 +216,7 @@ std::unique_ptr<QuicConnection> CreateQuicConnection( // The p-time can go up to as high as 120ms, and when it does, it's // when the low overhead is the most important thing. Ideally it should be // above 120ms, but it cannot be higher than 0.5*RTO, which equals to 100ms. - sent_packet_manager.set_local_max_ack_delay( + received_packet_manager.set_max_ack_delay( QuicTime::Delta::FromMilliseconds(100)); sent_packet_manager.set_peer_max_ack_delay( QuicTime::Delta::FromMilliseconds(100)); diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_multiplexer.cc b/chromium/net/third_party/quiche/src/quic/quartc/quartc_multiplexer.cc new file mode 100644 index 00000000000..001e2198d16 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/quartc/quartc_multiplexer.cc @@ -0,0 +1,269 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/third_party/quiche/src/quic/quartc/quartc_multiplexer.h" + +#include <cstdint> + +#include "net/third_party/quiche/src/quic/core/quic_data_writer.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_mem_slice.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_mem_slice_span.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" + +namespace quic { + +QuartcSendChannel::QuartcSendChannel(QuartcMultiplexer* multiplexer, + uint64_t id, + QuicBufferAllocator* allocator, + Delegate* delegate) + : multiplexer_(multiplexer), + id_(id), + encoded_length_(QuicDataWriter::GetVarInt62Len(id_)), + allocator_(allocator), + delegate_(delegate) {} + +QuartcStream* QuartcSendChannel::CreateOutgoingBidirectionalStream() { + if (!session_) { + QUIC_LOG(DFATAL) << "Session is not ready to write yet; channel_id=" << id_; + return nullptr; + } + QuicMemSlice id_slice = EncodeChannelId(); + + QuartcStream* stream = session_->CreateOutgoingBidirectionalStream(); + QuicConsumedData consumed = + stream->WriteMemSlices(QuicMemSliceSpan(&id_slice), /*fin=*/false); + DCHECK_EQ(consumed.bytes_consumed, encoded_length_); + return stream; +} + +bool QuartcSendChannel::SendOrQueueMessage(QuicMemSliceSpan message, + int64_t datagram_id) { + if (!session_) { + QUIC_LOG(DFATAL) << "Session is not ready to write yet; channel_id=" << id_ + << "datagram size=" << message.total_length(); + return false; + } + QuicMemSliceStorage storage(nullptr, 0, nullptr, 0); // Empty storage. + storage.Append(EncodeChannelId()); + + message.ConsumeAll( + [&storage](QuicMemSlice slice) { storage.Append(std::move(slice)); }); + + // Allocate a unique datagram id so that notifications can be routed back to + // the right send channel. + int64_t unique_datagram_id = multiplexer_->AllocateDatagramId(this); + multiplexer_to_user_datagram_ids_[unique_datagram_id] = datagram_id; + + return session_->SendOrQueueMessage(storage.ToSpan(), unique_datagram_id); +} + +void QuartcSendChannel::OnMessageSent(int64_t datagram_id) { + // Map back to the caller-chosen |datagram_id|. + datagram_id = multiplexer_to_user_datagram_ids_[datagram_id]; + delegate_->OnMessageSent(datagram_id); +} + +void QuartcSendChannel::OnMessageAcked(int64_t datagram_id, + QuicTime receive_timestamp) { + // Map back to the caller-chosen |datagram_id|. + auto it = multiplexer_to_user_datagram_ids_.find(datagram_id); + if (it == multiplexer_to_user_datagram_ids_.end()) { + QUIC_LOG(DFATAL) << "Datagram acked/lost multiple times; datagram_id=" + << datagram_id; + return; + } + delegate_->OnMessageAcked(it->second, receive_timestamp); + multiplexer_to_user_datagram_ids_.erase(it); +} + +void QuartcSendChannel::OnMessageLost(int64_t datagram_id) { + // Map back to the caller-chosen |datagram_id|. + auto it = multiplexer_to_user_datagram_ids_.find(datagram_id); + if (it == multiplexer_to_user_datagram_ids_.end()) { + QUIC_LOG(DFATAL) << "Datagram acked/lost multiple times; datagram_id=" + << datagram_id; + return; + } + delegate_->OnMessageLost(it->second); + multiplexer_to_user_datagram_ids_.erase(it); +} + +void QuartcSendChannel::OnSessionCreated(QuartcSession* session) { + session_ = session; +} + +QuicMemSlice QuartcSendChannel::EncodeChannelId() { + QuicMemSlice id_slice(allocator_, encoded_length_); + QuicDataWriter writer(encoded_length_, const_cast<char*>(id_slice.data())); + writer.WriteVarInt62(id_); + return id_slice; +} + +QuartcMultiplexer::QuartcMultiplexer( + QuicBufferAllocator* allocator, + QuartcSessionEventDelegate* session_delegate, + QuartcReceiveChannel* default_receive_channel) + : allocator_(allocator), + session_delegate_(session_delegate), + default_receive_channel_(default_receive_channel) { + CHECK_NE(session_delegate_, nullptr); + CHECK_NE(default_receive_channel_, nullptr); +} + +QuartcSendChannel* QuartcMultiplexer::CreateSendChannel( + uint64_t channel_id, + QuartcSendChannel::Delegate* delegate) { + send_channels_.push_back(QuicMakeUnique<QuartcSendChannel>( + this, channel_id, allocator_, delegate)); + if (session_) { + send_channels_.back()->OnSessionCreated(session_); + } + return send_channels_.back().get(); +} + +void QuartcMultiplexer::RegisterReceiveChannel(uint64_t channel_id, + QuartcReceiveChannel* channel) { + if (channel == nullptr) { + receive_channels_.erase(channel_id); + return; + } + auto& registered_channel = receive_channels_[channel_id]; + if (registered_channel) { + QUIC_LOG(DFATAL) << "Attempted to overwrite existing channel_id=" + << channel_id; + return; + } + registered_channel = channel; +} + +int64_t QuartcMultiplexer::AllocateDatagramId(QuartcSendChannel* channel) { + send_channels_by_datagram_id_[next_datagram_id_] = channel; + return next_datagram_id_++; +} + +void QuartcMultiplexer::OnSessionCreated(QuartcSession* session) { + for (auto& channel : send_channels_) { + channel->OnSessionCreated(session); + } + session_ = session; + session_delegate_->OnSessionCreated(session); +} + +void QuartcMultiplexer::OnCryptoHandshakeComplete() { + session_delegate_->OnCryptoHandshakeComplete(); +} + +void QuartcMultiplexer::OnConnectionWritable() { + session_delegate_->OnConnectionWritable(); +} + +void QuartcMultiplexer::OnIncomingStream(QuartcStream* stream) { + stream->SetDelegate(this); +} + +void QuartcMultiplexer::OnCongestionControlChange( + QuicBandwidth bandwidth_estimate, + QuicBandwidth pacing_rate, + QuicTime::Delta latest_rtt) { + session_delegate_->OnCongestionControlChange(bandwidth_estimate, pacing_rate, + latest_rtt); +} + +void QuartcMultiplexer::OnConnectionClosed( + const QuicConnectionCloseFrame& frame, + ConnectionCloseSource source) { + session_delegate_->OnConnectionClosed(frame, source); +} + +void QuartcMultiplexer::OnMessageReceived(QuicStringPiece message) { + QuicDataReader reader(message); + QuicVariableLengthIntegerLength channel_id_length = + reader.PeekVarInt62Length(); + + uint64_t channel_id; + if (!reader.ReadVarInt62(&channel_id)) { + QUIC_LOG(DFATAL) << "Received message without properly encoded channel id"; + return; + } + + QuartcReceiveChannel* channel = default_receive_channel_; + auto it = receive_channels_.find(channel_id); + if (it != receive_channels_.end()) { + channel = it->second; + } + + channel->OnMessageReceived(channel_id, message.substr(channel_id_length)); +} + +void QuartcMultiplexer::OnMessageSent(int64_t datagram_id) { + auto it = send_channels_by_datagram_id_.find(datagram_id); + if (it == send_channels_by_datagram_id_.end()) { + return; + } + it->second->OnMessageSent(datagram_id); +} + +void QuartcMultiplexer::OnMessageAcked(int64_t datagram_id, + QuicTime receive_timestamp) { + auto it = send_channels_by_datagram_id_.find(datagram_id); + if (it == send_channels_by_datagram_id_.end()) { + return; + } + it->second->OnMessageAcked(datagram_id, receive_timestamp); + send_channels_by_datagram_id_.erase(it); +} + +void QuartcMultiplexer::OnMessageLost(int64_t datagram_id) { + auto it = send_channels_by_datagram_id_.find(datagram_id); + if (it == send_channels_by_datagram_id_.end()) { + return; + } + it->second->OnMessageLost(datagram_id); + send_channels_by_datagram_id_.erase(it); +} + +size_t QuartcMultiplexer::OnReceived(QuartcStream* stream, + iovec* iov, + size_t iov_length, + bool /*fin*/) { + if (iov == nullptr || iov_length <= 0) { + return 0; + } + + QuicDataReader reader(static_cast<char*>(iov[0].iov_base), iov[0].iov_len); + QuicVariableLengthIntegerLength channel_id_length = + reader.PeekVarInt62Length(); + + uint64_t channel_id; + if (reader.BytesRemaining() >= channel_id_length) { + // Fast path, have enough data to read immediately. + if (!reader.ReadVarInt62(&channel_id)) { + return 0; + } + } else { + // Slow path, need to coalesce multiple iovecs. + std::string data; + for (size_t i = 0; i < iov_length; ++i) { + data += std::string(static_cast<char*>(iov[i].iov_base), iov[i].iov_len); + } + QuicDataReader combined_reader(data); + if (!combined_reader.ReadVarInt62(&channel_id)) { + return 0; + } + } + + QuartcReceiveChannel* channel = default_receive_channel_; + auto it = receive_channels_.find(channel_id); + if (it != receive_channels_.end()) { + channel = it->second; + } + channel->OnIncomingStream(channel_id, stream); + return channel_id_length; +} + +void QuartcMultiplexer::OnClose(QuartcStream* /*stream*/) {} + +void QuartcMultiplexer::OnBufferChanged(QuartcStream* /*stream*/) {} + +} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_multiplexer.h b/chromium/net/third_party/quiche/src/quic/quartc/quartc_multiplexer.h new file mode 100644 index 00000000000..9cb581d47b7 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/quartc/quartc_multiplexer.h @@ -0,0 +1,190 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef QUICHE_QUIC_QUARTC_QUARTC_MULTIPLEXER_H_ +#define QUICHE_QUIC_QUARTC_QUARTC_MULTIPLEXER_H_ + +#include <cstdint> + +#include "net/third_party/quiche/src/quic/core/quic_time.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_mem_slice_span.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" +#include "net/third_party/quiche/src/quic/quartc/quartc_endpoint.h" +#include "net/third_party/quiche/src/quic/quartc/quartc_session.h" +#include "net/third_party/quiche/src/quic/quartc/quartc_stream.h" + +namespace quic { + +class QuartcMultiplexer; + +// A single, multiplexed send channel within a Quartc session. A send channel +// wraps send-side operations with an outgoing multiplex id. +class QuartcSendChannel { + public: + class Delegate { + public: + virtual ~Delegate() = default; + + // Called when a message with |datagram_id| is sent by this channel. + virtual void OnMessageSent(int64_t datagram_id) = 0; + + // Called when a message sent on this channel with |datagram_id| is acked. + // |receive_timestamp| indicates when the peer received this message, + // according to the peer's clock. + virtual void OnMessageAcked(int64_t datagram_id, + QuicTime receive_timestamp) = 0; + + // Called when a message sent on this channel with |datagram_id| is lost. + virtual void OnMessageLost(int64_t datagram_id) = 0; + }; + + QuartcSendChannel(QuartcMultiplexer* multiplexer, + uint64_t id, + QuicBufferAllocator* allocator, + Delegate* delegate); + virtual ~QuartcSendChannel() = default; + + // Creates a new, outgoing stream on this channel. + // + // Automatically writes the channel id to the start of the stream. The caller + // SHOULD create a |ScopedPacketFlusher| before calling this function to + // prevent the channel id from being sent by itself. + QuartcStream* CreateOutgoingBidirectionalStream(); + + // Writes |message| to the session. Prepends the channel's send id before any + // following message data. + bool SendOrQueueMessage(QuicMemSliceSpan message, int64_t datagram_id); + + // Gets the current largest message payload for this channel. Returns the + // largest payload size supported by the session minus overhead required to + // encode this channel's send id. + QuicPacketLength GetCurrentLargestMessagePayload() const; + + // The following are called by the multiplexer to deliver message + // notifications. The |datagram_id| passed to these is unique per-message, + // and must be translated back to the sender's chosen datagram_id. + void OnMessageSent(int64_t datagram_id); + void OnMessageAcked(int64_t datagram_id, QuicTime receive_timestamp); + void OnMessageLost(int64_t datagram_id); + void OnSessionCreated(QuartcSession* session); + + private: + // Creates a mem slice containing a varint-62 encoded channel id. + QuicMemSlice EncodeChannelId(); + + QuartcMultiplexer* const multiplexer_; + const uint64_t id_; + const QuicVariableLengthIntegerLength encoded_length_; + QuicBufferAllocator* const allocator_; + Delegate* const delegate_; + + QuartcSession* session_; + + // Map of multiplexer-chosen to user/caller-specified datagram ids. The user + // may specify any number as a datagram's id. This number does not have to be + // unique across channels (nor even within a single channel). In order + // to demux sent, acked, and lost messages, the multiplexer assigns a globally + // unique id to each message. This map is used to restore the original caller + // datagram id before issuing callbacks. + QuicUnorderedMap<int64_t, int64_t> multiplexer_to_user_datagram_ids_; +}; + +// A single, multiplexed receive channel within a Quartc session. A receive +// channel is a delegate which accepts incoming streams and datagrams on one (or +// more) channel ids. +class QuartcReceiveChannel { + public: + virtual ~QuartcReceiveChannel() = default; + + // Called when a new incoming stream arrives on this channel. + virtual void OnIncomingStream(uint64_t channel_id, QuartcStream* stream) = 0; + + // Called when a message is recieved by this channel. + virtual void OnMessageReceived(uint64_t channel_id, + QuicStringPiece message) = 0; +}; + +// Delegate for session-wide events. +class QuartcSessionEventDelegate { + public: + virtual ~QuartcSessionEventDelegate() = default; + + virtual void OnSessionCreated(QuartcSession* session) = 0; + virtual void OnCryptoHandshakeComplete() = 0; + virtual void OnConnectionWritable() = 0; + virtual void OnCongestionControlChange(QuicBandwidth bandwidth_estimate, + QuicBandwidth pacing_rate, + QuicTime::Delta latest_rtt) = 0; + virtual void OnConnectionClosed(const QuicConnectionCloseFrame& frame, + ConnectionCloseSource source) = 0; +}; + +// A multiplexer capable of sending and receiving data on multiple channels. +class QuartcMultiplexer : public QuartcEndpoint::Delegate, + public QuartcStream::Delegate { + public: + // Creates a new multiplexer. |session_delegate| handles all session-wide + // events, while |default_receive_channel| handles incoming data on unknown + // or unregistered channel ids. Neither |session_delegate| nor + // |default_receive_channel| may be nullptr, and both must outlive the + // multiplexer. + QuartcMultiplexer(QuicBufferAllocator* allocator, + QuartcSessionEventDelegate* session_delegate, + QuartcReceiveChannel* default_receive_channel); + + // Creates a new send channel. The channel is owned by the multiplexer, and + // references to it must not outlive the multiplexer. + QuartcSendChannel* CreateSendChannel(uint64_t channel_id, + QuartcSendChannel::Delegate* delegate); + + // Registers a receiver for incoming data on |channel_id|. + void RegisterReceiveChannel(uint64_t channel_id, + QuartcReceiveChannel* channel); + + // Allocates a datagram id to |channel|. + int64_t AllocateDatagramId(QuartcSendChannel* channel); + + // QuartcEndpoint::Delegate overrides. + void OnSessionCreated(QuartcSession* session) override; + + // QuartcSession::Delegate overrides. + void OnCryptoHandshakeComplete() override; + void OnConnectionWritable() override; + void OnIncomingStream(QuartcStream* stream) override; + void OnCongestionControlChange(QuicBandwidth bandwidth_estimate, + QuicBandwidth pacing_rate, + QuicTime::Delta latest_rtt) override; + void OnConnectionClosed(const QuicConnectionCloseFrame& frame, + ConnectionCloseSource source) override; + void OnMessageReceived(QuicStringPiece message) override; + void OnMessageSent(int64_t datagram_id) override; + void OnMessageAcked(int64_t datagram_id, QuicTime receive_timestamp) override; + void OnMessageLost(int64_t datagram_id) override; + + // QuartcStream::Delegate overrides. + size_t OnReceived(QuartcStream* stream, + iovec* iov, + size_t iov_length, + bool fin) override; + void OnClose(QuartcStream* stream) override; + void OnBufferChanged(QuartcStream* stream) override; + + private: + QuicBufferAllocator* const allocator_; + QuartcSessionEventDelegate* const session_delegate_; + + QuartcSession* session_ = nullptr; + std::vector<std::unique_ptr<QuartcSendChannel>> send_channels_; + QuicUnorderedMap<uint64_t, QuartcReceiveChannel*> receive_channels_; + QuartcReceiveChannel* default_receive_channel_ = nullptr; + + int64_t next_datagram_id_ = 1; + QuicUnorderedMap<int64_t, QuartcSendChannel*> send_channels_by_datagram_id_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_QUARTC_QUARTC_MULTIPLEXER_H_ diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_multiplexer_test.cc b/chromium/net/third_party/quiche/src/quic/quartc/quartc_multiplexer_test.cc new file mode 100644 index 00000000000..64609cbc56e --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/quartc/quartc_multiplexer_test.cc @@ -0,0 +1,490 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/third_party/quiche/src/quic/quartc/quartc_multiplexer.h" + +#include <memory> + +#include "net/third_party/quiche/src/quic/core/frames/quic_connection_close_frame.h" +#include "net/third_party/quiche/src/quic/core/quic_bandwidth.h" +#include "net/third_party/quiche/src/quic/core/quic_buffer_allocator.h" +#include "net/third_party/quiche/src/quic/core/quic_time.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test_mem_slice_vector.h" +#include "net/third_party/quiche/src/quic/quartc/counting_packet_filter.h" +#include "net/third_party/quiche/src/quic/quartc/quartc_endpoint.h" +#include "net/third_party/quiche/src/quic/quartc/quartc_fakes.h" +#include "net/third_party/quiche/src/quic/quartc/quartc_session.h" +#include "net/third_party/quiche/src/quic/quartc/quartc_stream.h" +#include "net/third_party/quiche/src/quic/quartc/simulated_packet_transport.h" +#include "net/third_party/quiche/src/quic/test_tools/simulator/link.h" +#include "net/third_party/quiche/src/quic/test_tools/simulator/simulator.h" + +namespace quic { +namespace { + +using ::testing::ElementsAreArray; +using ::testing::Gt; +using ::testing::IsEmpty; +using ::testing::Pair; + +constexpr QuicTime::Delta kPropagationDelay = + QuicTime::Delta::FromMilliseconds(10); + +class FakeSessionEventDelegate : public QuartcSessionEventDelegate { + public: + void OnSessionCreated(QuartcSession* session) override { + session->StartCryptoHandshake(); + session_ = session; + } + + void OnConnectionWritable() override { ++writable_count_; } + + void OnCryptoHandshakeComplete() override { ++handshake_count_; } + + void OnConnectionClosed(const QuicConnectionCloseFrame& frame, + ConnectionCloseSource source) override { + error_ = frame.quic_error_code; + close_source_ = source; + } + + void OnCongestionControlChange(QuicBandwidth bandwidth_estimate, + QuicBandwidth pacing_rate, + QuicTime::Delta latest_rtt) override { + latest_bandwidth_estimate_ = bandwidth_estimate; + latest_pacing_rate_ = pacing_rate; + latest_rtt_ = latest_rtt; + } + + QuartcSession* session() { return session_; } + int writable_count() const { return writable_count_; } + int handshake_count() const { return handshake_count_; } + QuicErrorCode error() const { return error_; } + ConnectionCloseSource close_source() const { return close_source_; } + QuicBandwidth latest_bandwidth_estimate() const { + return latest_bandwidth_estimate_; + } + QuicBandwidth latest_pacing_rate() const { return latest_pacing_rate_; } + QuicTime::Delta latest_rtt() const { return latest_rtt_; } + + private: + QuartcSession* session_ = nullptr; + int writable_count_ = 0; + int handshake_count_ = 0; + QuicErrorCode error_ = QUIC_NO_ERROR; + ConnectionCloseSource close_source_; + QuicBandwidth latest_bandwidth_estimate_ = QuicBandwidth::Zero(); + QuicBandwidth latest_pacing_rate_ = QuicBandwidth::Zero(); + QuicTime::Delta latest_rtt_ = QuicTime::Delta::Zero(); +}; + +class FakeSendDelegate : public QuartcSendChannel::Delegate { + public: + void OnMessageSent(int64_t datagram_id) override { + datagrams_sent_.push_back(datagram_id); + } + + void OnMessageAcked(int64_t datagram_id, + QuicTime receive_timestamp) override { + datagrams_acked_.push_back({datagram_id, receive_timestamp}); + } + + void OnMessageLost(int64_t datagram_id) override { + datagrams_lost_.push_back(datagram_id); + } + + const std::vector<int64_t>& datagrams_sent() const { return datagrams_sent_; } + const std::vector<std::pair<int64_t, QuicTime>>& datagrams_acked() const { + return datagrams_acked_; + } + const std::vector<int64_t>& datagrams_lost() const { return datagrams_lost_; } + + private: + std::vector<int64_t> datagrams_sent_; + std::vector<std::pair<int64_t, QuicTime>> datagrams_acked_; + std::vector<int64_t> datagrams_lost_; +}; + +class FakeReceiveDelegate : public QuartcReceiveChannel, + public QuartcStream::Delegate { + public: + const std::vector<std::pair<uint64_t, std::string>> messages_received() + const { + return messages_received_; + } + + void OnIncomingStream(uint64_t channel_id, QuartcStream* stream) override { + stream->SetDelegate(this); + stream_to_channel_id_[stream] = channel_id; + } + + void OnMessageReceived(uint64_t channel_id, + QuicStringPiece message) override { + messages_received_.emplace_back(channel_id, message); + } + + // Stream delegate overrides. + size_t OnReceived(QuartcStream* stream, + iovec* iov, + size_t iov_length, + bool fin) override { + if (!fin) { + return 0; + } + + size_t bytes = 0; + std::string message; + for (size_t i = 0; i < iov_length; ++i) { + message += + std::string(static_cast<char*>(iov[i].iov_base), iov[i].iov_len); + bytes += iov[i].iov_len; + } + QUIC_LOG(INFO) << "Received " << bytes << " byte message on channel " + << stream_to_channel_id_[stream]; + messages_received_.emplace_back(stream_to_channel_id_[stream], message); + return bytes; + } + + void OnClose(QuartcStream* stream) override { + stream_to_channel_id_.erase(stream); + } + + void OnBufferChanged(QuartcStream* /*stream*/) override {} + + private: + std::vector<std::pair<uint64_t, std::string>> messages_received_; + QuicUnorderedMap<QuartcStream*, uint64_t> stream_to_channel_id_; +}; + +class QuartcMultiplexerTest : public QuicTest { + public: + QuartcMultiplexerTest() + : simulator_(), + client_transport_(&simulator_, + "client_transport", + "server_transport", + 10 * kDefaultMaxPacketSize), + server_transport_(&simulator_, + "server_transport", + "client_transport", + 10 * kDefaultMaxPacketSize), + client_filter_(&simulator_, "client_filter", &client_transport_), + client_server_link_(&client_filter_, + &server_transport_, + QuicBandwidth::FromKBitsPerSecond(10 * 1000), + kPropagationDelay), + client_multiplexer_(simulator_.GetStreamSendBufferAllocator(), + &client_session_delegate_, + &client_default_receiver_), + server_multiplexer_(simulator_.GetStreamSendBufferAllocator(), + &server_session_delegate_, + &server_default_receiver_), + client_endpoint_(QuicMakeUnique<QuartcClientEndpoint>( + simulator_.GetAlarmFactory(), + simulator_.GetClock(), + simulator_.GetRandomGenerator(), + &client_multiplexer_, + quic::QuartcSessionConfig(), + /*serialized_server_config=*/"")), + server_endpoint_(QuicMakeUnique<QuartcServerEndpoint>( + simulator_.GetAlarmFactory(), + simulator_.GetClock(), + simulator_.GetRandomGenerator(), + &server_multiplexer_, + quic::QuartcSessionConfig())) { + // TODO(b/134175506): Remove when IETF QUIC supports receive timestamps. + SetQuicReloadableFlag(quic_enable_version_99, false); + } + + void Connect() { + client_endpoint_->Connect(&client_transport_); + server_endpoint_->Connect(&server_transport_); + ASSERT_TRUE(simulator_.RunUntil([this]() { + return client_session_delegate_.writable_count() > 0 && + server_session_delegate_.writable_count() > 0; + })); + } + + void Disconnect() { + client_session_delegate_.session()->CloseConnection("test"); + server_session_delegate_.session()->CloseConnection("test"); + } + + protected: + QuartcMultiplexer* client_multiplexer() { return &client_multiplexer_; } + + QuartcMultiplexer* server_multiplexer() { return &server_multiplexer_; } + + simulator::Simulator simulator_; + + simulator::SimulatedQuartcPacketTransport client_transport_; + simulator::SimulatedQuartcPacketTransport server_transport_; + simulator::CountingPacketFilter client_filter_; + simulator::SymmetricLink client_server_link_; + + FakeSessionEventDelegate client_session_delegate_; + FakeSessionEventDelegate server_session_delegate_; + + FakeReceiveDelegate client_default_receiver_; + FakeReceiveDelegate server_default_receiver_; + + QuartcMultiplexer client_multiplexer_; + QuartcMultiplexer server_multiplexer_; + + std::unique_ptr<QuartcClientEndpoint> client_endpoint_; + std::unique_ptr<QuartcServerEndpoint> server_endpoint_; +}; + +TEST_F(QuartcMultiplexerTest, MultiplexMessages) { + Connect(); + + FakeSendDelegate send_delegate_1; + QuartcSendChannel* send_channel_1 = + client_multiplexer()->CreateSendChannel(1, &send_delegate_1); + FakeSendDelegate send_delegate_2; + QuartcSendChannel* send_channel_2 = + client_multiplexer()->CreateSendChannel(2, &send_delegate_2); + + FakeReceiveDelegate receive_delegate_1; + server_multiplexer()->RegisterReceiveChannel(1, &receive_delegate_1); + + int num_messages = 10; + std::vector<std::pair<uint64_t, std::string>> messages_1; + messages_1.reserve(num_messages); + std::vector<std::pair<uint64_t, std::string>> messages_2; + messages_2.reserve(num_messages); + std::vector<int64_t> messages_sent_1; + std::vector<int64_t> messages_sent_2; + std::vector<testing::Matcher<std::pair<int64_t, QuicTime>>> ack_matchers_1; + std::vector<testing::Matcher<std::pair<int64_t, QuicTime>>> ack_matchers_2; + for (int i = 0; i < num_messages; ++i) { + messages_1.emplace_back(1, QuicStrCat("message for 1: ", i)); + test::QuicTestMemSliceVector slice_1( + {std::make_pair(const_cast<char*>(messages_1.back().second.data()), + messages_1.back().second.size())}); + send_channel_1->SendOrQueueMessage(slice_1.span(), i); + messages_sent_1.push_back(i); + ack_matchers_1.push_back(Pair(i, Gt(QuicTime::Zero()))); + + messages_2.emplace_back(2, QuicStrCat("message for 2: ", i)); + test::QuicTestMemSliceVector slice_2( + {std::make_pair(const_cast<char*>(messages_2.back().second.data()), + messages_2.back().second.size())}); + // Use i + 5 as the datagram id for channel 2, so that some of the ids + // overlap and some are disjoint. + send_channel_2->SendOrQueueMessage(slice_2.span(), i + 5); + messages_sent_2.push_back(i + 5); + ack_matchers_2.push_back(Pair(i + 5, Gt(QuicTime::Zero()))); + } + + EXPECT_TRUE(simulator_.RunUntil([&send_delegate_1, &send_delegate_2]() { + return send_delegate_1.datagrams_acked().size() == 10 && + send_delegate_2.datagrams_acked().size() == 10; + })); + + EXPECT_EQ(send_delegate_1.datagrams_sent(), messages_sent_1); + EXPECT_EQ(send_delegate_2.datagrams_sent(), messages_sent_2); + + EXPECT_EQ(receive_delegate_1.messages_received(), messages_1); + EXPECT_EQ(server_default_receiver_.messages_received(), messages_2); + + EXPECT_THAT(send_delegate_1.datagrams_acked(), + ElementsAreArray(ack_matchers_1)); + EXPECT_THAT(send_delegate_2.datagrams_acked(), + ElementsAreArray(ack_matchers_2)); +} + +TEST_F(QuartcMultiplexerTest, MultiplexStreams) { + FakeSendDelegate send_delegate_1; + QuartcSendChannel* send_channel_1 = + client_multiplexer()->CreateSendChannel(1, &send_delegate_1); + FakeSendDelegate send_delegate_2; + QuartcSendChannel* send_channel_2 = + client_multiplexer()->CreateSendChannel(2, &send_delegate_2); + + FakeQuartcStreamDelegate fake_send_stream_delegate; + + FakeReceiveDelegate receive_delegate_1; + server_multiplexer()->RegisterReceiveChannel(1, &receive_delegate_1); + + Connect(); + + int num_messages = 10; + std::vector<std::pair<uint64_t, std::string>> messages_1; + messages_1.reserve(num_messages); + std::vector<std::pair<uint64_t, std::string>> messages_2; + messages_2.reserve(num_messages); + for (int i = 0; i < num_messages; ++i) { + messages_1.emplace_back(1, QuicStrCat("message for 1: ", i)); + test::QuicTestMemSliceVector slice_1( + {std::make_pair(const_cast<char*>(messages_1.back().second.data()), + messages_1.back().second.size())}); + QuartcStream* stream_1 = + send_channel_1->CreateOutgoingBidirectionalStream(); + stream_1->SetDelegate(&fake_send_stream_delegate); + stream_1->WriteMemSlices(slice_1.span(), /*fin=*/true); + + messages_2.emplace_back(2, QuicStrCat("message for 2: ", i)); + test::QuicTestMemSliceVector slice_2( + {std::make_pair(const_cast<char*>(messages_2.back().second.data()), + messages_2.back().second.size())}); + QuartcStream* stream_2 = + send_channel_2->CreateOutgoingBidirectionalStream(); + stream_2->SetDelegate(&fake_send_stream_delegate); + stream_2->WriteMemSlices(slice_2.span(), /*fin=*/true); + } + + EXPECT_TRUE(simulator_.RunUntilOrTimeout( + [this, &receive_delegate_1]() { + return receive_delegate_1.messages_received().size() == 10 && + server_default_receiver_.messages_received().size() == 10; + }, + QuicTime::Delta::FromSeconds(5))); + + EXPECT_EQ(receive_delegate_1.messages_received(), messages_1); + EXPECT_EQ(server_default_receiver_.messages_received(), messages_2); +} + +// Tests that datagram-lost callbacks are invoked on the right send channel +// delegate, and that they work with overlapping datagram ids. +TEST_F(QuartcMultiplexerTest, MultiplexLostDatagrams) { + Connect(); + ASSERT_TRUE(simulator_.RunUntil([this]() { + return client_session_delegate_.handshake_count() > 0 && + server_session_delegate_.handshake_count() > 0; + })); + + // Just drop everything we try to send. + client_filter_.set_packets_to_drop(30); + + FakeSendDelegate send_delegate_1; + QuartcSendChannel* send_channel_1 = + client_multiplexer()->CreateSendChannel(1, &send_delegate_1); + FakeSendDelegate send_delegate_2; + QuartcSendChannel* send_channel_2 = + client_multiplexer()->CreateSendChannel(2, &send_delegate_2); + + FakeQuartcStreamDelegate fake_send_stream_delegate; + + FakeReceiveDelegate receive_delegate_1; + server_multiplexer()->RegisterReceiveChannel(1, &receive_delegate_1); + + int num_messages = 10; + std::vector<std::pair<uint64_t, std::string>> messages_1; + messages_1.reserve(num_messages); + std::vector<std::pair<uint64_t, std::string>> messages_2; + messages_2.reserve(num_messages); + std::vector<int64_t> messages_sent_1; + std::vector<int64_t> messages_sent_2; + for (int i = 0; i < num_messages; ++i) { + messages_1.emplace_back(1, QuicStrCat("message for 1: ", i)); + test::QuicTestMemSliceVector slice_1( + {std::make_pair(const_cast<char*>(messages_1.back().second.data()), + messages_1.back().second.size())}); + send_channel_1->SendOrQueueMessage(slice_1.span(), i); + messages_sent_1.push_back(i); + + messages_2.emplace_back(2, QuicStrCat("message for 2: ", i)); + test::QuicTestMemSliceVector slice_2( + {std::make_pair(const_cast<char*>(messages_2.back().second.data()), + messages_2.back().second.size())}); + // Use i + 5 as the datagram id for channel 2, so that some of the ids + // overlap and some are disjoint. + send_channel_2->SendOrQueueMessage(slice_2.span(), i + 5); + messages_sent_2.push_back(i + 5); + } + + // Now send something retransmittable to prompt loss detection. + // If we never send anything retransmittable, we will never get acks, and + // never detect losses. + messages_1.emplace_back(1, QuicStrCat("message for 1: ", num_messages)); + test::QuicTestMemSliceVector slice( + {std::make_pair(const_cast<char*>(messages_1.back().second.data()), + messages_1.back().second.size())}); + QuartcStream* stream_1 = send_channel_1->CreateOutgoingBidirectionalStream(); + stream_1->SetDelegate(&fake_send_stream_delegate); + stream_1->WriteMemSlices(slice.span(), /*fin=*/true); + + EXPECT_TRUE(simulator_.RunUntilOrTimeout( + [&send_delegate_1, &send_delegate_2]() { + return send_delegate_1.datagrams_lost().size() == 10 && + send_delegate_2.datagrams_lost().size() == 10; + }, + QuicTime::Delta::FromSeconds(60))); + + EXPECT_EQ(send_delegate_1.datagrams_lost(), messages_sent_1); + EXPECT_EQ(send_delegate_2.datagrams_lost(), messages_sent_2); + + EXPECT_THAT(send_delegate_1.datagrams_acked(), IsEmpty()); + EXPECT_THAT(send_delegate_2.datagrams_acked(), IsEmpty()); + + EXPECT_THAT(receive_delegate_1.messages_received(), IsEmpty()); + EXPECT_THAT(server_default_receiver_.messages_received(), IsEmpty()); +} + +TEST_F(QuartcMultiplexerTest, UnregisterReceiveChannel) { + Connect(); + + FakeSendDelegate send_delegate; + QuartcSendChannel* send_channel = + client_multiplexer()->CreateSendChannel(1, &send_delegate); + FakeQuartcStreamDelegate fake_send_stream_delegate; + + FakeReceiveDelegate receive_delegate; + server_multiplexer()->RegisterReceiveChannel(1, &receive_delegate); + server_multiplexer()->RegisterReceiveChannel(1, nullptr); + + int num_messages = 10; + std::vector<std::pair<uint64_t, std::string>> messages; + messages.reserve(num_messages); + std::vector<int64_t> messages_sent; + std::vector<testing::Matcher<std::pair<int64_t, QuicTime>>> ack_matchers; + for (int i = 0; i < num_messages; ++i) { + messages.emplace_back(1, QuicStrCat("message for 1: ", i)); + test::QuicTestMemSliceVector slice( + {std::make_pair(const_cast<char*>(messages.back().second.data()), + messages.back().second.size())}); + send_channel->SendOrQueueMessage(slice.span(), i); + messages_sent.push_back(i); + ack_matchers.push_back(Pair(i, Gt(QuicTime::Zero()))); + } + + EXPECT_TRUE(simulator_.RunUntil([&send_delegate]() { + return send_delegate.datagrams_acked().size() == 10; + })); + + EXPECT_EQ(send_delegate.datagrams_sent(), messages_sent); + EXPECT_EQ(server_default_receiver_.messages_received(), messages); + EXPECT_THAT(send_delegate.datagrams_acked(), ElementsAreArray(ack_matchers)); +} + +TEST_F(QuartcMultiplexerTest, CloseEvent) { + Connect(); + Disconnect(); + + EXPECT_EQ(client_session_delegate_.error(), QUIC_CONNECTION_CANCELLED); + EXPECT_EQ(server_session_delegate_.error(), QUIC_CONNECTION_CANCELLED); +} + +TEST_F(QuartcMultiplexerTest, CongestionEvent) { + Connect(); + ASSERT_TRUE(simulator_.RunUntil([this]() { + return client_session_delegate_.handshake_count() > 0 && + server_session_delegate_.handshake_count() > 0; + })); + + EXPECT_GT(client_session_delegate_.latest_bandwidth_estimate(), + QuicBandwidth::Zero()); + EXPECT_GT(client_session_delegate_.latest_pacing_rate(), + QuicBandwidth::Zero()); + EXPECT_GT(client_session_delegate_.latest_rtt(), QuicTime::Delta::Zero()); +} + +} // namespace +} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_session.cc b/chromium/net/third_party/quiche/src/quic/quartc/quartc_session.cc index 8a918caac56..117b1373363 100644 --- a/chromium/net/third_party/quiche/src/quic/quartc/quartc_session.cc +++ b/chromium/net/third_party/quiche/src/quic/quartc/quartc_session.cc @@ -24,7 +24,11 @@ QuartcSession::QuartcSession(std::unique_ptr<QuicConnection> connection, const QuicConfig& config, const ParsedQuicVersionVector& supported_versions, const QuicClock* clock) - : QuicSession(connection.get(), visitor, config, supported_versions), + : QuicSession(connection.get(), + visitor, + config, + supported_versions, + /*num_expected_unidirectional_static_streams = */ 0), connection_(std::move(connection)), clock_(clock), per_packet_options_(QuicMakeUnique<QuartcPerPacketOptions>()) { @@ -152,10 +156,7 @@ bool QuartcSession::SendProbingData() { void QuartcSession::OnCryptoHandshakeEvent(CryptoHandshakeEvent event) { QuicSession::OnCryptoHandshakeEvent(event); switch (event) { - case ENCRYPTION_FIRST_ESTABLISHED: - case ENCRYPTION_REESTABLISHED: - // 1-rtt setup triggers 'ENCRYPTION_REESTABLISHED' (after REJ, when the - // CHLO is sent). + case ENCRYPTION_ESTABLISHED: DCHECK(IsEncryptionEstablished()); DCHECK(session_delegate_); session_delegate_->OnConnectionWritable(); @@ -207,7 +208,7 @@ void QuartcSession::OnCongestionWindowChange(QuicTime /*now*/) { bool QuartcSession::ShouldKeepConnectionAlive() const { // TODO(mellem): Quartc may want different keepalive logic than HTTP. - return GetNumOpenDynamicStreams() > 0; + return GetNumActiveStreams() > 0; } void QuartcSession::OnConnectionClosed(const QuicConnectionCloseFrame& frame, @@ -301,7 +302,8 @@ std::unique_ptr<QuartcStream> QuartcSession::InitializeDataStream( // Register the stream to the QuicWriteBlockedList. |priority| is clamped // between 0 and 7, with 0 being the highest priority and 7 the lowest // priority. - write_blocked_streams()->UpdateStreamPriority(stream->id(), priority); + write_blocked_streams()->UpdateStreamPriority( + stream->id(), spdy::SpdyStreamPrecedence(priority)); if (IsIncomingStream(stream->id())) { DCHECK(session_delegate_); diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_stream.cc b/chromium/net/third_party/quiche/src/quic/quartc/quartc_stream.cc index 607fa432723..f18d42defc8 100644 --- a/chromium/net/third_party/quiche/src/quic/quartc/quartc_stream.cc +++ b/chromium/net/third_party/quiche/src/quic/quartc/quartc_stream.cc @@ -27,24 +27,28 @@ QuartcStream::QuartcStream(QuicStreamId id, QuicSession* session) QuartcStream::~QuartcStream() {} void QuartcStream::OnDataAvailable() { - bool fin = sequencer()->ReadableBytes() + sequencer()->NumBytesConsumed() == - sequencer()->close_offset(); - - // Upper bound on number of readable regions. Each complete block's worth of - // data crosses at most one region boundary. The remainder may cross one more - // boundary. Number of regions is one more than the number of region - // boundaries crossed. - size_t iov_length = sequencer()->ReadableBytes() / - QuicStreamSequencerBuffer::kBlockSizeBytes + - 2; - std::unique_ptr<iovec[]> iovecs = QuicMakeUnique<iovec[]>(iov_length); - iov_length = sequencer()->GetReadableRegions(iovecs.get(), iov_length); - - sequencer()->MarkConsumed( - delegate_->OnReceived(this, iovecs.get(), iov_length, fin)); - if (sequencer()->IsClosed()) { - OnFinRead(); - } + size_t bytes_consumed = 0; + do { + bool fin = sequencer()->ReadableBytes() + sequencer()->NumBytesConsumed() == + sequencer()->close_offset(); + + // Upper bound on number of readable regions. Each complete block's worth + // of data crosses at most one region boundary. The remainder may cross one + // more boundary. Number of regions is one more than the number of region + // boundaries crossed. + size_t iov_length = sequencer()->ReadableBytes() / + QuicStreamSequencerBuffer::kBlockSizeBytes + + 2; + std::unique_ptr<iovec[]> iovecs = QuicMakeUnique<iovec[]>(iov_length); + iov_length = sequencer()->GetReadableRegions(iovecs.get(), iov_length); + + bytes_consumed = delegate_->OnReceived(this, iovecs.get(), iov_length, fin); + sequencer()->MarkConsumed(bytes_consumed); + if (sequencer()->IsClosed()) { + OnFinRead(); + break; + } + } while (bytes_consumed > 0); } void QuartcStream::OnClose() { @@ -56,8 +60,9 @@ void QuartcStream::OnClose() { void QuartcStream::OnStreamDataConsumed(size_t bytes_consumed) { QuicStream::OnStreamDataConsumed(bytes_consumed); - DCHECK(delegate_); - delegate_->OnBufferChanged(this); + if (delegate_) { + delegate_->OnBufferChanged(this); + } } void QuartcStream::OnDataBuffered( @@ -65,8 +70,9 @@ void QuartcStream::OnDataBuffered( QuicByteCount /*data_length*/, const QuicReferenceCountedPointer< QuicAckListenerInterface>& /*ack_listener*/) { - DCHECK(delegate_); - delegate_->OnBufferChanged(this); + if (delegate_) { + delegate_->OnBufferChanged(this); + } } bool QuartcStream::OnStreamFrameAcked(QuicStreamOffset offset, @@ -156,10 +162,6 @@ void QuartcStream::FinishWriting() { } void QuartcStream::SetDelegate(Delegate* delegate) { - if (delegate_) { - QUIC_LOG(WARNING) << "The delegate for Stream " << id() - << " has already been set."; - } delegate_ = delegate; DCHECK(delegate_); } diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_stream_test.cc b/chromium/net/third_party/quiche/src/quic/quartc/quartc_stream_test.cc index be23b75d5e9..eb4d4128689 100644 --- a/chromium/net/third_party/quiche/src/quic/quartc/quartc_stream_test.cc +++ b/chromium/net/third_party/quiche/src/quic/quartc/quartc_stream_test.cc @@ -55,7 +55,8 @@ class MockQuicSession : public QuicSession { : QuicSession(connection, nullptr /*visitor*/, config, - CurrentSupportedVersions()), + CurrentSupportedVersions(), + /*num_expected_unidirectional_static_streams = */ 0), write_buffer_(write_buffer) {} ~MockQuicSession() override {} @@ -93,7 +94,7 @@ class MockQuicSession : public QuicSession { const QuicCryptoStream* GetCryptoStream() const override { return nullptr; } QuicCryptoStream* GetMutableCryptoStream() override { return nullptr; } bool ShouldKeepConnectionAlive() const override { - return GetNumOpenDynamicStreams() > 0; + return GetNumActiveStreams() > 0; } // Called by QuicStream when they want to close stream. @@ -107,9 +108,9 @@ class MockQuicSession : public QuicSession { // Tracks whether the stream is write blocked and its priority. void RegisterReliableStream(QuicStreamId stream_id, spdy::SpdyPriority priority) { - write_blocked_streams()->RegisterStream(stream_id, - /*is_static_stream=*/false, - priority); + write_blocked_streams()->RegisterStream( + stream_id, + /*is_static_stream=*/false, spdy::SpdyStreamPrecedence(priority)); } // The session take ownership of the stream. diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/crypto_test_utils.cc b/chromium/net/third_party/quiche/src/quic/test_tools/crypto_test_utils.cc index 095472a7b99..27e47b2172f 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/crypto_test_utils.cc +++ b/chromium/net/third_party/quiche/src/quic/test_tools/crypto_test_utils.cc @@ -232,9 +232,6 @@ int HandshakeWithFakeServer(QuicConfig* server_quic_config, CanAcceptClientHello(testing::_, testing::_, testing::_, testing::_, testing::_)) .Times(testing::AnyNumber()); - EXPECT_CALL(*server_session.helper(), - GenerateConnectionIdForReject(testing::_, testing::_)) - .Times(testing::AnyNumber()); EXPECT_CALL(*server_conn, OnCanWrite()).Times(testing::AnyNumber()); EXPECT_CALL(*client_conn, OnCanWrite()).Times(testing::AnyNumber()); @@ -679,9 +676,13 @@ void MovePackets(PacketSavingConnection* source_conn, PacketSavingConnection* dest_conn, Perspective dest_perspective) { SimpleQuicFramer framer(source_conn->supported_versions(), dest_perspective); + QuicFramerPeer::SetLastSerializedServerConnectionId(framer.framer(), + TestConnectionId()); SimpleQuicFramer null_encryption_framer(source_conn->supported_versions(), dest_perspective); + QuicFramerPeer::SetLastSerializedServerConnectionId( + null_encryption_framer.framer(), TestConnectionId()); size_t index = *inout_packet_index; for (; index < source_conn->encrypted_packets_.size(); index++) { @@ -691,6 +692,8 @@ void MovePackets(PacketSavingConnection* source_conn, // them into |framer|, perform the decryption with them, and then swap ther // back. QuicConnectionPeer::SwapCrypters(dest_conn, framer.framer()); + QuicConnectionPeer::AddBytesReceived( + dest_conn, source_conn->encrypted_packets_[index]->length()); if (!framer.ProcessPacket(*source_conn->encrypted_packets_[index])) { // The framer will be unable to decrypt forward-secure packets sent after // the handshake is complete. Don't treat them as handshake packets. @@ -721,7 +724,11 @@ void MovePackets(PacketSavingConnection* source_conn, QuicConnectionPeer::SetCurrentPacket( dest_conn, source_conn->encrypted_packets_[index]->AsStringPiece()); for (const auto& stream_frame : framer.stream_frames()) { - dest_stream->OnStreamFrame(*stream_frame); + // Ignore stream frames that are sent on other streams in the crypto + // event. + if (stream_frame->stream_id == dest_stream->id()) { + dest_stream->OnStreamFrame(*stream_frame); + } } for (const auto& crypto_frame : framer.crypto_frames()) { dest_stream->OnCryptoFrame(*crypto_frame); diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/mock_quic_session_visitor.h b/chromium/net/third_party/quiche/src/quic/test_tools/mock_quic_session_visitor.h index 11dc24a724c..0954bb71390 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/mock_quic_session_visitor.h +++ b/chromium/net/third_party/quiche/src/quic/test_tools/mock_quic_session_visitor.h @@ -39,9 +39,6 @@ class MockQuicCryptoServerStreamHelper : public QuicCryptoServerStream::Helper { MockQuicCryptoServerStreamHelper& operator=( const MockQuicCryptoServerStreamHelper&) = delete; ~MockQuicCryptoServerStreamHelper() override; - MOCK_CONST_METHOD2(GenerateConnectionIdForReject, - QuicConnectionId(QuicTransportVersion version, - QuicConnectionId connection_id)); MOCK_CONST_METHOD5(CanAcceptClientHello, bool(const CryptoHandshakeMessage& message, const QuicSocketAddress& client_address, diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/mock_quic_time_wait_list_manager.h b/chromium/net/third_party/quiche/src/quic/test_tools/mock_quic_time_wait_list_manager.h index b46e68b1d49..6b2b6d0968f 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/mock_quic_time_wait_list_manager.h +++ b/chromium/net/third_party/quiche/src/quic/test_tools/mock_quic_time_wait_list_manager.h @@ -45,10 +45,11 @@ class MockTimeWaitListManager : public QuicTimeWaitListManager { PacketHeaderFormat header_format, std::unique_ptr<QuicPerPacketContext> packet_context)); - MOCK_METHOD7(SendVersionNegotiationPacket, + MOCK_METHOD8(SendVersionNegotiationPacket, void(QuicConnectionId server_connection_id, QuicConnectionId client_connection_id, bool ietf_quic, + bool has_length_prefix, const ParsedQuicVersionVector& supported_versions, const QuicSocketAddress& server_address, const QuicSocketAddress& client_address, diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/packet_dropping_test_writer.h b/chromium/net/third_party/quiche/src/quic/test_tools/packet_dropping_test_writer.h index 92c895627ff..4b3e8af28ea 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/packet_dropping_test_writer.h +++ b/chromium/net/third_party/quiche/src/quic/test_tools/packet_dropping_test_writer.h @@ -161,14 +161,14 @@ class PacketDroppingTestWriter : public QuicPacketWriterWrapper { uint64_t num_calls_to_write_; QuicMutex config_mutex_; - int32_t fake_packet_loss_percentage_ GUARDED_BY(config_mutex_); - int32_t fake_drop_first_n_packets_ GUARDED_BY(config_mutex_); - int32_t fake_blocked_socket_percentage_ GUARDED_BY(config_mutex_); - int32_t fake_packet_reorder_percentage_ GUARDED_BY(config_mutex_); - QuicTime::Delta fake_packet_delay_ GUARDED_BY(config_mutex_); - QuicBandwidth fake_bandwidth_ GUARDED_BY(config_mutex_); - QuicByteCount buffer_size_ GUARDED_BY(config_mutex_); - int32_t num_consecutive_packet_lost_ GUARDED_BY(config_mutex_); + int32_t fake_packet_loss_percentage_ QUIC_GUARDED_BY(config_mutex_); + int32_t fake_drop_first_n_packets_ QUIC_GUARDED_BY(config_mutex_); + int32_t fake_blocked_socket_percentage_ QUIC_GUARDED_BY(config_mutex_); + int32_t fake_packet_reorder_percentage_ QUIC_GUARDED_BY(config_mutex_); + QuicTime::Delta fake_packet_delay_ QUIC_GUARDED_BY(config_mutex_); + QuicBandwidth fake_bandwidth_ QUIC_GUARDED_BY(config_mutex_); + QuicByteCount buffer_size_ QUIC_GUARDED_BY(config_mutex_); + int32_t num_consecutive_packet_lost_ QUIC_GUARDED_BY(config_mutex_); }; } // namespace test diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/qpack_encoder_peer.cc b/chromium/net/third_party/quiche/src/quic/test_tools/qpack_encoder_peer.cc new file mode 100644 index 00000000000..9719bdb9b7a --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/test_tools/qpack_encoder_peer.cc @@ -0,0 +1,30 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/third_party/quiche/src/quic/test_tools/qpack_encoder_peer.h" + +#include "net/third_party/quiche/src/quic/core/qpack/qpack_encoder.h" + +namespace quic { +namespace test { + +// static +QpackHeaderTable* QpackEncoderPeer::header_table(QpackEncoder* encoder) { + return &encoder->header_table_; +} + +// static +uint64_t QpackEncoderPeer::maximum_blocked_streams( + const QpackEncoder* encoder) { + return encoder->maximum_blocked_streams_; +} + +// static +uint64_t QpackEncoderPeer::smallest_blocking_index( + const QpackEncoder* encoder) { + return encoder->blocking_manager_.smallest_blocking_index(); +} + +} // namespace test +} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/qpack_encoder_peer.h b/chromium/net/third_party/quiche/src/quic/test_tools/qpack_encoder_peer.h new file mode 100644 index 00000000000..2edf4274611 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/test_tools/qpack_encoder_peer.h @@ -0,0 +1,30 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef QUICHE_QUIC_TEST_TOOLS_QPACK_ENCODER_PEER_H_ +#define QUICHE_QUIC_TEST_TOOLS_QPACK_ENCODER_PEER_H_ + +#include <cstdint> + +namespace quic { + +class QpackEncoder; +class QpackHeaderTable; + +namespace test { + +class QpackEncoderPeer { + public: + QpackEncoderPeer() = delete; + + static QpackHeaderTable* header_table(QpackEncoder* encoder); + static uint64_t maximum_blocked_streams(const QpackEncoder* encoder); + static uint64_t smallest_blocking_index(const QpackEncoder* encoder); +}; + +} // namespace test + +} // namespace quic + +#endif // QUICHE_QUIC_TEST_TOOLS_QPACK_ENCODER_PEER_H_ diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/qpack_header_table_peer.cc b/chromium/net/third_party/quiche/src/quic/test_tools/qpack_header_table_peer.cc new file mode 100644 index 00000000000..bb18731dae7 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/test_tools/qpack_header_table_peer.cc @@ -0,0 +1,25 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/third_party/quiche/src/quic/test_tools/qpack_header_table_peer.h" + +#include "net/third_party/quiche/src/quic/core/qpack/qpack_header_table.h" + +namespace quic { +namespace test { + +// static +uint64_t QpackHeaderTablePeer::dynamic_table_capacity( + const QpackHeaderTable* header_table) { + return header_table->dynamic_table_capacity_; +} + +// static +uint64_t QpackHeaderTablePeer::maximum_dynamic_table_capacity( + const QpackHeaderTable* header_table) { + return header_table->maximum_dynamic_table_capacity_; +} + +} // namespace test +} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/qpack_header_table_peer.h b/chromium/net/third_party/quiche/src/quic/test_tools/qpack_header_table_peer.h new file mode 100644 index 00000000000..cbf3f448a28 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/test_tools/qpack_header_table_peer.h @@ -0,0 +1,29 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef QUICHE_QUIC_TEST_TOOLS_QPACK_HEADER_TABLE_PEER_H_ +#define QUICHE_QUIC_TEST_TOOLS_QPACK_HEADER_TABLE_PEER_H_ + +#include <cstdint> + +namespace quic { + +class QpackHeaderTable; + +namespace test { + +class QpackHeaderTablePeer { + public: + QpackHeaderTablePeer() = delete; + + static uint64_t dynamic_table_capacity(const QpackHeaderTable* header_table); + static uint64_t maximum_dynamic_table_capacity( + const QpackHeaderTable* header_table); +}; + +} // namespace test + +} // namespace quic + +#endif // QUICHE_QUIC_TEST_TOOLS_QPACK_HEADER_TABLE_PEER_H_ diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_connection_peer.cc b/chromium/net/third_party/quiche/src/quic/test_tools/quic_connection_peer.cc index 66d1a51434e..ef2ffb39c02 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/quic_connection_peer.cc +++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_connection_peer.cc @@ -332,5 +332,18 @@ void QuicConnectionPeer::SetLastHeaderFormat(QuicConnection* connection, connection->last_header_.form = format; } +// static +void QuicConnectionPeer::AddBytesReceived(QuicConnection* connection, + size_t length) { + if (connection->EnforceAntiAmplificationLimit()) { + connection->bytes_received_before_address_validation_ += length; + } +} + +// static +void QuicConnectionPeer::SetAddressValidated(QuicConnection* connection) { + connection->address_validated_ = true; +} + } // namespace test } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h b/chromium/net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h index d0797f52adf..f329a1cb833 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h +++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h @@ -133,6 +133,8 @@ class QuicConnectionPeer { QuicConnection* connection); static void SetLastHeaderFormat(QuicConnection* connection, PacketHeaderFormat format); + static void AddBytesReceived(QuicConnection* connection, size_t length); + static void SetAddressValidated(QuicConnection* connection); }; } // namespace test diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_crypto_server_config_peer.h b/chromium/net/third_party/quiche/src/quic/test_tools/quic_crypto_server_config_peer.h index 3f5a9840a33..baaa9b8bffb 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/quic_crypto_server_config_peer.h +++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_crypto_server_config_peer.h @@ -73,7 +73,7 @@ class QuicCryptoServerConfigPeer { // ConfigsDebug returns a std::string that contains debugging information // about the set of Configs loaded in |server_config_| and their status. std::string ConfigsDebug() - SHARED_LOCKS_REQUIRED(server_config_->configs_lock_); + QUIC_SHARED_LOCKS_REQUIRED(server_config_->configs_lock_); void SelectNewPrimaryConfig(int seconds); diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_framer_peer.h b/chromium/net/third_party/quiche/src/quic/test_tools/quic_framer_peer.h index 8496718fc29..24b8818a804 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/quic_framer_peer.h +++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_framer_peer.h @@ -179,6 +179,12 @@ class QuicFramerPeer { uint8_t* destination_connection_id_length, uint8_t* source_connection_id_length, std::string* detailed_error); + + static void set_current_received_frame_type( + QuicFramer* framer, + uint64_t current_received_frame_type) { + framer->current_received_frame_type_ = current_received_frame_type; + } }; } // namespace test diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_session_peer.cc b/chromium/net/third_party/quiche/src/quic/test_tools/quic_session_peer.cc index e78b0597284..ab4e5af1ebc 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/quic_session_peer.cc +++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_session_peer.cc @@ -161,8 +161,7 @@ void QuicSessionPeer::ActivateStream(QuicSession* session, // static void QuicSessionPeer::RegisterStaticStream(QuicSession* session, std::unique_ptr<QuicStream> stream) { - return session->RegisterStaticStream(std::move(stream), - /*stream_already_counted = */ false); + return session->RegisterStaticStream(std::move(stream)); } // static @@ -243,5 +242,12 @@ void QuicSessionPeer::SendRstStreamInner(QuicSession* session, session->SendRstStreamInner(id, error, bytes_written, close_write_side_only); } +// static +PendingStream* QuicSessionPeer::GetPendingStream(QuicSession* session, + QuicStreamId stream_id) { + auto it = session->pending_stream_map_.find(stream_id); + return it == session->pending_stream_map_.end() ? nullptr : it->second.get(); +} + } // namespace test } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_session_peer.h b/chromium/net/third_party/quiche/src/quic/test_tools/quic_session_peer.h index f027eb564a6..decc4b93f45 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/quic_session_peer.h +++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_session_peer.h @@ -86,6 +86,8 @@ class QuicSessionPeer { QuicRstStreamErrorCode error, QuicStreamOffset bytes_written, bool close_write_side_only); + static PendingStream* GetPendingStream(QuicSession* session, + QuicStreamId stream_id); }; } // namespace test diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.cc b/chromium/net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.cc index 68552a83a0f..85237c454e1 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.cc +++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.cc @@ -5,6 +5,7 @@ #include "net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.h" #include "net/third_party/quiche/src/quic/core/http/quic_spdy_session.h" +#include "net/third_party/quiche/src/quic/core/qpack/qpack_receive_stream.h" #include "net/third_party/quiche/src/quic/core/quic_utils.h" namespace quic { @@ -13,11 +14,13 @@ namespace test { // static QuicHeadersStream* QuicSpdySessionPeer::GetHeadersStream( QuicSpdySession* session) { + DCHECK(!VersionUsesQpack(session->connection()->transport_version())); return session->headers_stream(); } void QuicSpdySessionPeer::SetHeadersStream(QuicSpdySession* session, QuicHeadersStream* headers_stream) { + DCHECK(!VersionUsesQpack(session->connection()->transport_version())); for (auto& it : session->stream_map()) { if (it.first == QuicUtils::GetHeadersStreamId( session->connection()->transport_version())) { @@ -58,10 +61,10 @@ size_t QuicSpdySessionPeer::WriteHeadersOnHeadersStream( QuicStreamId id, spdy::SpdyHeaderBlock headers, bool fin, - spdy::SpdyPriority priority, + const spdy::SpdyStreamPrecedence& precedence, QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) { return session->WriteHeadersOnHeadersStream( - id, std::move(headers), fin, priority, std::move(ack_listener)); + id, std::move(headers), fin, precedence, std::move(ack_listener)); } // static @@ -82,5 +85,29 @@ QuicSendControlStream* QuicSpdySessionPeer::GetSendControlStream( return session->send_control_stream_; } +// static +QpackSendStream* QuicSpdySessionPeer::GetQpackDecoderSendStream( + QuicSpdySession* session) { + return session->qpack_decoder_send_stream_; +} + +// static +QpackSendStream* QuicSpdySessionPeer::GetQpackEncoderSendStream( + QuicSpdySession* session) { + return session->qpack_encoder_send_stream_; +} + +// static +QpackReceiveStream* QuicSpdySessionPeer::GetQpackDecoderReceiveStream( + QuicSpdySession* session) { + return session->qpack_decoder_receive_stream_; +} + +// static +QpackReceiveStream* QuicSpdySessionPeer::GetQpackEncoderReceiveStream( + QuicSpdySession* session) { + return session->qpack_encoder_receive_stream_; +} + } // namespace test } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.h b/chromium/net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.h index aacf712ae2f..1cfd45f252c 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.h +++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.h @@ -7,6 +7,8 @@ #include "net/third_party/quiche/src/quic/core/http/quic_receive_control_stream.h" #include "net/third_party/quiche/src/quic/core/http/quic_send_control_stream.h" +#include "net/third_party/quiche/src/quic/core/qpack/qpack_receive_stream.h" +#include "net/third_party/quiche/src/quic/core/qpack/qpack_send_stream.h" #include "net/third_party/quiche/src/quic/core/quic_packets.h" #include "net/third_party/quiche/src/quic/core/quic_write_blocked_list.h" #include "net/third_party/quiche/src/spdy/core/spdy_framer.h" @@ -41,7 +43,7 @@ class QuicSpdySessionPeer { QuicStreamId id, spdy::SpdyHeaderBlock headers, bool fin, - spdy::SpdyPriority priority, + const spdy::SpdyStreamPrecedence& precedence, QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener); // |session| can't be nullptr. static QuicStreamId GetNextOutgoingUnidirectionalStreamId( @@ -49,6 +51,12 @@ class QuicSpdySessionPeer { static QuicReceiveControlStream* GetReceiveControlStream( QuicSpdySession* session); static QuicSendControlStream* GetSendControlStream(QuicSpdySession* session); + static QpackSendStream* GetQpackDecoderSendStream(QuicSpdySession* session); + static QpackSendStream* GetQpackEncoderSendStream(QuicSpdySession* session); + static QpackReceiveStream* GetQpackDecoderReceiveStream( + QuicSpdySession* session); + static QpackReceiveStream* GetQpackEncoderReceiveStream( + QuicSpdySession* session); }; } // namespace test diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_client.cc b/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_client.cc index e2e33c7a3bf..402a2311882 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_client.cc +++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_client.cc @@ -233,9 +233,14 @@ MockableQuicClient::mockable_network_helper() const { } QuicConnectionId MockableQuicClient::GenerateNewConnectionId() { - return server_connection_id_overridden_ - ? override_server_connection_id_ - : QuicClient::GenerateNewConnectionId(); + if (server_connection_id_overridden_) { + return override_server_connection_id_; + } + if (override_server_connection_id_length_ >= 0) { + return QuicUtils::CreateRandomConnectionId( + override_server_connection_id_length_); + } + return QuicClient::GenerateNewConnectionId(); } void MockableQuicClient::UseConnectionId( @@ -244,9 +249,20 @@ void MockableQuicClient::UseConnectionId( override_server_connection_id_ = server_connection_id; } +void MockableQuicClient::UseConnectionIdLength( + int server_connection_id_length) { + override_server_connection_id_length_ = server_connection_id_length; +} + QuicConnectionId MockableQuicClient::GetClientConnectionId() { - return client_connection_id_overridden_ ? override_client_connection_id_ - : QuicClient::GetClientConnectionId(); + if (client_connection_id_overridden_) { + return override_client_connection_id_; + } + if (override_client_connection_id_length_ >= 0) { + return QuicUtils::CreateRandomConnectionId( + override_client_connection_id_length_); + } + return QuicClient::GetClientConnectionId(); } void MockableQuicClient::UseClientConnectionId( @@ -255,6 +271,11 @@ void MockableQuicClient::UseClientConnectionId( override_client_connection_id_ = client_connection_id; } +void MockableQuicClient::UseClientConnectionIdLength( + int client_connection_id_length) { + override_client_connection_id_length_ = client_connection_id_length; +} + void MockableQuicClient::UseWriter(QuicPacketWriterWrapper* writer) { mockable_network_helper()->UseWriter(writer); } @@ -526,7 +547,8 @@ QuicSpdyClientStream* QuicTestClient::GetOrCreateStream() { if (!latest_created_stream_) { SetLatestCreatedStream(client_->CreateClientStream()); if (latest_created_stream_) { - latest_created_stream_->SetPriority(priority_); + latest_created_stream_->SetPriority( + spdy::SpdyStreamPrecedence(priority_)); } } @@ -769,12 +791,23 @@ void QuicTestClient::UseConnectionId(QuicConnectionId server_connection_id) { client_->UseConnectionId(server_connection_id); } +void QuicTestClient::UseConnectionIdLength(int server_connection_id_length) { + DCHECK(!connected()); + client_->UseConnectionIdLength(server_connection_id_length); +} + void QuicTestClient::UseClientConnectionId( QuicConnectionId client_connection_id) { DCHECK(!connected()); client_->UseClientConnectionId(client_connection_id); } +void QuicTestClient::UseClientConnectionIdLength( + int client_connection_id_length) { + DCHECK(!connected()); + client_->UseClientConnectionIdLength(client_connection_id_length); +} + bool QuicTestClient::MigrateSocket(const QuicIpAddress& new_host) { return client_->MigrateSocket(new_host); } diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_client.h b/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_client.h index 7b835620557..cd5b09f7d77 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_client.h +++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_client.h @@ -56,8 +56,10 @@ class MockableQuicClient : public QuicClient { QuicConnectionId GenerateNewConnectionId() override; void UseConnectionId(QuicConnectionId server_connection_id); + void UseConnectionIdLength(int server_connection_id_length); QuicConnectionId GetClientConnectionId() override; void UseClientConnectionId(QuicConnectionId client_connection_id); + void UseClientConnectionIdLength(int client_connection_id_length); void UseWriter(QuicPacketWriterWrapper* writer); void set_peer_address(const QuicSocketAddress& address); @@ -74,9 +76,11 @@ class MockableQuicClient : public QuicClient { // Server connection ID to use, if server_connection_id_overridden_ QuicConnectionId override_server_connection_id_; bool server_connection_id_overridden_; + int override_server_connection_id_length_ = -1; // Client connection ID to use, if client_connection_id_overridden_ QuicConnectionId override_client_connection_id_; bool client_connection_id_overridden_; + int override_client_connection_id_length_ = -1; CachedNetworkParameters cached_network_paramaters_; }; @@ -227,9 +231,15 @@ class QuicTestClient : public QuicSpdyStream::Visitor, // Configures client_ to use a specific server connection ID instead of a // random one. void UseConnectionId(QuicConnectionId server_connection_id); + // Configures client_ to use a specific server connection ID length instead + // of the default of kQuicDefaultConnectionIdLength. + void UseConnectionIdLength(int server_connection_id_length); // Configures client_ to use a specific client connection ID instead of an // empty one. void UseClientConnectionId(QuicConnectionId client_connection_id); + // Configures client_ to use a specific client connection ID length instead + // of the default of zero. + void UseClientConnectionIdLength(int client_connection_id_length); // Returns nullptr if the maximum number of streams have already been created. QuicSpdyClientStream* GetOrCreateStream(); diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_server.cc b/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_server.cc index 68c500e67b7..19b986e8dba 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_server.cc +++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_server.cc @@ -184,7 +184,7 @@ QuicDispatcher* QuicTestServer::CreateQuicDispatcher() { QuicMakeUnique<QuicEpollConnectionHelper>(epoll_server(), QuicAllocator::BUFFER_POOL), std::unique_ptr<QuicCryptoServerStream::Helper>( - new QuicSimpleCryptoServerStreamHelper(QuicRandom::GetInstance())), + new QuicSimpleCryptoServerStreamHelper()), QuicMakeUnique<QuicEpollAlarmFactory>(epoll_server()), server_backend(), expected_server_connection_id_length()); } diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_utils.cc b/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_utils.cc index 99c7511daee..5ff5e05a9b0 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_utils.cc +++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_utils.cc @@ -259,6 +259,11 @@ bool NoOpFramerVisitor::OnPacketHeader(const QuicPacketHeader& /*header*/) { void NoOpFramerVisitor::OnCoalescedPacket( const QuicEncryptedPacket& /*packet*/) {} +void NoOpFramerVisitor::OnUndecryptablePacket( + const QuicEncryptedPacket& /*packet*/, + EncryptionLevel /*decryption_level*/, + bool /*has_decryption_key*/) {} + bool NoOpFramerVisitor::OnStreamFrame(const QuicStreamFrame& /*frame*/) { return true; } @@ -524,7 +529,8 @@ MockQuicSession::MockQuicSession(QuicConnection* connection, : QuicSession(connection, nullptr, DefaultQuicConfig(), - connection->supported_versions()) { + connection->supported_versions(), + /*num_expected_unidirectional_static_streams = */ 0) { if (create_mock_crypto_stream) { crypto_stream_ = QuicMakeUnique<MockQuicCryptoStream>(this); } @@ -633,9 +639,6 @@ TestQuicSpdyServerSession::TestQuicSpdyServerSession( crypto_config, compressed_certs_cache) { Initialize(); - ON_CALL(helper_, GenerateConnectionIdForReject(_, _)) - .WillByDefault(testing::Return( - QuicUtils::CreateRandomConnectionId(connection->random_generator()))); ON_CALL(helper_, CanAcceptClientHello(_, _, _, _, _)) .WillByDefault(testing::Return(true)); } @@ -1158,8 +1161,8 @@ QuicStreamId GetNthClientInitiatedBidirectionalStreamId( QuicTransportVersion version, int n) { int num = n; - if (!VersionLacksHeadersStream(version)) { - num++; // + 1 because spdy_session contains headers stream. + if (!VersionUsesQpack(version)) { + num++; } return QuicUtils::GetFirstBidirectionalStreamId(version, Perspective::IS_CLIENT) + diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_utils.h b/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_utils.h index 20494d7f3f0..5750b9bc61d 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_utils.h +++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_utils.h @@ -261,6 +261,10 @@ class MockFramerVisitor : public QuicFramerVisitorInterface { MOCK_METHOD1(OnDecryptedPacket, void(EncryptionLevel level)); MOCK_METHOD1(OnPacketHeader, bool(const QuicPacketHeader& header)); MOCK_METHOD1(OnCoalescedPacket, void(const QuicEncryptedPacket& packet)); + MOCK_METHOD3(OnUndecryptablePacket, + void(const QuicEncryptedPacket& packet, + EncryptionLevel decryption_level, + bool has_decryption_key)); MOCK_METHOD1(OnStreamFrame, bool(const QuicStreamFrame& frame)); MOCK_METHOD1(OnCryptoFrame, bool(const QuicCryptoFrame& frame)); MOCK_METHOD2(OnAckFrameStart, bool(QuicPacketNumber, QuicTime::Delta)); @@ -314,6 +318,9 @@ class NoOpFramerVisitor : public QuicFramerVisitorInterface { void OnDecryptedPacket(EncryptionLevel /*level*/) override {} bool OnPacketHeader(const QuicPacketHeader& header) override; void OnCoalescedPacket(const QuicEncryptedPacket& packet) override; + void OnUndecryptablePacket(const QuicEncryptedPacket& packet, + EncryptionLevel decryption_level, + bool has_decryption_key) override; bool OnStreamFrame(const QuicStreamFrame& frame) override; bool OnCryptoFrame(const QuicCryptoFrame& frame) override; bool OnAckFrameStart(QuicPacketNumber largest_acked, @@ -375,9 +382,10 @@ class MockQuicConnectionVisitor : public QuicConnectionVisitorInterface { MOCK_CONST_METHOD0(ShouldKeepConnectionAlive, bool()); MOCK_METHOD1(OnSuccessfulVersionNegotiation, void(const ParsedQuicVersion& version)); - MOCK_METHOD2(OnConnectivityProbeReceived, + MOCK_METHOD3(OnPacketReceived, void(const QuicSocketAddress& self_address, - const QuicSocketAddress& peer_address)); + const QuicSocketAddress& peer_address, + bool is_connectivity_probe)); MOCK_METHOD0(OnConfigNegotiated, void()); MOCK_METHOD0(OnAckNeedsRetransmittableFrame, void()); MOCK_METHOD0(SendPing, void()); @@ -624,6 +632,12 @@ class MockQuicSession : public QuicSession { MOCK_CONST_METHOD0(IsCryptoHandshakeConfirmed, bool()); MOCK_CONST_METHOD0(ShouldKeepConnectionAlive, bool()); MOCK_METHOD2(SendStopSending, void(uint16_t code, QuicStreamId stream_id)); + MOCK_METHOD1(OnCryptoHandshakeEvent, void(QuicSession::CryptoHandshakeEvent)); + MOCK_CONST_METHOD0(GetAlpnsToOffer, std::vector<std::string>()); + MOCK_CONST_METHOD1(SelectAlpn, + std::vector<QuicStringPiece>::const_iterator( + const std::vector<QuicStringPiece>&)); + MOCK_METHOD1(OnAlpnSelected, void(QuicStringPiece)); using QuicSession::ActivateStream; @@ -676,6 +690,8 @@ class MockQuicSpdySession : public QuicSpdySession { QuicSession::OnConnectionClosed(frame, source); } + using QuicSession::RegisterStaticStream; + // From QuicSession. MOCK_METHOD2(OnConnectionClosed, void(const QuicConnectionCloseFrame& frame, @@ -702,7 +718,8 @@ class MockQuicSpdySession : public QuicSpdySession { MOCK_METHOD2(OnStreamHeaders, void(QuicStreamId stream_id, QuicStringPiece headers_data)); MOCK_METHOD2(OnStreamHeadersPriority, - void(QuicStreamId stream_id, spdy::SpdyPriority priority)); + void(QuicStreamId stream_id, + const spdy::SpdyStreamPrecedence& precedence)); MOCK_METHOD3(OnStreamHeadersComplete, void(QuicStreamId stream_id, bool fin, size_t frame_len)); MOCK_METHOD4(OnStreamHeaderList, @@ -723,7 +740,8 @@ class MockQuicSpdySession : public QuicSpdySession { size_t frame_len, const QuicHeaderList& header_list)); MOCK_METHOD2(OnPriorityFrame, - void(QuicStreamId id, spdy::SpdyPriority priority)); + void(QuicStreamId id, + const spdy::SpdyStreamPrecedence& precedence)); MOCK_METHOD1(OnHeadersHeadOfLineBlocking, void(QuicTime::Delta delta)); MOCK_METHOD4( diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/server_thread.h b/chromium/net/third_party/quiche/src/quic/test_tools/server_thread.h index ffbce7f15a9..d430297f30b 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/server_thread.h +++ b/chromium/net/third_party/quiche/src/quic/test_tools/server_thread.h @@ -73,13 +73,13 @@ class ServerThread : public QuicThread { std::unique_ptr<QuicServer> server_; QuicSocketAddress address_; mutable QuicMutex port_lock_; - int port_ GUARDED_BY(port_lock_); + int port_ QUIC_GUARDED_BY(port_lock_); bool initialized_; QuicMutex scheduled_actions_lock_; QuicDeque<std::function<void()>> scheduled_actions_ - GUARDED_BY(scheduled_actions_lock_); + QUIC_GUARDED_BY(scheduled_actions_lock_); }; } // namespace test diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/simple_quic_framer.cc b/chromium/net/third_party/quiche/src/quic/test_tools/simple_quic_framer.cc index 547f6d26c8d..ad7e7bb32a1 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/simple_quic_framer.cc +++ b/chromium/net/third_party/quiche/src/quic/test_tools/simple_quic_framer.cc @@ -60,6 +60,10 @@ class SimpleFramerVisitor : public QuicFramerVisitorInterface { void OnCoalescedPacket(const QuicEncryptedPacket& /*packet*/) override {} + void OnUndecryptablePacket(const QuicEncryptedPacket& /*packet*/, + EncryptionLevel /*decryption_level*/, + bool /*has_decryption_key*/) override {} + bool OnStreamFrame(const QuicStreamFrame& frame) override { // Save a copy of the data so it is valid after the packet is processed. std::string* string_data = diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/simple_session_notifier.cc b/chromium/net/third_party/quiche/src/quic/test_tools/simple_session_notifier.cc index 16c441c8c32..72a23d73178 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/simple_session_notifier.cc +++ b/chromium/net/third_party/quiche/src/quic/test_tools/simple_session_notifier.cc @@ -175,6 +175,14 @@ void SimpleSessionNotifier::OnCanWrite() { } } +void SimpleSessionNotifier::OnStreamReset(QuicStreamId id, + QuicRstStreamErrorCode error) { + if (error != QUIC_STREAM_NO_ERROR) { + // Delete stream to avoid retransmissions. + stream_map_.erase(id); + } +} + bool SimpleSessionNotifier::WillingToWrite() const { QUIC_DVLOG(1) << "has_buffered_control_frames: " << HasBufferedControlFrames() << " as_lost_control_frames: " << !lost_control_frames_.empty() diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/simple_session_notifier.h b/chromium/net/third_party/quiche/src/quic/test_tools/simple_session_notifier.h index 74240539ae7..b3d5d729e82 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/simple_session_notifier.h +++ b/chromium/net/third_party/quiche/src/quic/test_tools/simple_session_notifier.h @@ -45,6 +45,9 @@ class SimpleSessionNotifier : public SessionNotifierInterface { // Called when connection_ becomes writable. void OnCanWrite(); + // Called to reset stream. + void OnStreamReset(QuicStreamId id, QuicRstStreamErrorCode error); + // Returns true if there are 1) unsent control frames and stream data, or 2) // lost control frames and stream data. bool WillingToWrite() const; diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/simple_session_notifier_test.cc b/chromium/net/third_party/quiche/src/quic/test_tools/simple_session_notifier_test.cc index 8b931fed34d..63da6397209 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/simple_session_notifier_test.cc +++ b/chromium/net/third_party/quiche/src/quic/test_tools/simple_session_notifier_test.cc @@ -251,8 +251,10 @@ TEST_F(SimpleSessionNotifierTest, OnCanWriteCryptoFrames) { .WillOnce(Invoke(&connection_, &MockQuicConnection::QuicConnection_SendCryptoData)); EXPECT_CALL(connection_, CloseConnection(QUIC_PACKET_WRITE_ERROR, _, _)); - producer.SaveCryptoData(ENCRYPTION_INITIAL, 0, std::string(1024, 'a')); - producer.SaveCryptoData(ENCRYPTION_INITIAL, 500, std::string(524, 'a')); + std::string crypto_data1(1024, 'a'); + producer.SaveCryptoData(ENCRYPTION_INITIAL, 0, crypto_data1); + std::string crypto_data2(524, 'a'); + producer.SaveCryptoData(ENCRYPTION_INITIAL, 500, crypto_data2); notifier_.WriteCryptoData(ENCRYPTION_INITIAL, 1024, 0); // Send crypto data [1024, 2048) in ENCRYPTION_ZERO_RTT. connection_.SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT); @@ -261,7 +263,8 @@ TEST_F(SimpleSessionNotifierTest, OnCanWriteCryptoFrames) { EXPECT_CALL(connection_, SendCryptoData(ENCRYPTION_ZERO_RTT, 1024, 0)) .WillOnce(Invoke(&connection_, &MockQuicConnection::QuicConnection_SendCryptoData)); - producer.SaveCryptoData(ENCRYPTION_ZERO_RTT, 0, std::string(1024, 'a')); + std::string crypto_data3(1024, 'a'); + producer.SaveCryptoData(ENCRYPTION_ZERO_RTT, 0, crypto_data3); notifier_.WriteCryptoData(ENCRYPTION_ZERO_RTT, 1024, 0); // Send stream 3 [0, 1024) and connection is blocked. EXPECT_CALL(connection_, SendStreamData(3, 1024, 0, FIN)) diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/simulator/quic_endpoint.h b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/quic_endpoint.h index 7481ddc6b1f..43fce53bb4c 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/simulator/quic_endpoint.h +++ b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/quic_endpoint.h @@ -97,9 +97,9 @@ class QuicEndpoint : public Endpoint, void OnWriteBlocked() override {} void OnSuccessfulVersionNegotiation( const ParsedQuicVersion& /*version*/) override {} - void OnConnectivityProbeReceived( - const QuicSocketAddress& /*self_address*/, - const QuicSocketAddress& /*peer_address*/) override {} + void OnPacketReceived(const QuicSocketAddress& /*self_address*/, + const QuicSocketAddress& /*peer_address*/, + bool /*is_connectivity_probe*/) override {} void OnCongestionWindowChange(QuicTime /*now*/) override {} void OnConnectionMigration(AddressChangeType /*type*/) override {} void OnPathDegrading() override {} diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_client.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_client.cc index e7fe6b4830e..123cf1ee30f 100644 --- a/chromium/net/third_party/quiche/src/quic/tools/quic_client.cc +++ b/chromium/net/third_party/quiche/src/quic/tools/quic_client.cc @@ -5,10 +5,12 @@ #include "net/third_party/quiche/src/quic/tools/quic_client.h" #include <errno.h> +#include <netdb.h> #include <netinet/in.h> #include <string.h> #include <sys/epoll.h> #include <sys/socket.h> +#include <sys/types.h> #include "net/third_party/quiche/src/quic/core/crypto/quic_random.h" #include "net/third_party/quiche/src/quic/core/http/spdy_utils.h" @@ -21,6 +23,7 @@ #include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" #include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h" #include "net/quic/platform/impl/quic_socket_utils.h" #include "net/third_party/quiche/src/quic/tools/quic_simple_client_session.h" @@ -30,6 +33,29 @@ namespace quic { +namespace tools { + +QuicSocketAddress LookupAddress(std::string host, std::string port) { + addrinfo hint; + memset(&hint, 0, sizeof(hint)); + hint.ai_protocol = IPPROTO_UDP; + + addrinfo* info_list = nullptr; + int result = getaddrinfo(host.c_str(), port.c_str(), &hint, &info_list); + if (result != 0) { + QUIC_LOG(ERROR) << "Failed to look up " << host << ": " + << gai_strerror(result); + return QuicSocketAddress(); + } + + CHECK(info_list != nullptr); + std::unique_ptr<addrinfo, void (*)(addrinfo*)> info_list_owned(info_list, + freeaddrinfo); + return QuicSocketAddress(info_list->ai_addr, info_list->ai_addrlen); +} + +} // namespace tools + QuicClient::QuicClient(QuicSocketAddress server_address, const QuicServerId& server_id, const ParsedQuicVersionVector& supported_versions, diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_client.h b/chromium/net/third_party/quiche/src/quic/tools/quic_client.h index e5d21700418..8e43be8498b 100644 --- a/chromium/net/third_party/quiche/src/quic/tools/quic_client.h +++ b/chromium/net/third_party/quiche/src/quic/tools/quic_client.h @@ -29,6 +29,12 @@ namespace test { class QuicClientPeer; } // namespace test +namespace tools { + +QuicSocketAddress LookupAddress(std::string host, std::string port); + +} // namespace tools + class QuicClient : public QuicSpdyClientBase { public: // This will create its own QuicClientEpollNetworkHelper. diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_client_interop_test_bin.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_client_interop_test_bin.cc new file mode 100644 index 00000000000..39f8ff9abe7 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/tools/quic_client_interop_test_bin.cc @@ -0,0 +1,164 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <iostream> +#include <memory> +#include <string> + +#include "net/third_party/quiche/src/quic/core/quic_versions.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_epoll.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_system_event_loop.h" +#include "net/third_party/quiche/src/quic/tools/fake_proof_verifier.h" +#include "net/third_party/quiche/src/quic/tools/quic_client.h" + +DEFINE_QUIC_COMMAND_LINE_FLAG(std::string, + host, + "", + "The IP or hostname to connect to."); + +DEFINE_QUIC_COMMAND_LINE_FLAG(int32_t, port, 0, "The port to connect to."); + +namespace quic { + +enum class Feature { + // A version negotiation response is elicited and acted on. + kVersionNegotiation, + // The handshake completes successfully. + kHandshake, + // Stream data is being exchanged and ACK'ed. + kStreamData, + // The connection close procedcure completes with a zero error code. + kConnectionClose, + // An H3 transaction succeeded. + kHttp3, + // TODO(nharper): Add Retry to list of tested features. +}; + +char MatrixLetter(Feature f) { + switch (f) { + case Feature::kVersionNegotiation: + return 'V'; + case Feature::kHandshake: + return 'H'; + case Feature::kStreamData: + return 'D'; + case Feature::kConnectionClose: + return 'C'; + case Feature::kHttp3: + return '3'; + } +} + +std::set<Feature> AttemptRequest(QuicSocketAddress addr, + std::string authority, + QuicServerId server_id, + ParsedQuicVersionVector versions) { + std::set<Feature> features; + auto proof_verifier = QuicMakeUnique<FakeProofVerifier>(); + QuicEpollServer epoll_server; + auto client = QuicMakeUnique<QuicClient>( + addr, server_id, versions, &epoll_server, std::move(proof_verifier)); + if (!client->Initialize()) { + return features; + } + if (!client->Connect()) { + QuicErrorCode error = client->session()->error(); + if (error == QUIC_INVALID_VERSION) { + // QuicFramer::ProcessPacket returns RaiseError(QUIC_INVALID_VERSION) if + // it receives a packet containing a version in the header that is not our + // version. It might be possible that we didn't actually process a VN + // packet here. + features.insert(Feature::kVersionNegotiation); + return features; + } + return features; + } + if (!client->session()->IsCryptoHandshakeConfirmed()) { + return features; + } + features.insert(Feature::kHandshake); + + // Construct and send a request. + spdy::SpdyHeaderBlock header_block; + header_block[":method"] = "GET"; + header_block[":scheme"] = "https"; + header_block[":authority"] = authority; + header_block[":path"] = "/"; + client->set_store_response(true); + client->SendRequest(header_block, "", /*fin=*/true); + + // TODO(nharper): After some period of time, time out and don't report + // success. + while (client->WaitForEvents()) { + } + + if (!client->connected()) { + return features; + } + + if (client->latest_response_code() != -1) { + features.insert(Feature::kHttp3); + } + + // TODO(nharper): Properly check that we actually sent stream data and + // received ACKs for it. + features.insert(Feature::kStreamData); + // TODO(nharper): Check that we sent/received (which one?) a CONNECTION_CLOSE + // with error code 0. + features.insert(Feature::kConnectionClose); + return features; +} + +std::set<Feature> ServerSupport(std::string host, int port) { + // Configure version list. + QuicVersionInitializeSupportForIetfDraft(); + ParsedQuicVersion version = + ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_99); + ParsedQuicVersionVector versions = {version}; + QuicEnableVersion(version); + + // Build the client, and try to connect. + QuicSocketAddress addr = tools::LookupAddress(host, QuicStrCat(port)); + QuicServerId server_id(host, port, false); + std::string authority = QuicStrCat(host, ":", port); + + ParsedQuicVersionVector versions_with_negotiation = versions; + versions_with_negotiation.insert(versions_with_negotiation.begin(), + QuicVersionReservedForNegotiation()); + auto supported_features = + AttemptRequest(addr, authority, server_id, versions_with_negotiation); + if (!supported_features.empty()) { + supported_features.insert(Feature::kVersionNegotiation); + } else { + supported_features = AttemptRequest(addr, authority, server_id, versions); + } + return supported_features; +} + +} // namespace quic + +int main(int argc, char* argv[]) { + QuicSystemEventLoop event_loop("quic_client"); + const char* usage = "Usage: quic_client_interop_test [options]"; + + std::vector<std::string> args = + quic::QuicParseCommandLineFlags(usage, argc, argv); + if (!args.empty()) { + quic::QuicPrintCommandLineFlagHelp(usage); + exit(1); + } + std::string host = GetQuicFlag(FLAGS_host); + int port = GetQuicFlag(FLAGS_port); + if (host.empty() || port == 0) { + quic::QuicPrintCommandLineFlagHelp(usage); + exit(1); + } + + auto supported_features = quic::ServerSupport(host, port); + std::cout << "Supported features: "; + for (auto feature : supported_features) { + std::cout << MatrixLetter(feature); + } + std::cout << std::endl; +} diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_client_test.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_client_test.cc index 12a687c1671..e2d7f1413c8 100644 --- a/chromium/net/third_party/quiche/src/quic/tools/quic_client_test.cc +++ b/chromium/net/third_party/quiche/src/quic/tools/quic_client_test.cc @@ -52,43 +52,49 @@ size_t NumOpenSocketFDs() { return socket_count; } -// Creates a new QuicClient and Initializes it. Caller is responsible for -// deletion. -std::unique_ptr<QuicClient> CreateAndInitializeQuicClient(QuicEpollServer* eps, - uint16_t port) { - QuicSocketAddress server_address(QuicSocketAddress(TestLoopback(), port)); - QuicServerId server_id("hostname", server_address.port(), false); - ParsedQuicVersionVector versions = AllSupportedVersions(); - auto client = - QuicMakeUnique<QuicClient>(server_address, server_id, versions, eps, - crypto_test_utils::ProofVerifierForTesting()); - EXPECT_TRUE(client->Initialize()); - return client; -} +class QuicClientTest : public QuicTest { + public: + QuicClientTest() { + // Creates and destroys a single client first which may open persistent + // sockets when initializing platform dependencies like certificate + // verifier. Future creation of addtional clients will deterministically + // open one socket per client. + CreateAndInitializeQuicClient(); + } + + // Creates a new QuicClient and Initializes it on an unused port. + // Caller is responsible for deletion. + std::unique_ptr<QuicClient> CreateAndInitializeQuicClient() { + uint16_t port = QuicPickServerPortForTestsOrDie(); + QuicSocketAddress server_address(QuicSocketAddress(TestLoopback(), port)); + QuicServerId server_id("hostname", server_address.port(), false); + ParsedQuicVersionVector versions = AllSupportedVersions(); + auto client = QuicMakeUnique<QuicClient>( + server_address, server_id, versions, &epoll_server_, + crypto_test_utils::ProofVerifierForTesting()); + EXPECT_TRUE(client->Initialize()); + return client; + } -class QuicClientTest : public QuicTest {}; + private: + QuicEpollServer epoll_server_; +}; TEST_F(QuicClientTest, DoNotLeakSocketFDs) { // Make sure that the QuicClient doesn't leak socket FDs. Doing so could cause // port exhaustion in long running processes which repeatedly create clients. - // Record initial number of FDs, after creating EpollServer and creating and - // destroying a single client (the latter is needed since initializing - // platform dependencies like certificate verifier may open a persistent - // socket). - QuicEpollServer eps; - CreateAndInitializeQuicClient(&eps, QuicPickUnusedPortOrDie()); + // Record the initial number of FDs. size_t number_of_open_fds = NumOpenSocketFDs(); // Create a number of clients, initialize them, and verify this has resulted // in additional FDs being opened. const int kNumClients = 50; for (int i = 0; i < kNumClients; ++i) { - std::unique_ptr<QuicClient> client( - CreateAndInitializeQuicClient(&eps, QuicPickUnusedPortOrDie())); - + EXPECT_EQ(number_of_open_fds, NumOpenSocketFDs()); + std::unique_ptr<QuicClient> client(CreateAndInitializeQuicClient()); // Initializing the client will create a new FD. - EXPECT_LT(number_of_open_fds, NumOpenSocketFDs()); + EXPECT_EQ(number_of_open_fds + 1, NumOpenSocketFDs()); } // The FDs created by the QuicClients should now be closed. @@ -96,12 +102,12 @@ TEST_F(QuicClientTest, DoNotLeakSocketFDs) { } TEST_F(QuicClientTest, CreateAndCleanUpUDPSockets) { - QuicEpollServer eps; size_t number_of_open_fds = NumOpenSocketFDs(); - std::unique_ptr<QuicClient> client( - CreateAndInitializeQuicClient(&eps, QuicPickUnusedPortOrDie())); + std::unique_ptr<QuicClient> client(CreateAndInitializeQuicClient()); + // Creating and initializing a client will result in one socket being opened. EXPECT_EQ(number_of_open_fds + 1, NumOpenSocketFDs()); + // Create more UDP sockets. EXPECT_TRUE(QuicClientPeer::CreateUDPSocketAndBind(client.get())); EXPECT_EQ(number_of_open_fds + 2, NumOpenSocketFDs()); diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_epoll_client_factory.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_epoll_client_factory.cc index a74fba36397..7cfb00a62b2 100644 --- a/chromium/net/third_party/quiche/src/quic/tools/quic_epoll_client_factory.cc +++ b/chromium/net/third_party/quiche/src/quic/tools/quic_epoll_client_factory.cc @@ -14,36 +14,14 @@ namespace quic { -namespace { - -QuicSocketAddress LookupAddress(std::string host, std::string port) { - addrinfo hint; - memset(&hint, 0, sizeof(hint)); - hint.ai_protocol = IPPROTO_UDP; - - addrinfo* info_list = nullptr; - int result = getaddrinfo(host.c_str(), port.c_str(), &hint, &info_list); - if (result != 0) { - QUIC_LOG(ERROR) << "Failed to look up " << host << ": " - << gai_strerror(result); - return QuicSocketAddress(); - } - - CHECK(info_list != nullptr); - std::unique_ptr<addrinfo, void (*)(addrinfo*)> info_list_owned(info_list, - freeaddrinfo); - return QuicSocketAddress(info_list->ai_addr, info_list->ai_addrlen); -} - -} // namespace - std::unique_ptr<QuicSpdyClientBase> QuicEpollClientFactory::CreateClient( std::string host_for_handshake, std::string host_for_lookup, uint16_t port, ParsedQuicVersionVector versions, std::unique_ptr<ProofVerifier> verifier) { - QuicSocketAddress addr = LookupAddress(host_for_lookup, QuicStrCat(port)); + QuicSocketAddress addr = + tools::LookupAddress(host_for_lookup, QuicStrCat(port)); if (!addr.IsInitialized()) { QUIC_LOG(ERROR) << "Unable to resolve address: " << host_for_lookup; return nullptr; diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_memory_cache_backend.h b/chromium/net/third_party/quiche/src/quic/tools/quic_memory_cache_backend.h index ff76c9ad41b..8e202c04399 100644 --- a/chromium/net/third_party/quiche/src/quic/tools/quic_memory_cache_backend.h +++ b/chromium/net/third_party/quiche/src/quic/tools/quic_memory_cache_backend.h @@ -177,15 +177,15 @@ class QuicMemoryCacheBackend : public QuicSimpleServerBackend { // Cached responses. QuicUnorderedMap<std::string, std::unique_ptr<QuicBackendResponse>> responses_ - GUARDED_BY(response_mutex_); + QUIC_GUARDED_BY(response_mutex_); // The default response for cache misses, if set. std::unique_ptr<QuicBackendResponse> default_response_ - GUARDED_BY(response_mutex_); + QUIC_GUARDED_BY(response_mutex_); // A map from request URL to associated server push responses (if any). std::multimap<std::string, QuicBackendResponse::ServerPushInfo> - server_push_resources_ GUARDED_BY(response_mutex_); + server_push_resources_ QUIC_GUARDED_BY(response_mutex_); // Protects against concurrent access from test threads setting responses, and // server threads accessing those responses. diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_packet_printer_bin.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_packet_printer_bin.cc index a87f10eadf1..7738ea7938f 100644 --- a/chromium/net/third_party/quiche/src/quic/tools/quic_packet_printer_bin.cc +++ b/chromium/net/third_party/quiche/src/quic/tools/quic_packet_printer_bin.cc @@ -88,6 +88,11 @@ class QuicPacketPrinter : public QuicFramerVisitorInterface { void OnCoalescedPacket(const QuicEncryptedPacket& /*packet*/) override { std::cerr << "OnCoalescedPacket\n"; } + void OnUndecryptablePacket(const QuicEncryptedPacket& /*packet*/, + EncryptionLevel /*decryption_level*/, + bool /*has_decryption_key*/) override { + std::cerr << "OnUndecryptablePacket\n"; + } bool OnStreamFrame(const QuicStreamFrame& frame) override { std::cerr << "OnStreamFrame: " << frame; std::cerr << " data: { " diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_server.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_server.cc index 8820040e565..46b04a5dadc 100644 --- a/chromium/net/third_party/quiche/src/quic/tools/quic_server.cc +++ b/chromium/net/third_party/quiche/src/quic/tools/quic_server.cc @@ -155,7 +155,7 @@ QuicDispatcher* QuicServer::CreateQuicDispatcher() { std::unique_ptr<QuicEpollConnectionHelper>(new QuicEpollConnectionHelper( &epoll_server_, QuicAllocator::BUFFER_POOL)), std::unique_ptr<QuicCryptoServerStream::Helper>( - new QuicSimpleCryptoServerStreamHelper(QuicRandom::GetInstance())), + new QuicSimpleCryptoServerStreamHelper()), std::unique_ptr<QuicEpollAlarmFactory>( new QuicEpollAlarmFactory(&epoll_server_)), quic_simple_server_backend_, expected_server_connection_id_length_); diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_server_test.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_server_test.cc index b1a88a6f13f..8c894434dee 100644 --- a/chromium/net/third_party/quiche/src/quic/tools/quic_server_test.cc +++ b/chromium/net/third_party/quiche/src/quic/tools/quic_server_test.cc @@ -72,7 +72,7 @@ class TestQuicServer : public QuicServer { new QuicEpollConnectionHelper(epoll_server(), QuicAllocator::BUFFER_POOL)), std::unique_ptr<QuicCryptoServerStream::Helper>( - new QuicSimpleCryptoServerStreamHelper(QuicRandom::GetInstance())), + new QuicSimpleCryptoServerStreamHelper()), std::unique_ptr<QuicEpollAlarmFactory>( new QuicEpollAlarmFactory(epoll_server())), &quic_simple_server_backend_); @@ -86,11 +86,13 @@ class TestQuicServer : public QuicServer { class QuicServerEpollInTest : public QuicTest { public: QuicServerEpollInTest() - : port_(QuicPickUnusedPortOrDie()), + : port_(QuicPickServerPortForTestsOrDie()), server_address_(TestLoopback(), port_) {} void StartListening() { server_.CreateUDPSocketAndListen(server_address_); + server_address_ = QuicSocketAddress(server_address_.host(), server_.port()); + ASSERT_TRUE(QuicServerPeer::SetSmallSocket(&server_)); if (!server_.overflow_supported()) { @@ -163,8 +165,7 @@ class QuicServerDispatchPacketTest : public QuicTest { new QuicEpollConnectionHelper(&eps_, QuicAllocator::BUFFER_POOL)), std::unique_ptr<QuicCryptoServerStream::Helper>( - new QuicSimpleCryptoServerStreamHelper( - QuicRandom::GetInstance())), + new QuicSimpleCryptoServerStreamHelper()), std::unique_ptr<QuicEpollAlarmFactory>( new QuicEpollAlarmFactory(&eps_)), &quic_simple_server_backend_) { diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_crypto_server_stream_helper.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_crypto_server_stream_helper.cc index 9d556008eb3..d27506ea06c 100644 --- a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_crypto_server_stream_helper.cc +++ b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_crypto_server_stream_helper.cc @@ -8,20 +8,12 @@ namespace quic { -QuicSimpleCryptoServerStreamHelper::QuicSimpleCryptoServerStreamHelper( - QuicRandom* random) - : random_(random) {} +QuicSimpleCryptoServerStreamHelper::QuicSimpleCryptoServerStreamHelper() = + default; QuicSimpleCryptoServerStreamHelper::~QuicSimpleCryptoServerStreamHelper() = default; -QuicConnectionId -QuicSimpleCryptoServerStreamHelper::GenerateConnectionIdForReject( - QuicTransportVersion /*version*/, - QuicConnectionId /*connection_id*/) const { - return QuicUtils::CreateRandomConnectionId(random_); -} - bool QuicSimpleCryptoServerStreamHelper::CanAcceptClientHello( const CryptoHandshakeMessage& /*message*/, const QuicSocketAddress& /*client_address*/, diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_crypto_server_stream_helper.h b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_crypto_server_stream_helper.h index 3480776ff1a..c4dcb08daee 100644 --- a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_crypto_server_stream_helper.h +++ b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_crypto_server_stream_helper.h @@ -15,22 +15,15 @@ namespace quic { class QuicSimpleCryptoServerStreamHelper : public QuicCryptoServerStream::Helper { public: - explicit QuicSimpleCryptoServerStreamHelper(QuicRandom* random); + QuicSimpleCryptoServerStreamHelper(); ~QuicSimpleCryptoServerStreamHelper() override; - QuicConnectionId GenerateConnectionIdForReject( - QuicTransportVersion /*version*/, - QuicConnectionId /*connection_id*/) const override; - bool CanAcceptClientHello(const CryptoHandshakeMessage& message, const QuicSocketAddress& client_address, const QuicSocketAddress& peer_address, const QuicSocketAddress& self_address, std::string* error_details) const override; - - private: - QuicRandom* random_; // Unowned. }; } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_crypto_server_stream_helper_test.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_crypto_server_stream_helper_test.cc deleted file mode 100644 index 4ede5889a9f..00000000000 --- a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_crypto_server_stream_helper_test.cc +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "net/third_party/quiche/src/quic/tools/quic_simple_crypto_server_stream_helper.h" - -#include "net/third_party/quiche/src/quic/core/quic_utils.h" -#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" -#include "net/third_party/quiche/src/quic/test_tools/mock_random.h" -#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" - -namespace quic { - -class QuicSimpleCryptoServerStreamHelperTest : public QuicTest {}; - -TEST_F(QuicSimpleCryptoServerStreamHelperTest, GenerateConnectionIdForReject) { - test::MockRandom random; - QuicSimpleCryptoServerStreamHelper helper(&random); - - EXPECT_EQ(QuicUtils::CreateRandomConnectionId(&random), - helper.GenerateConnectionIdForReject(QUIC_VERSION_99, - test::TestConnectionId())); -} - -} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_session.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_session.cc index 2e98cdd3e01..d06cd4b28a1 100644 --- a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_session.cc +++ b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_session.cc @@ -6,6 +6,7 @@ #include <utility> +#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_utils.h" #include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" @@ -32,11 +33,7 @@ QuicSimpleServerSession::QuicSimpleServerSession( crypto_config, compressed_certs_cache), highest_promised_stream_id_( - VersionHasStreamType(connection->transport_version()) - ? QuicUtils::GetFirstUnidirectionalStreamId( - connection->transport_version(), - Perspective::IS_SERVER) - : QuicUtils::GetInvalidStreamId(connection->transport_version())), + QuicUtils::GetInvalidStreamId(connection->transport_version())), quic_simple_server_backend_(quic_simple_server_backend) { DCHECK(quic_simple_server_backend_); } @@ -68,6 +65,7 @@ void QuicSimpleServerSession::PromisePushResources( const std::string& request_url, const std::list<QuicBackendResponse::ServerPushInfo>& resources, QuicStreamId original_stream_id, + const spdy::SpdyStreamPrecedence& original_precedence, const spdy::SpdyHeaderBlock& original_request_headers) { if (!server_push_enabled()) { return; @@ -78,10 +76,17 @@ void QuicSimpleServerSession::PromisePushResources( request_url, resource, original_request_headers); highest_promised_stream_id_ += QuicUtils::StreamIdDelta(connection()->transport_version()); + if (VersionHasIetfQuicFrames(connection()->transport_version()) && + highest_promised_stream_id_ > max_allowed_push_id()) { + return; + } SendPushPromise(original_stream_id, highest_promised_stream_id_, headers.Clone()); promised_streams_.push_back(PromisedStreamInfo( - std::move(headers), highest_promised_stream_id_, resource.priority)); + std::move(headers), highest_promised_stream_id_, + use_http2_priority_write_scheduler() + ? original_precedence + : spdy::SpdyStreamPrecedence(resource.priority))); } // Procese promised push request as many as possible. @@ -216,7 +221,7 @@ void QuicSimpleServerSession::HandlePromisedPushRequests() { DCHECK_EQ(promised_info.stream_id, promised_stream->id()); QUIC_DLOG(INFO) << "created server push stream " << promised_stream->id(); - promised_stream->SetPriority(promised_info.priority); + promised_stream->SetPriority(promised_info.precedence); spdy::SpdyHeaderBlock request_headers( std::move(promised_info.request_headers)); @@ -228,8 +233,19 @@ void QuicSimpleServerSession::HandlePromisedPushRequests() { void QuicSimpleServerSession::OnCanCreateNewOutgoingStream( bool unidirectional) { + QuicSpdySession::OnCanCreateNewOutgoingStream(unidirectional); if (unidirectional) { HandlePromisedPushRequests(); } } + +void QuicSimpleServerSession::MaybeInitializeHttp3UnidirectionalStreams() { + size_t previous_static_stream_count = num_outgoing_static_streams(); + QuicSpdySession::MaybeInitializeHttp3UnidirectionalStreams(); + size_t current_static_stream_count = num_outgoing_static_streams(); + DCHECK_GE(current_static_stream_count, previous_static_stream_count); + highest_promised_stream_id_ += + QuicUtils::StreamIdDelta(transport_version()) * + (current_static_stream_count - previous_static_stream_count); +} } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_session.h b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_session.h index d8cd6b5c539..d485e7c28dc 100644 --- a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_session.h +++ b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_session.h @@ -40,14 +40,14 @@ class QuicSimpleServerSession : public QuicServerSessionBase { public: PromisedStreamInfo(spdy::SpdyHeaderBlock request_headers, QuicStreamId stream_id, - spdy::SpdyPriority priority) + const spdy::SpdyStreamPrecedence& precedence) : request_headers(std::move(request_headers)), stream_id(stream_id), - priority(priority), + precedence(precedence), is_cancelled(false) {} spdy::SpdyHeaderBlock request_headers; QuicStreamId stream_id; - spdy::SpdyPriority priority; + spdy::SpdyStreamPrecedence precedence; bool is_cancelled; }; @@ -76,6 +76,7 @@ class QuicSimpleServerSession : public QuicServerSessionBase { const std::string& request_url, const std::list<QuicBackendResponse::ServerPushInfo>& resources, QuicStreamId original_stream_id, + const spdy::SpdyStreamPrecedence& original_precedence, const spdy::SpdyHeaderBlock& original_request_headers); void OnCanCreateNewOutgoingStream(bool unidirectional) override; @@ -101,6 +102,8 @@ class QuicSimpleServerSession : public QuicServerSessionBase { return quic_simple_server_backend_; } + void MaybeInitializeHttp3UnidirectionalStreams() override; + private: friend class test::QuicSimpleServerSessionPeer; diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_session_test.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_session_test.cc index cef24b79a31..6cc97ed7098 100644 --- a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_session_test.cc +++ b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_session_test.cc @@ -198,13 +198,18 @@ class QuicSimpleServerSessionTest QuicConfigPeer::SetReceivedMaxIncomingBidirectionalStreams( &config_, kMaxStreamsForTest); config_.SetMaxIncomingUnidirectionalStreamsToSend(kMaxStreamsForTest); - QuicConfigPeer::SetReceivedMaxIncomingUnidirectionalStreams( - &config_, kMaxStreamsForTest); config_.SetInitialStreamFlowControlWindowToSend( kInitialStreamFlowControlWindowForTest); config_.SetInitialSessionFlowControlWindowToSend( kInitialSessionFlowControlWindowForTest); + if (VersionUsesQpack(GetParam().transport_version)) { + QuicConfigPeer::SetReceivedMaxIncomingUnidirectionalStreams( + &config_, kMaxStreamsForTest + 3); + } else { + QuicConfigPeer::SetReceivedMaxIncomingUnidirectionalStreams( + &config_, kMaxStreamsForTest); + } ParsedQuicVersionVector supported_versions = SupportedVersions(GetParam()); connection_ = new StrictMock<MockQuicConnectionWithSendStreamData>( @@ -471,9 +476,11 @@ TEST_P(QuicSimpleServerSessionTest, CreateOutgoingDynamicStreamUptoLimit) { EXPECT_EQ(1u, session_->GetNumOpenIncomingStreams()); EXPECT_EQ(0u, session_->GetNumOpenOutgoingStreams()); - session_->UnregisterStreamPriority( - QuicUtils::GetHeadersStreamId(connection_->transport_version()), - /*is_static=*/true); + if (!VersionUsesQpack(connection_->transport_version())) { + session_->UnregisterStreamPriority( + QuicUtils::GetHeadersStreamId(connection_->transport_version()), + /*is_static=*/true); + } // Assume encryption already established. QuicSimpleServerSessionPeer::SetCryptoStream(session_.get(), nullptr); MockQuicCryptoServerStream* crypto_stream = @@ -481,9 +488,12 @@ TEST_P(QuicSimpleServerSessionTest, CreateOutgoingDynamicStreamUptoLimit) { session_.get(), &stream_helper_); crypto_stream->set_encryption_established(true); QuicSimpleServerSessionPeer::SetCryptoStream(session_.get(), crypto_stream); - session_->RegisterStreamPriority( - QuicUtils::GetHeadersStreamId(connection_->transport_version()), - /*is_static=*/true, QuicStream::kDefaultPriority); + if (!VersionUsesQpack(connection_->transport_version())) { + session_->RegisterStreamPriority( + QuicUtils::GetHeadersStreamId(connection_->transport_version()), + /*is_static=*/true, + spdy::SpdyStreamPrecedence(QuicStream::kDefaultPriority)); + } // Create push streams till reaching the upper limit of allowed open streams. for (size_t i = 0; i < kMaxStreamsForTest; ++i) { @@ -491,7 +501,7 @@ TEST_P(QuicSimpleServerSessionTest, CreateOutgoingDynamicStreamUptoLimit) { QuicSimpleServerSessionPeer::CreateOutgoingUnidirectionalStream( session_.get()); if (VersionHasStreamType(connection_->transport_version())) { - EXPECT_EQ(GetNthServerInitiatedUnidirectionalId(i + 1), + EXPECT_EQ(GetNthServerInitiatedUnidirectionalId(i + 3), created_stream->id()); } else { EXPECT_EQ(GetNthServerInitiatedUnidirectionalId(i), created_stream->id()); @@ -528,7 +538,7 @@ TEST_P(QuicSimpleServerSessionTest, GetEvenIncomingError) { "Data for nonexistent stream", _)); EXPECT_EQ(nullptr, QuicSessionPeer::GetOrCreateStream( - session_.get(), GetNthServerInitiatedUnidirectionalId(1))); + session_.get(), GetNthServerInitiatedUnidirectionalId(3))); } // In order to test the case where server push stream creation goes beyond @@ -574,9 +584,11 @@ class QuicSimpleServerSessionServerPushTest visitor_ = QuicConnectionPeer::GetVisitor(connection_); - session_->UnregisterStreamPriority( - QuicUtils::GetHeadersStreamId(connection_->transport_version()), - /*is_static=*/true); + if (!VersionUsesQpack(connection_->transport_version())) { + session_->UnregisterStreamPriority( + QuicUtils::GetHeadersStreamId(connection_->transport_version()), + /*is_static=*/true); + } QuicSimpleServerSessionPeer::SetCryptoStream(session_.get(), nullptr); // Assume encryption already established. MockQuicCryptoServerStream* crypto_stream = new MockQuicCryptoServerStream( @@ -585,9 +597,12 @@ class QuicSimpleServerSessionServerPushTest crypto_stream->set_encryption_established(true); QuicSimpleServerSessionPeer::SetCryptoStream(session_.get(), crypto_stream); - session_->RegisterStreamPriority( - QuicUtils::GetHeadersStreamId(connection_->transport_version()), - /*is_static=*/true, QuicStream::kDefaultPriority); + if (!VersionUsesQpack(connection_->transport_version())) { + session_->RegisterStreamPriority( + QuicUtils::GetHeadersStreamId(connection_->transport_version()), + /*is_static=*/true, + spdy::SpdyStreamPrecedence(QuicStream::kDefaultPriority)); + } } // Given |num_resources|, create this number of fake push resources and push @@ -612,7 +627,7 @@ class QuicSimpleServerSessionServerPushTest for (unsigned int i = 1; i <= num_resources; ++i) { QuicStreamId stream_id; if (VersionHasStreamType(connection_->transport_version())) { - stream_id = GetNthServerInitiatedUnidirectionalId(i); + stream_id = GetNthServerInitiatedUnidirectionalId(i + 2); } else { stream_id = GetNthServerInitiatedUnidirectionalId(i - 1); } @@ -675,9 +690,10 @@ class QuicSimpleServerSessionServerPushTest EXPECT_CALL(*session_, SendBlocked(stream_id)); } } - session_->PromisePushResources(request_url, push_resources, - GetNthClientInitiatedBidirectionalId(0), - request_headers); + session_->PromisePushResources( + request_url, push_resources, GetNthClientInitiatedBidirectionalId(0), + spdy::SpdyStreamPrecedence(0, spdy::kHttp2DefaultStreamWeight, false), + request_headers); return data_frame_header_length; } @@ -700,6 +716,7 @@ INSTANTIATE_TEST_SUITE_P(Tests, // opened and send push response. TEST_P(QuicSimpleServerSessionServerPushTest, TestPromisePushResources) { MaybeConsumeHeadersStreamData(); + session_->set_max_allowed_push_id(kMaxQuicStreamId); size_t num_resources = kMaxStreamsForTest + 5; PromisePushResources(num_resources); EXPECT_EQ(kMaxStreamsForTest, session_->GetNumOpenOutgoingStreams()); @@ -710,12 +727,13 @@ TEST_P(QuicSimpleServerSessionServerPushTest, TestPromisePushResources) { TEST_P(QuicSimpleServerSessionServerPushTest, HandlePromisedPushRequestsAfterStreamDraining) { MaybeConsumeHeadersStreamData(); + session_->set_max_allowed_push_id(kMaxQuicStreamId); size_t num_resources = kMaxStreamsForTest + 1; QuicByteCount data_frame_header_length = PromisePushResources(num_resources); QuicStreamId next_out_going_stream_id; if (VersionHasStreamType(connection_->transport_version())) { next_out_going_stream_id = - GetNthServerInitiatedUnidirectionalId(kMaxStreamsForTest + 1); + GetNthServerInitiatedUnidirectionalId(kMaxStreamsForTest + 3); } else { next_out_going_stream_id = GetNthServerInitiatedUnidirectionalId(kMaxStreamsForTest); @@ -760,11 +778,11 @@ TEST_P(QuicSimpleServerSessionServerPushTest, // Version 99 also has unidirectional static streams, so we need to send // MaxStreamFrame of the number of resources + number of static streams. session_->OnMaxStreamsFrame( - QuicMaxStreamsFrame(0, num_resources + 1, /*unidirectional=*/true)); + QuicMaxStreamsFrame(0, num_resources + 3, /*unidirectional=*/true)); } if (VersionHasStreamType(connection_->transport_version())) { - session_->StreamDraining(GetNthServerInitiatedUnidirectionalId(1)); + session_->StreamDraining(GetNthServerInitiatedUnidirectionalId(3)); } else { session_->StreamDraining(GetNthServerInitiatedUnidirectionalId(0)); } @@ -778,6 +796,7 @@ TEST_P(QuicSimpleServerSessionServerPushTest, TEST_P(QuicSimpleServerSessionServerPushTest, ResetPromisedStreamToCancelServerPush) { MaybeConsumeHeadersStreamData(); + session_->set_max_allowed_push_id(kMaxQuicStreamId); // Having two extra resources to be send later. One of them will be reset, so // when opened stream become close, only one will become open. @@ -795,7 +814,7 @@ TEST_P(QuicSimpleServerSessionServerPushTest, QuicStreamId stream_got_reset; if (VersionHasStreamType(connection_->transport_version())) { stream_got_reset = - GetNthServerInitiatedUnidirectionalId(kMaxStreamsForTest + 2); + GetNthServerInitiatedUnidirectionalId(kMaxStreamsForTest + 4); } else { stream_got_reset = GetNthServerInitiatedUnidirectionalId(kMaxStreamsForTest + 1); @@ -815,7 +834,7 @@ TEST_P(QuicSimpleServerSessionServerPushTest, QuicStreamId stream_not_reset; if (VersionHasStreamType(connection_->transport_version())) { stream_not_reset = - GetNthServerInitiatedUnidirectionalId(kMaxStreamsForTest + 1); + GetNthServerInitiatedUnidirectionalId(kMaxStreamsForTest + 3); } else { stream_not_reset = GetNthServerInitiatedUnidirectionalId(kMaxStreamsForTest); @@ -855,10 +874,10 @@ TEST_P(QuicSimpleServerSessionServerPushTest, // For pre-v-99, the node monitors its own stream usage and makes streams // available as it closes/etc them. session_->OnMaxStreamsFrame( - QuicMaxStreamsFrame(0, num_resources + 1, /*unidirectional=*/true)); + QuicMaxStreamsFrame(0, num_resources + 3, /*unidirectional=*/true)); } - session_->StreamDraining(GetNthServerInitiatedUnidirectionalId(1)); - session_->StreamDraining(GetNthServerInitiatedUnidirectionalId(2)); + session_->StreamDraining(GetNthServerInitiatedUnidirectionalId(3)); + session_->StreamDraining(GetNthServerInitiatedUnidirectionalId(4)); } // Tests that closing a open outgoing stream can trigger a promised resource in @@ -866,6 +885,7 @@ TEST_P(QuicSimpleServerSessionServerPushTest, TEST_P(QuicSimpleServerSessionServerPushTest, CloseStreamToHandleMorePromisedStream) { MaybeConsumeHeadersStreamData(); + session_->set_max_allowed_push_id(kMaxQuicStreamId); size_t num_resources = kMaxStreamsForTest + 1; if (VersionHasIetfQuicFrames(transport_version())) { // V99 will send out a stream-id-blocked frame when the we desired to exceed @@ -878,14 +898,14 @@ TEST_P(QuicSimpleServerSessionServerPushTest, QuicStreamId stream_to_open; if (VersionHasStreamType(connection_->transport_version())) { stream_to_open = - GetNthServerInitiatedUnidirectionalId(kMaxStreamsForTest + 1); + GetNthServerInitiatedUnidirectionalId(kMaxStreamsForTest + 3); } else { stream_to_open = GetNthServerInitiatedUnidirectionalId(kMaxStreamsForTest); } // Resetting an open stream will close the stream and give space for extra // stream to be opened. - QuicStreamId stream_got_reset = GetNthServerInitiatedUnidirectionalId(1); + QuicStreamId stream_got_reset = GetNthServerInitiatedUnidirectionalId(3); EXPECT_CALL(owner_, OnRstStreamReceived(_)).Times(1); EXPECT_CALL(*connection_, SendControlFrame(_)); if (!VersionHasIetfQuicFrames(transport_version())) { @@ -929,7 +949,7 @@ TEST_P(QuicSimpleServerSessionServerPushTest, // For pre-v-99, the node monitors its own stream usage and makes streams // available as it closes/etc them. session_->OnMaxStreamsFrame( - QuicMaxStreamsFrame(0, num_resources + 1, /*unidirectional=*/true)); + QuicMaxStreamsFrame(0, num_resources + 3, /*unidirectional=*/true)); } visitor_->OnRstStream(rst); // Create and inject a STOP_SENDING frame. In GOOGLE QUIC, receiving a diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_stream.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_stream.cc index f8522060af2..26ea62afc8f 100644 --- a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_stream.cc +++ b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_stream.cc @@ -239,7 +239,7 @@ void QuicSimpleServerStream::OnResponseBackendComplete( << " push resources."; QuicSimpleServerSession* session = static_cast<QuicSimpleServerSession*>(spdy_session()); - session->PromisePushResources(request_url, resources, id(), + session->PromisePushResources(request_url, resources, id(), precedence(), request_headers_); } diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_stream_test.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_stream_test.cc index 7bac18ec95b..55ea842e69f 100644 --- a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_stream_test.cc +++ b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_stream_test.cc @@ -133,7 +133,8 @@ class MockQuicSimpleServerSession : public QuicSimpleServerSession { size_t frame_len, const QuicHeaderList& header_list)); MOCK_METHOD2(OnStreamHeadersPriority, - void(QuicStreamId stream_id, spdy::SpdyPriority priority)); + void(QuicStreamId stream_id, + const spdy::SpdyStreamPrecedence& precedence)); MOCK_METHOD3(SendRstStream, void(QuicStreamId stream_id, QuicRstStreamErrorCode error, @@ -144,15 +145,17 @@ class MockQuicSimpleServerSession : public QuicSimpleServerSession { const std::string& request_url, const std::list<QuicBackendResponse::ServerPushInfo>& resources, QuicStreamId original_stream_id, + const spdy::SpdyStreamPrecedence& original_precedence, const spdy::SpdyHeaderBlock& original_request_headers) override { original_request_headers_ = original_request_headers.Clone(); PromisePushResourcesMock(request_url, resources, original_stream_id, - original_request_headers); + original_precedence, original_request_headers); } - MOCK_METHOD4(PromisePushResourcesMock, + MOCK_METHOD5(PromisePushResourcesMock, void(const std::string&, const std::list<QuicBackendResponse::ServerPushInfo>&, QuicStreamId, + const spdy::SpdyStreamPrecedence&, const spdy::SpdyHeaderBlock&)); using QuicSession::ActivateStream; @@ -201,6 +204,7 @@ class QuicSimpleServerStreamTest : public QuicTestWithParam<ParsedQuicVersion> { kInitialStreamFlowControlWindowForTest); session_.config()->SetInitialSessionFlowControlWindowToSend( kInitialSessionFlowControlWindowForTest); + session_.Initialize(); stream_ = new StrictMock<TestStream>( GetNthClientInitiatedBidirectionalStreamId( connection_->transport_version(), 0), @@ -413,7 +417,7 @@ TEST_P(QuicSimpleServerStreamTest, SendPushResponseWith404Response) { // Create a new promised stream with even id(). auto promised_stream = new StrictMock<TestStream>( GetNthServerInitiatedUnidirectionalStreamId( - connection_->transport_version(), 0), + connection_->transport_version(), 3), &session_, WRITE_UNIDIRECTIONAL, &memory_cache_backend_); session_.ActivateStream(QuicWrapUnique(promised_stream)); @@ -514,7 +518,7 @@ TEST_P(QuicSimpleServerStreamTest, SendResponseWithPushResources) { host + request_path, _, GetNthClientInitiatedBidirectionalStreamId( connection_->transport_version(), 0), - _)); + _, _)); EXPECT_CALL(*stream_, WriteHeadersMock(false)); if (HasFrameHeader()) { EXPECT_CALL(session_, WritevData(_, _, header_length, _, NO_FIN)); @@ -549,7 +553,7 @@ TEST_P(QuicSimpleServerStreamTest, PushResponseOnServerInitiatedStream) { // Create a stream with even stream id and test against this stream. const QuicStreamId kServerInitiatedStreamId = GetNthServerInitiatedUnidirectionalStreamId( - connection_->transport_version(), 0); + connection_->transport_version(), 3); // Create a server initiated stream and pass it to session_. auto server_initiated_stream = new StrictMock<TestStream>(kServerInitiatedStreamId, &session_, diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_spdy_client_base.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_spdy_client_base.cc index 3a3bafe2f3e..09a1998a802 100644 --- a/chromium/net/third_party/quiche/src/quic/tools/quic_spdy_client_base.cc +++ b/chromium/net/third_party/quiche/src/quic/tools/quic_spdy_client_base.cc @@ -174,7 +174,8 @@ QuicSpdyClientStream* QuicSpdyClientBase::CreateClientStream() { auto* stream = static_cast<QuicSpdyClientStream*>( client_session()->CreateOutgoingBidirectionalStream()); if (stream) { - stream->SetPriority(QuicStream::kDefaultPriority); + stream->SetPriority( + spdy::SpdyStreamPrecedence(QuicStream::kDefaultPriority)); stream->set_visitor(this); } return stream; diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_toy_client.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_toy_client.cc index 319d3a2d2ec..09e7a50d571 100644 --- a/chromium/net/third_party/quiche/src/quic/tools/quic_toy_client.cc +++ b/chromium/net/third_party/quiche/src/quic/tools/quic_toy_client.cc @@ -111,13 +111,11 @@ DEFINE_QUIC_COMMAND_LINE_FLAG( "versions are offered in the handshake. Also supports wire versions " "such as Q043 or T099."); -DEFINE_QUIC_COMMAND_LINE_FLAG( - int32_t, - quic_ietf_draft, - 0, - "QUIC IETF draft number to use over the wire, e.g. 18. " - "By default this sets quic_version to T099. " - "This also enables required internal QUIC flags."); +DEFINE_QUIC_COMMAND_LINE_FLAG(bool, + quic_ietf_draft, + false, + "Use the IETF draft version. This also enables " + "required internal QUIC flags."); DEFINE_QUIC_COMMAND_LINE_FLAG( bool, @@ -183,14 +181,12 @@ int QuicToyClient::SendRequestsAndPrintResponses( quic::ParsedQuicVersionVector versions = quic::CurrentSupportedVersions(); std::string quic_version_string = GetQuicFlag(FLAGS_quic_version); - const int32_t quic_ietf_draft = GetQuicFlag(FLAGS_quic_ietf_draft); - if (quic_ietf_draft > 0) { - quic::QuicVersionInitializeSupportForIetfDraft(quic_ietf_draft); - if (quic_version_string.length() == 0) { - quic_version_string = "T099"; - } - } - if (quic_version_string.length() > 0) { + if (GetQuicFlag(FLAGS_quic_ietf_draft)) { + quic::QuicVersionInitializeSupportForIetfDraft(); + versions = {{quic::PROTOCOL_TLS1_3, quic::QUIC_VERSION_99}}; + quic::QuicEnableVersion(versions[0]); + + } else if (!quic_version_string.empty()) { if (quic_version_string[0] == 'T') { // ParseQuicVersionString checks quic_supports_tls_handshake. SetQuicFlag(FLAGS_quic_supports_tls_handshake, true); @@ -201,8 +197,7 @@ int QuicToyClient::SendRequestsAndPrintResponses( quic::QUIC_VERSION_UNSUPPORTED) { return 1; } - versions.clear(); - versions.push_back(parsed_quic_version); + versions = {parsed_quic_version}; quic::QuicEnableVersion(parsed_quic_version); } @@ -216,7 +211,7 @@ int QuicToyClient::SendRequestsAndPrintResponses( if (GetQuicFlag(FLAGS_disable_certificate_verification)) { proof_verifier = quic::QuicMakeUnique<FakeProofVerifier>(); } else { - proof_verifier = quic::CreateDefaultProofVerifier(); + proof_verifier = quic::CreateDefaultProofVerifier(url.host()); } // Build the client, and try to connect. diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_toy_server.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_toy_server.cc index 633b465960e..9fb15eb87e5 100644 --- a/chromium/net/third_party/quiche/src/quic/tools/quic_toy_server.cc +++ b/chromium/net/third_party/quiche/src/quic/tools/quic_toy_server.cc @@ -26,12 +26,11 @@ DEFINE_QUIC_COMMAND_LINE_FLAG( "construction to seed the cache. Cache directory can be " "generated using `wget -p --save-headers <url>`"); -DEFINE_QUIC_COMMAND_LINE_FLAG( - int32_t, - quic_ietf_draft, - 0, - "QUIC IETF draft number to use over the wire, e.g. 18. " - "This also enables required internal QUIC flags."); +DEFINE_QUIC_COMMAND_LINE_FLAG(bool, + quic_ietf_draft, + false, + "Use the IETF draft version. This also enables " + "required internal QUIC flags."); namespace quic { @@ -50,9 +49,8 @@ QuicToyServer::QuicToyServer(BackendFactory* backend_factory, : backend_factory_(backend_factory), server_factory_(server_factory) {} int QuicToyServer::Start() { - const int32_t quic_ietf_draft = GetQuicFlag(FLAGS_quic_ietf_draft); - if (quic_ietf_draft > 0) { - quic::QuicVersionInitializeSupportForIetfDraft(quic_ietf_draft); + if (GetQuicFlag(FLAGS_quic_ietf_draft)) { + QuicVersionInitializeSupportForIetfDraft(); quic::QuicEnableVersion( quic::ParsedQuicVersion(quic::PROTOCOL_TLS1_3, quic::QUIC_VERSION_99)); } diff --git a/chromium/net/third_party/quiche/src/spdy/core/fifo_write_scheduler.h b/chromium/net/third_party/quiche/src/spdy/core/fifo_write_scheduler.h new file mode 100644 index 00000000000..aa904f6d740 --- /dev/null +++ b/chromium/net/third_party/quiche/src/spdy/core/fifo_write_scheduler.h @@ -0,0 +1,220 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef QUICHE_SPDY_CORE_FIFO_WRITE_SCHEDULER_H_ +#define QUICHE_SPDY_CORE_FIFO_WRITE_SCHEDULER_H_ + +#include <map> +#include <set> +#include <string> + +#include "net/third_party/quiche/src/spdy/core/write_scheduler.h" +#include "net/third_party/quiche/src/spdy/platform/api/spdy_string_utils.h" + +namespace spdy { + +// A write scheduler where the stream with the smallest stream ID will have the +// highest priority. +template <typename StreamIdType> +class FifoWriteScheduler : public WriteScheduler<StreamIdType> { + public: + using typename WriteScheduler<StreamIdType>::StreamPrecedenceType; + + FifoWriteScheduler() = default; + + // WriteScheduler methods + void RegisterStream(StreamIdType stream_id, + const StreamPrecedenceType& precedence) override; + void UnregisterStream(StreamIdType stream_id) override; + bool StreamRegistered(StreamIdType stream_id) const override; + StreamPrecedenceType GetStreamPrecedence( + StreamIdType stream_id) const override; + void UpdateStreamPrecedence(StreamIdType stream_id, + const StreamPrecedenceType& precedence) override; + std::vector<StreamIdType> GetStreamChildren( + StreamIdType stream_id) const override; + void RecordStreamEventTime(StreamIdType stream_id, + int64_t now_in_usec) override; + int64_t GetLatestEventWithPrecedence(StreamIdType stream_id) const override; + bool ShouldYield(StreamIdType stream_id) const override; + void MarkStreamReady(StreamIdType stream_id, bool add_to_front) override; + void MarkStreamNotReady(StreamIdType stream_id) override; + bool HasReadyStreams() const override; + StreamIdType PopNextReadyStream() override; + std::tuple<StreamIdType, StreamPrecedenceType> + PopNextReadyStreamAndPrecedence() override; + size_t NumReadyStreams() const override; + bool IsStreamReady(StreamIdType stream_id) const override; + size_t NumRegisteredStreams() const override; + std::string DebugString() const override; + + private: + std::set<StreamIdType> ready_streams_; + // This map maps stream ID to read/write event time (us since Unix epoch). + std::map<StreamIdType, int64_t> registered_streams_; +}; + +template <typename StreamIdType> +void FifoWriteScheduler<StreamIdType>::RegisterStream( + StreamIdType stream_id, + const StreamPrecedenceType& /*precedence*/) { + if (StreamRegistered(stream_id)) { + SPDY_BUG << "Stream " << stream_id << " already registered"; + return; + } + registered_streams_.emplace_hint(registered_streams_.end(), stream_id, 0); +} + +template <typename StreamIdType> +void FifoWriteScheduler<StreamIdType>::UnregisterStream( + StreamIdType stream_id) { + if (!StreamRegistered(stream_id)) { + SPDY_BUG << "Stream " << stream_id << " is not registered"; + return; + } + registered_streams_.erase(stream_id); + ready_streams_.erase(stream_id); +} + +template <typename StreamIdType> +bool FifoWriteScheduler<StreamIdType>::StreamRegistered( + StreamIdType stream_id) const { + return registered_streams_.find(stream_id) != registered_streams_.end(); +} + +// Stream precedence is not supported by this scheduler. +template <typename StreamIdType> +typename FifoWriteScheduler<StreamIdType>::StreamPrecedenceType +FifoWriteScheduler<StreamIdType>::GetStreamPrecedence( + StreamIdType /*stream_id*/) const { + return StreamPrecedenceType(kV3LowestPriority); +} + +template <typename StreamIdType> +void FifoWriteScheduler<StreamIdType>::UpdateStreamPrecedence( + StreamIdType /*stream_id*/, + const StreamPrecedenceType& /*precedence*/) {} + +template <typename StreamIdType> +std::vector<StreamIdType> FifoWriteScheduler<StreamIdType>::GetStreamChildren( + StreamIdType /*stream_id*/) const { + return std::vector<StreamIdType>(); +} + +template <typename StreamIdType> +void FifoWriteScheduler<StreamIdType>::RecordStreamEventTime( + StreamIdType stream_id, + int64_t now_in_usec) { + auto it = registered_streams_.find(stream_id); + if (it != registered_streams_.end()) { + it->second = now_in_usec; + } else { + SPDY_BUG << "Stream " << stream_id << " is not registered"; + } +} + +template <typename StreamIdType> +int64_t FifoWriteScheduler<StreamIdType>::GetLatestEventWithPrecedence( + StreamIdType stream_id) const { + if (!StreamRegistered(stream_id)) { + SPDY_BUG << "Stream " << stream_id << " is not registered"; + return 0; + } + int64_t latest_event_time_us = 0; + for (auto it = registered_streams_.begin(); it != registered_streams_.end(); + ++it) { + if (stream_id <= it->first) { + break; + } + latest_event_time_us = std::max(latest_event_time_us, it->second); + } + return latest_event_time_us; +} + +template <typename StreamIdType> +bool FifoWriteScheduler<StreamIdType>::ShouldYield( + StreamIdType stream_id) const { + return !ready_streams_.empty() && stream_id > *ready_streams_.begin(); +} + +template <typename StreamIdType> +void FifoWriteScheduler<StreamIdType>::MarkStreamReady(StreamIdType stream_id, + bool /*add_to_front*/) { + if (!StreamRegistered(stream_id)) { + SPDY_BUG << "Stream " << stream_id << " is not registered"; + return; + } + if (ready_streams_.find(stream_id) != ready_streams_.end()) { + SPDY_DVLOG(1) << "Stream already exists in the list"; + return; + } + ready_streams_.insert(stream_id); +} + +template <typename StreamIdType> +void FifoWriteScheduler<StreamIdType>::MarkStreamNotReady( + StreamIdType stream_id) { + auto it = ready_streams_.find(stream_id); + if (it == ready_streams_.end()) { + SPDY_DVLOG(1) << "Try to remove a stream that is not on list"; + return; + } + ready_streams_.erase(it); +} + +template <typename StreamIdType> +bool FifoWriteScheduler<StreamIdType>::HasReadyStreams() const { + return !ready_streams_.empty(); +} + +template <typename StreamIdType> +StreamIdType FifoWriteScheduler<StreamIdType>::PopNextReadyStream() { + if (ready_streams_.empty()) { + SPDY_BUG << "No ready streams available"; + return 0; + } + auto it = ready_streams_.begin(); + StreamIdType id = *it; + ready_streams_.erase(it); + return id; +} + +template <typename StreamIdType> +std::tuple<StreamIdType, + typename FifoWriteScheduler<StreamIdType>::StreamPrecedenceType> +FifoWriteScheduler<StreamIdType>::PopNextReadyStreamAndPrecedence() { + return std::make_tuple(PopNextReadyStream(), + StreamPrecedenceType(kV3LowestPriority)); +} + +template <typename StreamIdType> +size_t FifoWriteScheduler<StreamIdType>::NumReadyStreams() const { + return ready_streams_.size(); +} + +template <typename StreamIdType> +bool FifoWriteScheduler<StreamIdType>::IsStreamReady( + StreamIdType stream_id) const { + if (!StreamRegistered(stream_id)) { + SPDY_BUG << "Stream " << stream_id << " is not registered"; + return false; + } + return ready_streams_.find(stream_id) != ready_streams_.end(); +} + +template <typename StreamIdType> +size_t FifoWriteScheduler<StreamIdType>::NumRegisteredStreams() const { + return registered_streams_.size(); +} + +template <typename StreamIdType> +std::string FifoWriteScheduler<StreamIdType>::DebugString() const { + return SpdyStrCat( + "FifoWriteScheduler {num_streams=", registered_streams_.size(), + " num_ready_streams=", NumReadyStreams(), "}"); +} + +} // namespace spdy + +#endif // QUICHE_SPDY_CORE_FIFO_WRITE_SCHEDULER_H_ diff --git a/chromium/net/third_party/quiche/src/spdy/core/fifo_write_scheduler_test.cc b/chromium/net/third_party/quiche/src/spdy/core/fifo_write_scheduler_test.cc new file mode 100644 index 00000000000..1ad6d62049d --- /dev/null +++ b/chromium/net/third_party/quiche/src/spdy/core/fifo_write_scheduler_test.cc @@ -0,0 +1,85 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/third_party/quiche/src/spdy/core/fifo_write_scheduler.h" + +#include "net/third_party/quiche/src/spdy/platform/api/spdy_test.h" +#include "net/third_party/quiche/src/spdy/platform/api/spdy_test_helpers.h" + +namespace spdy { + +namespace test { + +TEST(FifoWriteSchedulerTest, BasicTest) { + FifoWriteScheduler<SpdyStreamId> fifo; + EXPECT_FALSE(fifo.HasReadyStreams()); + EXPECT_SPDY_BUG( + EXPECT_EQ(0u, std::get<0>(fifo.PopNextReadyStreamAndPrecedence())), + "No ready streams available"); + EXPECT_SPDY_BUG(fifo.MarkStreamReady(9, true), "Stream 9 is not registered"); + EXPECT_SPDY_BUG(fifo.IsStreamReady(9), "Stream 9 is not registered"); + + SpdyStreamPrecedence precedence(1); + fifo.RegisterStream(3, precedence); + EXPECT_FALSE(fifo.IsStreamReady(3)); + fifo.RegisterStream(9, precedence); + fifo.RegisterStream(7, precedence); + fifo.RegisterStream(11, precedence); + fifo.RegisterStream(13, precedence); + fifo.RegisterStream(15, precedence); + fifo.RegisterStream(17, precedence); + EXPECT_EQ(7u, fifo.NumRegisteredStreams()); + EXPECT_FALSE(fifo.HasReadyStreams()); + + fifo.MarkStreamReady(9, true); + EXPECT_TRUE(fifo.IsStreamReady(9)); + EXPECT_TRUE(fifo.HasReadyStreams()); + fifo.MarkStreamReady(15, true); + fifo.MarkStreamReady(7, true); + fifo.MarkStreamReady(13, true); + fifo.MarkStreamReady(11, true); + fifo.MarkStreamReady(3, true); + fifo.MarkStreamReady(17, true); + EXPECT_EQ(7u, fifo.NumReadyStreams()); + + EXPECT_EQ(3u, fifo.PopNextReadyStream()); + EXPECT_EQ(7u, std::get<0>(fifo.PopNextReadyStreamAndPrecedence())); + EXPECT_EQ(5u, fifo.NumReadyStreams()); + + EXPECT_FALSE(fifo.ShouldYield(3)); + EXPECT_FALSE(fifo.ShouldYield(9)); + EXPECT_TRUE(fifo.ShouldYield(13)); + EXPECT_TRUE(fifo.ShouldYield(10)); + + fifo.MarkStreamNotReady(9); + EXPECT_EQ(4u, fifo.NumReadyStreams()); + EXPECT_FALSE(fifo.ShouldYield(10)); + EXPECT_TRUE(fifo.ShouldYield(12)); +} + +TEST(FifoWriteSchedulerTest, GetLatestEventTest) { + FifoWriteScheduler<SpdyStreamId> fifo; + + SpdyStreamPrecedence precedence(1); + fifo.RegisterStream(1, precedence); + fifo.RegisterStream(3, precedence); + fifo.RegisterStream(5, precedence); + fifo.RegisterStream(7, precedence); + fifo.RegisterStream(9, precedence); + fifo.RecordStreamEventTime(1, 3); + fifo.RecordStreamEventTime(3, 2); + fifo.RecordStreamEventTime(5, 4); + fifo.RecordStreamEventTime(7, 8); + fifo.RecordStreamEventTime(9, 1); + + EXPECT_EQ(8, fifo.GetLatestEventWithPrecedence(9)); + EXPECT_EQ(4, fifo.GetLatestEventWithPrecedence(7)); + EXPECT_EQ(3, fifo.GetLatestEventWithPrecedence(5)); + EXPECT_EQ(3, fifo.GetLatestEventWithPrecedence(3)); + EXPECT_EQ(0, fifo.GetLatestEventWithPrecedence(1)); +} + +} // namespace test + +} // namespace spdy diff --git a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_decoder_adapter_test.cc b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_decoder_adapter_test.cc index d016841f077..da3a56c6648 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_decoder_adapter_test.cc +++ b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_decoder_adapter_test.cc @@ -8,6 +8,7 @@ #include <stdint.h> +#include <string> #include <tuple> #include <utility> #include <vector> @@ -22,7 +23,6 @@ #include "net/third_party/quiche/src/spdy/core/spdy_test_utils.h" #include "net/third_party/quiche/src/spdy/platform/api/spdy_arraysize.h" #include "net/third_party/quiche/src/spdy/platform/api/spdy_logging.h" -#include "net/third_party/quiche/src/spdy/platform/api/spdy_string.h" #include "net/third_party/quiche/src/spdy/platform/api/spdy_string_utils.h" #include "net/third_party/quiche/src/spdy/platform/api/spdy_test.h" @@ -231,8 +231,8 @@ class HpackDecoderAdapterTest void expectEntry(size_t index, size_t size, - const SpdyString& name, - const SpdyString& value) { + const std::string& name, + const std::string& value) { const HpackStringPair* entry = decoder_peer_.GetTableEntry(index); EXPECT_EQ(name, entry->name) << "index " << index; EXPECT_EQ(value, entry->value); @@ -240,7 +240,7 @@ class HpackDecoderAdapterTest } SpdyHeaderBlock MakeHeaderBlock( - const std::vector<std::pair<SpdyString, SpdyString>>& headers) { + const std::vector<std::pair<std::string, std::string>>& headers) { SpdyHeaderBlock result; for (const auto& kv : headers) { result.AppendValueOrAddHeader(kv.first, kv.second); @@ -277,12 +277,12 @@ TEST_P(HpackDecoderAdapterTest, // limit is rejected. HandleControlFrameHeadersStart(); const size_t kMaxBufferSizeBytes = 50; - const SpdyString a_value = SpdyString(49, 'x'); + const std::string a_value = std::string(49, 'x'); decoder_.set_max_decode_buffer_size_bytes(kMaxBufferSizeBytes); HpackBlockBuilder hbb; hbb.AppendLiteralNameAndValue(HpackEntryType::kNeverIndexedLiteralHeader, false, "a", false, a_value); - const SpdyString& s = hbb.buffer(); + const std::string& s = hbb.buffer(); EXPECT_GT(s.size(), kMaxBufferSizeBytes); // Any one in input buffer must not exceed kMaxBufferSizeBytes. @@ -297,8 +297,8 @@ TEST_P(HpackDecoderAdapterTest, TEST_P(HpackDecoderAdapterTest, NameTooLong) { // Verify that a name longer than the allowed size generates an error. const size_t kMaxBufferSizeBytes = 50; - const SpdyString name = SpdyString(2 * kMaxBufferSizeBytes, 'x'); - const SpdyString value = "abc"; + const std::string name = std::string(2 * kMaxBufferSizeBytes, 'x'); + const std::string value = "abc"; decoder_.set_max_decode_buffer_size_bytes(kMaxBufferSizeBytes); @@ -307,7 +307,7 @@ TEST_P(HpackDecoderAdapterTest, NameTooLong) { false, name, false, value); const size_t fragment_size = (3 * kMaxBufferSizeBytes) / 2; - const SpdyString fragment = hbb.buffer().substr(0, fragment_size); + const std::string fragment = hbb.buffer().substr(0, fragment_size); HandleControlFrameHeadersStart(); EXPECT_FALSE(HandleControlFrameHeadersData(fragment)); @@ -316,8 +316,8 @@ TEST_P(HpackDecoderAdapterTest, NameTooLong) { TEST_P(HpackDecoderAdapterTest, HeaderTooLongToBuffer) { // Verify that a header longer than the allowed size generates an error if // it isn't all in one input buffer. - const SpdyString name = "some-key"; - const SpdyString value = "some-value"; + const std::string name = "some-key"; + const std::string value = "some-value"; const size_t kMaxBufferSizeBytes = name.size() + value.size() - 2; decoder_.set_max_decode_buffer_size_bytes(kMaxBufferSizeBytes); @@ -325,7 +325,7 @@ TEST_P(HpackDecoderAdapterTest, HeaderTooLongToBuffer) { hbb.AppendLiteralNameAndValue(HpackEntryType::kNeverIndexedLiteralHeader, false, name, false, value); const size_t fragment_size = hbb.size() - 1; - const SpdyString fragment = hbb.buffer().substr(0, fragment_size); + const std::string fragment = hbb.buffer().substr(0, fragment_size); HandleControlFrameHeadersStart(); EXPECT_FALSE(HandleControlFrameHeadersData(fragment)); @@ -333,8 +333,8 @@ TEST_P(HpackDecoderAdapterTest, HeaderTooLongToBuffer) { // Verify that a header block that exceeds the maximum length is rejected. TEST_P(HpackDecoderAdapterTest, HeaderBlockTooLong) { - const SpdyString name = "some-key"; - const SpdyString value = "some-value"; + const std::string name = "some-key"; + const std::string value = "some-value"; const size_t kMaxBufferSizeBytes = 1024; HpackBlockBuilder hbb; @@ -364,7 +364,7 @@ TEST_P(HpackDecoderAdapterTest, DecodeWithIncompleteData) { // No need to wait for more data. EXPECT_TRUE(HandleControlFrameHeadersData("\x82\x85\x82")); - std::vector<std::pair<SpdyString, SpdyString>> expected_headers = { + std::vector<std::pair<std::string, std::string>> expected_headers = { {":method", "GET"}, {":path", "/index.html"}, {":method", "GET"}}; SpdyHeaderBlock expected_block1 = MakeHeaderBlock(expected_headers); @@ -405,7 +405,7 @@ TEST_P(HpackDecoderAdapterTest, HandleHeaderRepresentation) { // Already-delimited headers are passed through. decoder_peer_.HandleHeaderRepresentation("passed-through", - SpdyString("foo\0baz", 7)); + std::string("foo\0baz", 7)); // Other headers are joined on \0. Case matters. decoder_peer_.HandleHeaderRepresentation("joined", "not joined"); @@ -492,7 +492,7 @@ TEST_P(HpackDecoderAdapterTest, InvalidIndexedHeader) { TEST_P(HpackDecoderAdapterTest, ContextUpdateMaximumSize) { EXPECT_EQ(kDefaultHeaderTableSizeSetting, decoder_peer_.header_table_size_limit()); - SpdyString input; + std::string input; { // Maximum-size update with size 126. Succeeds. HpackOutputStream output_stream; @@ -529,7 +529,7 @@ TEST_P(HpackDecoderAdapterTest, ContextUpdateMaximumSize) { // Two HeaderTableSizeUpdates may appear at the beginning of the block TEST_P(HpackDecoderAdapterTest, TwoTableSizeUpdates) { - SpdyString input; + std::string input; { // Should accept two table size updates, update to second one HpackOutputStream output_stream; @@ -546,7 +546,7 @@ TEST_P(HpackDecoderAdapterTest, TwoTableSizeUpdates) { // Three HeaderTableSizeUpdates should result in an error TEST_P(HpackDecoderAdapterTest, ThreeTableSizeUpdatesError) { - SpdyString input; + std::string input; { // Should reject three table size updates, update to second one HpackOutputStream output_stream; @@ -567,7 +567,7 @@ TEST_P(HpackDecoderAdapterTest, ThreeTableSizeUpdatesError) { // HeaderTableSizeUpdates may only appear at the beginning of the block // Any other updates should result in an error TEST_P(HpackDecoderAdapterTest, TableSizeUpdateSecondError) { - SpdyString input; + std::string input; { // Should reject a table size update appearing after a different entry // The table size should remain as the default @@ -587,7 +587,7 @@ TEST_P(HpackDecoderAdapterTest, TableSizeUpdateSecondError) { // HeaderTableSizeUpdates may only appear at the beginning of the block // Any other updates should result in an error TEST_P(HpackDecoderAdapterTest, TableSizeUpdateFirstThirdError) { - SpdyString input; + std::string input; { // Should reject the second table size update // if a different entry appears after the first update @@ -678,7 +678,7 @@ TEST_P(HpackDecoderAdapterTest, TruncatedHuffmanLiteral) { // | www.example.com // | -> :authority: www.example.com - SpdyString first = SpdyHexDecode("418cf1e3c2e5f23a6ba0ab90f4ff"); + std::string first = SpdyHexDecode("418cf1e3c2e5f23a6ba0ab90f4ff"); EXPECT_TRUE(DecodeHeaderBlock(first)); first.pop_back(); EXPECT_FALSE(DecodeHeaderBlock(first)); @@ -698,7 +698,7 @@ TEST_P(HpackDecoderAdapterTest, HuffmanEOSError) { // | www.example.com // | -> :authority: www.example.com - SpdyString first = SpdyHexDecode("418cf1e3c2e5f23a6ba0ab90f4ff"); + std::string first = SpdyHexDecode("418cf1e3c2e5f23a6ba0ab90f4ff"); EXPECT_TRUE(DecodeHeaderBlock(first)); first = SpdyHexDecode("418df1e3c2e5f23a6ba0ab90f4ffff"); EXPECT_FALSE(DecodeHeaderBlock(first)); @@ -715,7 +715,7 @@ TEST_P(HpackDecoderAdapterTest, BasicC31) { expected_header_set[":path"] = "/"; expected_header_set[":authority"] = "www.example.com"; - SpdyString encoded_header_set; + std::string encoded_header_set; EXPECT_TRUE( encoder.EncodeHeaderSet(expected_header_set, &encoded_header_set)); @@ -747,7 +747,7 @@ TEST_P(HpackDecoderAdapterTest, SectionC4RequestHuffmanExamples) { // | Decoded: // | www.example.com // | -> :authority: www.example.com - SpdyString first = SpdyHexDecode("828684418cf1e3c2e5f23a6ba0ab90f4ff"); + std::string first = SpdyHexDecode("828684418cf1e3c2e5f23a6ba0ab90f4ff"); const SpdyHeaderBlock& first_header_set = DecodeBlockExpectingSuccess(first); EXPECT_THAT(first_header_set, @@ -784,7 +784,7 @@ TEST_P(HpackDecoderAdapterTest, SectionC4RequestHuffmanExamples) { // | no-cache // | -> cache-control: no-cache - SpdyString second = SpdyHexDecode("828684be5886a8eb10649cbf"); + std::string second = SpdyHexDecode("828684be5886a8eb10649cbf"); const SpdyHeaderBlock& second_header_set = DecodeBlockExpectingSuccess(second); @@ -826,7 +826,7 @@ TEST_P(HpackDecoderAdapterTest, SectionC4RequestHuffmanExamples) { // | Decoded: // | custom-value // | -> custom-key: custom-value - SpdyString third = + std::string third = SpdyHexDecode("828785bf408825a849e95ba97d7f8925a849e95bb8e8b4bf"); const SpdyHeaderBlock& third_header_set = DecodeBlockExpectingSuccess(third); @@ -896,7 +896,7 @@ TEST_P(HpackDecoderAdapterTest, SectionC6ResponseHuffmanExamples) { // | -> location: https://www.e // | xample.com - SpdyString first = SpdyHexDecode( + std::string first = SpdyHexDecode( "488264025885aec3771a4b6196d07abe" "941054d444a8200595040b8166e082a6" "2d1bff6e919d29ad171863c78f0b97c8" @@ -939,7 +939,7 @@ TEST_P(HpackDecoderAdapterTest, SectionC6ResponseHuffmanExamples) { // | idx = 63 // | -> location: // | https://www.example.com - SpdyString second = SpdyHexDecode("4883640effc1c0bf"); + std::string second = SpdyHexDecode("4883640effc1c0bf"); const SpdyHeaderBlock& second_header_set = DecodeBlockExpectingSuccess(second); @@ -1011,7 +1011,7 @@ TEST_P(HpackDecoderAdapterTest, SectionC6ResponseHuffmanExamples) { // | -> set-cookie: foo=ASDJKHQ // | KBZXOQWEOPIUAXQWEOIU; // | max-age=3600; version=1 - SpdyString third = SpdyHexDecode( + std::string third = SpdyHexDecode( "88c16196d07abe941054d444a8200595" "040b8166e084a62d1bffc05a839bd9ab" "77ad94e7821dd7f2e6c7b335dfdfcd5b" @@ -1096,7 +1096,7 @@ TEST_P(HpackDecoderAdapterTest, ReuseNameOfEvictedEntry) { // SpdyHeaderBlock stores these 6 strings as '\0' separated values. // Make sure that is what happened. - SpdyString joined_values = expected_header_set[name].as_string(); + std::string joined_values = expected_header_set[name].as_string(); EXPECT_EQ(joined_values.size(), 2 * value1.size() + 2 * value2.size() + 2 * value3.size() + 5); diff --git a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_encoder.cc b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_encoder.cc index d76ead083c1..f4f427c582c 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_encoder.cc +++ b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_encoder.cc @@ -86,7 +86,7 @@ HpackEncoder::HpackEncoder(const HpackHuffmanTable& table) HpackEncoder::~HpackEncoder() = default; bool HpackEncoder::EncodeHeaderSet(const SpdyHeaderBlock& header_set, - SpdyString* output) { + std::string* output) { // Separate header set into pseudo-headers and regular headers. Representations pseudo_headers; Representations regular_headers; @@ -131,7 +131,7 @@ size_t HpackEncoder::EstimateMemoryUsage() const { } void HpackEncoder::EncodeRepresentations(RepresentationIterator* iter, - SpdyString* output) { + std::string* output) { MaybeEmitTableSize(); while (iter->HasNext()) { const auto header = iter->Next(); @@ -291,7 +291,7 @@ class HpackEncoder::Encoderator : public ProgressiveEncoder { // Encodes up to max_encoded_bytes of the current header block into the // given output string. - void Next(size_t max_encoded_bytes, SpdyString* output) override; + void Next(size_t max_encoded_bytes, std::string* output) override; private: HpackEncoder* encoder_; @@ -329,7 +329,7 @@ HpackEncoder::Encoderator::Encoderator(const SpdyHeaderBlock& header_set, } void HpackEncoder::Encoderator::Next(size_t max_encoded_bytes, - SpdyString* output) { + std::string* output) { SPDY_BUG_IF(!has_next_) << "Encoderator::Next called with nothing left to encode."; const bool use_compression = encoder_->enable_compression_; diff --git a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_encoder.h b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_encoder.h index c0cf968ab5d..a24d69ba18c 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_encoder.h +++ b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_encoder.h @@ -10,6 +10,7 @@ #include <functional> #include <map> #include <memory> +#include <string> #include <utility> #include <vector> @@ -17,7 +18,6 @@ #include "net/third_party/quiche/src/spdy/core/hpack/hpack_output_stream.h" #include "net/third_party/quiche/src/spdy/core/spdy_protocol.h" #include "net/third_party/quiche/src/spdy/platform/api/spdy_export.h" -#include "net/third_party/quiche/src/spdy/platform/api/spdy_string.h" #include "net/third_party/quiche/src/spdy/platform/api/spdy_string_piece.h" // An HpackEncoder encodes header sets as outlined in @@ -53,7 +53,7 @@ class SPDY_EXPORT_PRIVATE HpackEncoder { // Encodes the given header set into the given string. Returns // whether or not the encoding was successful. - bool EncodeHeaderSet(const SpdyHeaderBlock& header_set, SpdyString* output); + bool EncodeHeaderSet(const SpdyHeaderBlock& header_set, std::string* output); class SPDY_EXPORT_PRIVATE ProgressiveEncoder { public: @@ -64,7 +64,7 @@ class SPDY_EXPORT_PRIVATE HpackEncoder { // Encodes up to max_encoded_bytes of the current header block into the // given output string. - virtual void Next(size_t max_encoded_bytes, SpdyString* output) = 0; + virtual void Next(size_t max_encoded_bytes, std::string* output) = 0; }; // Returns a ProgressiveEncoder which must be outlived by both the given @@ -106,7 +106,7 @@ class SPDY_EXPORT_PRIVATE HpackEncoder { class Encoderator; // Encodes a sequence of header name-value pairs as a single header block. - void EncodeRepresentations(RepresentationIterator* iter, SpdyString* output); + void EncodeRepresentations(RepresentationIterator* iter, std::string* output); // Emits a static/dynamic indexed representation (Section 7.1). void EmitIndex(const HpackEntry* entry); diff --git a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_encoder_test.cc b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_encoder_test.cc index 4a70dc06a05..4683ccf8f97 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_encoder_test.cc +++ b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_encoder_test.cc @@ -42,7 +42,9 @@ class HpackEncoderPeer { return encoder_->huffman_table_; } void EmitString(SpdyStringPiece str) { encoder_->EmitString(str); } - void TakeString(SpdyString* out) { encoder_->output_stream_.TakeString(out); } + void TakeString(std::string* out) { + encoder_->output_stream_.TakeString(out); + } static void CookieToCrumbs(SpdyStringPiece cookie, std::vector<SpdyStringPiece>* out) { Representations tmp; @@ -69,7 +71,7 @@ class HpackEncoderPeer { // non-incremental encoding path. static bool EncodeHeaderSet(HpackEncoder* encoder, const SpdyHeaderBlock& header_set, - SpdyString* output, + std::string* output, bool use_incremental) { if (use_incremental) { return EncodeIncremental(encoder, header_set, output); @@ -80,14 +82,14 @@ class HpackEncoderPeer { static bool EncodeIncremental(HpackEncoder* encoder, const SpdyHeaderBlock& header_set, - SpdyString* output) { + std::string* output) { std::unique_ptr<HpackEncoder::ProgressiveEncoder> encoderator = encoder->EncodeHeaderSet(header_set); - SpdyString output_buffer; + std::string output_buffer; http2::test::Http2Random random; encoderator->Next(random.UniformInRange(0, 16), &output_buffer); while (encoderator->HasNext()) { - SpdyString second_buffer; + std::string second_buffer; encoderator->Next(random.UniformInRange(0, 16), &second_buffer); output_buffer.append(second_buffer); } @@ -180,7 +182,7 @@ class HpackEncoderTest : public ::testing::TestWithParam<bool> { expected_.AppendUint32(size); } void CompareWithExpectedEncoding(const SpdyHeaderBlock& header_set) { - SpdyString expected_out, actual_out; + std::string expected_out, actual_out; expected_.TakeString(&expected_out); EXPECT_TRUE(test::HpackEncoderPeer::EncodeHeaderSet( &encoder_, header_set, &actual_out, use_incremental_)); @@ -318,7 +320,7 @@ TEST_P(HpackEncoderTest, StringsDynamicallySelectHuffmanCoding) { expected_.AppendUint32(6); expected_.AppendBytes("@@@@@@"); - SpdyString expected_out, actual_out; + std::string expected_out, actual_out; expected_.TakeString(&expected_out); peer_.TakeString(&actual_out); EXPECT_EQ(expected_out, actual_out); @@ -507,7 +509,7 @@ TEST_P(HpackEncoderTest, DecomposeRepresentation) { TEST_P(HpackEncoderTest, CrumbleNullByteDelimitedValue) { SpdyHeaderBlock headers; // A header field to be crumbled: "spam: foo\0bar". - headers["spam"] = SpdyString("foo\0bar", 7); + headers["spam"] = std::string("foo\0bar", 7); ExpectIndexedLiteral("spam", "foo"); expected_.AppendPrefix(kLiteralIncrementalIndexOpcode); diff --git a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_entry.cc b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_entry.cc index 46e608615aa..0965739dc0e 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_entry.cc +++ b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_entry.cc @@ -75,7 +75,7 @@ size_t HpackEntry::Size() const { return Size(name(), value()); } -SpdyString HpackEntry::GetDebugString() const { +std::string HpackEntry::GetDebugString() const { return SpdyStrCat( "{ name: \"", name_ref_, "\", value: \"", value_ref_, "\", index: ", insertion_index_, " ", diff --git a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_entry.h b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_entry.h index 61103fddd8e..50ac3a78e5f 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_entry.h +++ b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_entry.h @@ -7,9 +7,9 @@ #include <cstddef> #include <cstdint> +#include <string> #include "net/third_party/quiche/src/spdy/platform/api/spdy_export.h" -#include "net/third_party/quiche/src/spdy/platform/api/spdy_string.h" #include "net/third_party/quiche/src/spdy/platform/api/spdy_string_piece.h" // All section references below are to @@ -70,7 +70,7 @@ class SPDY_EXPORT_PRIVATE HpackEntry { static size_t Size(SpdyStringPiece name, SpdyStringPiece value); size_t Size() const; - SpdyString GetDebugString() const; + std::string GetDebugString() const; int64_t time_added() const { return time_added_; } void set_time_added(int64_t now) { time_added_ = now; } @@ -86,8 +86,8 @@ class SPDY_EXPORT_PRIVATE HpackEntry { }; // These members are not used for LOOKUP entries. - SpdyString name_; - SpdyString value_; + std::string name_; + std::string value_; // These members are always valid. For DYNAMIC and STATIC entries, they // always point to |name_| and |value_|. diff --git a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_entry_test.cc b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_entry_test.cc index 224ac187013..316fcb92359 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_entry_test.cc +++ b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_entry_test.cc @@ -42,7 +42,7 @@ class HpackEntryTest : public ::testing::Test { return name_.size() + value_.size() + HpackEntry::kSizeOverhead; } - SpdyString name_, value_; + std::string name_, value_; private: // Referenced by HpackEntry instances. diff --git a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_header_table_test.cc b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_header_table_test.cc index 4001174dd1e..a7d24821726 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_header_table_test.cc +++ b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_header_table_test.cc @@ -7,11 +7,11 @@ #include <algorithm> #include <cstdint> #include <set> +#include <string> #include <vector> #include "net/third_party/quiche/src/spdy/core/hpack/hpack_constants.h" #include "net/third_party/quiche/src/spdy/core/hpack/hpack_entry.h" -#include "net/third_party/quiche/src/spdy/platform/api/spdy_string.h" #include "net/third_party/quiche/src/spdy/platform/api/spdy_test.h" namespace spdy { @@ -75,8 +75,8 @@ class HpackHeaderTableTest : public ::testing::Test { // Returns an entry whose Size() is equal to the given one. static HpackEntry MakeEntryOfSize(uint32_t size) { EXPECT_GE(size, HpackEntry::kSizeOverhead); - SpdyString name((size - HpackEntry::kSizeOverhead) / 2, 'n'); - SpdyString value(size - HpackEntry::kSizeOverhead - name.size(), 'v'); + std::string name((size - HpackEntry::kSizeOverhead) / 2, 'n'); + std::string value(size - HpackEntry::kSizeOverhead - name.size(), 'v'); HpackEntry entry(name, value, false, 0); EXPECT_EQ(size, entry.Size()); return entry; @@ -121,7 +121,7 @@ class HpackHeaderTableTest : public ::testing::Test { } } - HpackEntry DynamicEntry(const SpdyString& name, const SpdyString& value) { + HpackEntry DynamicEntry(const std::string& name, const std::string& value) { peer_.AddDynamicEntry(name, value); return peer_.dynamic_entries().back(); } @@ -256,7 +256,7 @@ TEST_F(HpackHeaderTableTest, EntryIndexing) { } TEST_F(HpackHeaderTableTest, SetSizes) { - SpdyString key = "key", value = "value"; + std::string key = "key", value = "value"; const HpackEntry* entry1 = table_.TryAddEntry(key, value); const HpackEntry* entry2 = table_.TryAddEntry(key, value); const HpackEntry* entry3 = table_.TryAddEntry(key, value); @@ -288,7 +288,7 @@ TEST_F(HpackHeaderTableTest, SetSizes) { } TEST_F(HpackHeaderTableTest, EvictionCountForEntry) { - SpdyString key = "key", value = "value"; + std::string key = "key", value = "value"; const HpackEntry* entry1 = table_.TryAddEntry(key, value); const HpackEntry* entry2 = table_.TryAddEntry(key, value); size_t entry3_size = HpackEntry::Size(key, value); @@ -305,7 +305,7 @@ TEST_F(HpackHeaderTableTest, EvictionCountForEntry) { } TEST_F(HpackHeaderTableTest, EvictionCountToReclaim) { - SpdyString key = "key", value = "value"; + std::string key = "key", value = "value"; const HpackEntry* entry1 = table_.TryAddEntry(key, value); const HpackEntry* entry2 = table_.TryAddEntry(key, value); diff --git a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_huffman_table.h b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_huffman_table.h index 80d9e5262b7..a01c62f822d 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_huffman_table.h +++ b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_huffman_table.h @@ -11,7 +11,6 @@ #include "net/third_party/quiche/src/spdy/core/hpack/hpack_constants.h" #include "net/third_party/quiche/src/spdy/platform/api/spdy_export.h" -#include "net/third_party/quiche/src/spdy/platform/api/spdy_string.h" #include "net/third_party/quiche/src/spdy/platform/api/spdy_string_piece.h" namespace spdy { diff --git a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_huffman_table_test.cc b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_huffman_table_test.cc index efade416d8a..de4d30c4fd1 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_huffman_table_test.cc +++ b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_huffman_table_test.cc @@ -4,6 +4,7 @@ #include "net/third_party/quiche/src/spdy/core/hpack/hpack_huffman_table.h" +#include <string> #include <utility> #include "net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_decoder.h" @@ -41,8 +42,8 @@ class GenericHuffmanTableTest : public ::testing::Test { protected: GenericHuffmanTableTest() : table_(), peer_(table_) {} - SpdyString EncodeString(SpdyStringPiece input) { - SpdyString result; + std::string EncodeString(SpdyStringPiece input) { + std::string result; HpackOutputStream output_stream; table_.EncodeString(input, &output_stream); @@ -188,7 +189,7 @@ class HpackHuffmanTableTest : public GenericHuffmanTableTest { } // Use http2::HpackHuffmanDecoder for roundtrip tests. - void DecodeString(const SpdyString& encoded, SpdyString* out) { + void DecodeString(const std::string& encoded, std::string* out) { http2::HpackHuffmanDecoder decoder; out->clear(); EXPECT_TRUE(decoder.Decode(encoded, out)); @@ -200,8 +201,8 @@ TEST_F(HpackHuffmanTableTest, InitializeHpackCode) { } TEST_F(HpackHuffmanTableTest, SpecRequestExamples) { - SpdyString buffer; - SpdyString test_table[] = { + std::string buffer; + std::string test_table[] = { SpdyHexDecode("f1e3c2e5f23a6ba0ab90f4ff"), "www.example.com", SpdyHexDecode("a8eb10649cbf"), @@ -213,8 +214,8 @@ TEST_F(HpackHuffmanTableTest, SpecRequestExamples) { }; // Round-trip each test example. for (size_t i = 0; i != SPDY_ARRAYSIZE(test_table); i += 2) { - const SpdyString& encodedFixture(test_table[i]); - const SpdyString& decodedFixture(test_table[i + 1]); + const std::string& encodedFixture(test_table[i]); + const std::string& decodedFixture(test_table[i + 1]); DecodeString(encodedFixture, &buffer); EXPECT_EQ(decodedFixture, buffer); buffer = EncodeString(decodedFixture); @@ -223,8 +224,8 @@ TEST_F(HpackHuffmanTableTest, SpecRequestExamples) { } TEST_F(HpackHuffmanTableTest, SpecResponseExamples) { - SpdyString buffer; - SpdyString test_table[] = { + std::string buffer; + std::string test_table[] = { SpdyHexDecode("6402"), "302", SpdyHexDecode("aec3771a4b"), @@ -242,8 +243,8 @@ TEST_F(HpackHuffmanTableTest, SpecResponseExamples) { }; // Round-trip each test example. for (size_t i = 0; i != SPDY_ARRAYSIZE(test_table); i += 2) { - const SpdyString& encodedFixture(test_table[i]); - const SpdyString& decodedFixture(test_table[i + 1]); + const std::string& encodedFixture(test_table[i]); + const std::string& decodedFixture(test_table[i + 1]); DecodeString(encodedFixture, &buffer); EXPECT_EQ(decodedFixture, buffer); buffer = EncodeString(decodedFixture); @@ -256,8 +257,8 @@ TEST_F(HpackHuffmanTableTest, RoundTripIndividualSymbols) { char c = static_cast<char>(i); char storage[3] = {c, c, c}; SpdyStringPiece input(storage, SPDY_ARRAYSIZE(storage)); - SpdyString buffer_in = EncodeString(input); - SpdyString buffer_out; + std::string buffer_in = EncodeString(input); + std::string buffer_out; DecodeString(buffer_in, &buffer_out); EXPECT_EQ(input, buffer_out); } @@ -270,21 +271,21 @@ TEST_F(HpackHuffmanTableTest, RoundTripSymbolSequence) { storage[511 - i] = static_cast<char>(i); } SpdyStringPiece input(storage, SPDY_ARRAYSIZE(storage)); - SpdyString buffer_in = EncodeString(input); - SpdyString buffer_out; + std::string buffer_in = EncodeString(input); + std::string buffer_out; DecodeString(buffer_in, &buffer_out); EXPECT_EQ(input, buffer_out); } TEST_F(HpackHuffmanTableTest, EncodedSizeAgreesWithEncodeString) { - SpdyString test_table[] = { + std::string test_table[] = { "", "Mon, 21 Oct 2013 20:13:21 GMT", "https://www.example.com", "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1", - SpdyString(1, '\0'), - SpdyString("foo\0bar", 7), - SpdyString(256, '\0'), + std::string(1, '\0'), + std::string("foo\0bar", 7), + std::string(256, '\0'), }; for (size_t i = 0; i != 256; ++i) { // Expand last |test_table| entry to cover all codes. @@ -292,7 +293,7 @@ TEST_F(HpackHuffmanTableTest, EncodedSizeAgreesWithEncodeString) { } HpackOutputStream output_stream; - SpdyString encoding; + std::string encoding; for (size_t i = 0; i != SPDY_ARRAYSIZE(test_table); ++i) { table_.EncodeString(test_table[i], &output_stream); output_stream.TakeString(&encoding); diff --git a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_output_stream.cc b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_output_stream.cc index fc01c3f3bc4..4ceaece2c6e 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_output_stream.cc +++ b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_output_stream.cc @@ -63,7 +63,7 @@ void HpackOutputStream::AppendUint32(uint32_t I) { } } -void HpackOutputStream::TakeString(SpdyString* output) { +void HpackOutputStream::TakeString(std::string* output) { // This must hold, since all public functions cause the buffer to // end on a byte boundary. DCHECK_EQ(bit_offset_, 0u); @@ -72,10 +72,11 @@ void HpackOutputStream::TakeString(SpdyString* output) { bit_offset_ = 0; } -void HpackOutputStream::BoundedTakeString(size_t max_size, SpdyString* output) { +void HpackOutputStream::BoundedTakeString(size_t max_size, + std::string* output) { if (buffer_.size() > max_size) { // Save off overflow bytes to temporary string (causes a copy). - SpdyString overflow(buffer_.data() + max_size, buffer_.size() - max_size); + std::string overflow(buffer_.data() + max_size, buffer_.size() - max_size); // Resize buffer down to the given limit. buffer_.resize(max_size); diff --git a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_output_stream.h b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_output_stream.h index 01631468283..5e6513923d8 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_output_stream.h +++ b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_output_stream.h @@ -7,10 +7,10 @@ #include <cstdint> #include <map> +#include <string> #include "net/third_party/quiche/src/spdy/core/hpack/hpack_constants.h" #include "net/third_party/quiche/src/spdy/platform/api/spdy_export.h" -#include "net/third_party/quiche/src/spdy/platform/api/spdy_string.h" #include "net/third_party/quiche/src/spdy/platform/api/spdy_string_piece.h" // All section references below are to @@ -49,11 +49,11 @@ class SPDY_EXPORT_PRIVATE HpackOutputStream { void AppendUint32(uint32_t I); // Swaps the internal buffer with |output|, then resets state. - void TakeString(SpdyString* output); + void TakeString(std::string* output); // Gives up to |max_size| bytes of the internal buffer to |output|. Resets // internal state with the overflow. - void BoundedTakeString(size_t max_size, SpdyString* output); + void BoundedTakeString(size_t max_size, std::string* output); // Size in bytes of stream's internal buffer. size_t size() const { return buffer_.size(); } @@ -63,7 +63,7 @@ class SPDY_EXPORT_PRIVATE HpackOutputStream { private: // The internal bit buffer. - SpdyString buffer_; + std::string buffer_; // If 0, the buffer ends on a byte boundary. If non-zero, the buffer // ends on the nth most significant bit. Guaranteed to be < 8. diff --git a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_output_stream_test.cc b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_output_stream_test.cc index 9e1988d1bbb..494241764fa 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_output_stream_test.cc +++ b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_output_stream_test.cc @@ -16,7 +16,7 @@ namespace { // significant bit, and that it can handle crossing a byte boundary. TEST(HpackOutputStreamTest, AppendBits) { HpackOutputStream output_stream; - SpdyString expected_str; + std::string expected_str; output_stream.AppendBits(0x1, 1); expected_str.append(1, 0x00); @@ -37,20 +37,20 @@ TEST(HpackOutputStreamTest, AppendBits) { output_stream.AppendBits(0x0, 7); - SpdyString str; + std::string str; output_stream.TakeString(&str); EXPECT_EQ(expected_str, str); } // Utility function to return I as a string encoded with an N-bit // prefix. -SpdyString EncodeUint32(uint8_t N, uint32_t I) { +std::string EncodeUint32(uint8_t N, uint32_t I) { HpackOutputStream output_stream; if (N < 8) { output_stream.AppendBits(0x00, 8 - N); } output_stream.AppendUint32(I); - SpdyString str; + std::string str; output_stream.TakeString(&str); return str; } @@ -61,7 +61,7 @@ SpdyString EncodeUint32(uint8_t N, uint32_t I) { TEST(HpackOutputStreamTest, OneByteIntegersEightBitPrefix) { // Minimum. - EXPECT_EQ(SpdyString("\x00", 1), EncodeUint32(8, 0x00)); + EXPECT_EQ(std::string("\x00", 1), EncodeUint32(8, 0x00)); EXPECT_EQ("\x7f", EncodeUint32(8, 0x7f)); // Maximum. EXPECT_EQ("\xfe", EncodeUint32(8, 0xfe)); @@ -69,7 +69,7 @@ TEST(HpackOutputStreamTest, OneByteIntegersEightBitPrefix) { TEST(HpackOutputStreamTest, TwoByteIntegersEightBitPrefix) { // Minimum. - EXPECT_EQ(SpdyString("\xff\x00", 2), EncodeUint32(8, 0xff)); + EXPECT_EQ(std::string("\xff\x00", 2), EncodeUint32(8, 0xff)); EXPECT_EQ("\xff\x01", EncodeUint32(8, 0x0100)); // Maximum. EXPECT_EQ("\xff\x7f", EncodeUint32(8, 0x017e)); @@ -112,13 +112,13 @@ TEST(HpackOutputStreamTest, SixByteIntegersEightBitPrefix) { TEST(HpackOutputStreamTest, OneByteIntegersOneToSevenBitPrefixes) { // Minimums. - EXPECT_EQ(SpdyString("\x00", 1), EncodeUint32(7, 0x00)); - EXPECT_EQ(SpdyString("\x00", 1), EncodeUint32(6, 0x00)); - EXPECT_EQ(SpdyString("\x00", 1), EncodeUint32(5, 0x00)); - EXPECT_EQ(SpdyString("\x00", 1), EncodeUint32(4, 0x00)); - EXPECT_EQ(SpdyString("\x00", 1), EncodeUint32(3, 0x00)); - EXPECT_EQ(SpdyString("\x00", 1), EncodeUint32(2, 0x00)); - EXPECT_EQ(SpdyString("\x00", 1), EncodeUint32(1, 0x00)); + EXPECT_EQ(std::string("\x00", 1), EncodeUint32(7, 0x00)); + EXPECT_EQ(std::string("\x00", 1), EncodeUint32(6, 0x00)); + EXPECT_EQ(std::string("\x00", 1), EncodeUint32(5, 0x00)); + EXPECT_EQ(std::string("\x00", 1), EncodeUint32(4, 0x00)); + EXPECT_EQ(std::string("\x00", 1), EncodeUint32(3, 0x00)); + EXPECT_EQ(std::string("\x00", 1), EncodeUint32(2, 0x00)); + EXPECT_EQ(std::string("\x00", 1), EncodeUint32(1, 0x00)); // Maximums. EXPECT_EQ("\x7e", EncodeUint32(7, 0x7e)); @@ -127,18 +127,18 @@ TEST(HpackOutputStreamTest, OneByteIntegersOneToSevenBitPrefixes) { EXPECT_EQ("\x0e", EncodeUint32(4, 0x0e)); EXPECT_EQ("\x06", EncodeUint32(3, 0x06)); EXPECT_EQ("\x02", EncodeUint32(2, 0x02)); - EXPECT_EQ(SpdyString("\x00", 1), EncodeUint32(1, 0x00)); + EXPECT_EQ(std::string("\x00", 1), EncodeUint32(1, 0x00)); } TEST(HpackOutputStreamTest, TwoByteIntegersOneToSevenBitPrefixes) { // Minimums. - EXPECT_EQ(SpdyString("\x7f\x00", 2), EncodeUint32(7, 0x7f)); - EXPECT_EQ(SpdyString("\x3f\x00", 2), EncodeUint32(6, 0x3f)); - EXPECT_EQ(SpdyString("\x1f\x00", 2), EncodeUint32(5, 0x1f)); - EXPECT_EQ(SpdyString("\x0f\x00", 2), EncodeUint32(4, 0x0f)); - EXPECT_EQ(SpdyString("\x07\x00", 2), EncodeUint32(3, 0x07)); - EXPECT_EQ(SpdyString("\x03\x00", 2), EncodeUint32(2, 0x03)); - EXPECT_EQ(SpdyString("\x01\x00", 2), EncodeUint32(1, 0x01)); + EXPECT_EQ(std::string("\x7f\x00", 2), EncodeUint32(7, 0x7f)); + EXPECT_EQ(std::string("\x3f\x00", 2), EncodeUint32(6, 0x3f)); + EXPECT_EQ(std::string("\x1f\x00", 2), EncodeUint32(5, 0x1f)); + EXPECT_EQ(std::string("\x0f\x00", 2), EncodeUint32(4, 0x0f)); + EXPECT_EQ(std::string("\x07\x00", 2), EncodeUint32(3, 0x07)); + EXPECT_EQ(std::string("\x03\x00", 2), EncodeUint32(2, 0x03)); + EXPECT_EQ(std::string("\x01\x00", 2), EncodeUint32(1, 0x01)); // Maximums. EXPECT_EQ("\x7f\x7f", EncodeUint32(7, 0xfe)); @@ -236,9 +236,9 @@ TEST(HpackOutputStreamTest, AppendUint32PreservesUpperBits) { HpackOutputStream output_stream; output_stream.AppendBits(0x7f, 7); output_stream.AppendUint32(0x01); - SpdyString str; + std::string str; output_stream.TakeString(&str); - EXPECT_EQ(SpdyString("\xff\x00", 2), str); + EXPECT_EQ(std::string("\xff\x00", 2), str); } TEST(HpackOutputStreamTest, AppendBytes) { @@ -247,7 +247,7 @@ TEST(HpackOutputStreamTest, AppendBytes) { output_stream.AppendBytes("buffer1"); output_stream.AppendBytes("buffer2"); - SpdyString str; + std::string str; output_stream.TakeString(&str); EXPECT_EQ("buffer1buffer2", str); } @@ -258,7 +258,7 @@ TEST(HpackOutputStreamTest, BoundedTakeString) { output_stream.AppendBytes("buffer12"); output_stream.AppendBytes("buffer456"); - SpdyString str; + std::string str; output_stream.BoundedTakeString(9, &str); EXPECT_EQ("buffer12b", str); diff --git a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_round_trip_test.cc b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_round_trip_test.cc index c035812bcf5..bae0fd2e7ef 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_round_trip_test.cc +++ b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_round_trip_test.cc @@ -12,7 +12,6 @@ #include "net/third_party/quiche/src/spdy/core/hpack/hpack_decoder_adapter.h" #include "net/third_party/quiche/src/spdy/core/hpack/hpack_encoder.h" #include "net/third_party/quiche/src/spdy/core/spdy_test_utils.h" -#include "net/third_party/quiche/src/spdy/platform/api/spdy_string.h" #include "net/third_party/quiche/src/spdy/platform/api/spdy_test.h" namespace spdy { @@ -34,7 +33,7 @@ class HpackRoundTripTest : public ::testing::TestWithParam<InputSizeParam> { } bool RoundTrip(const SpdyHeaderBlock& header_set) { - SpdyString encoded; + std::string encoded; encoder_.EncodeHeaderSet(header_set, &encoded); bool success = true; @@ -111,7 +110,7 @@ TEST_P(HpackRoundTripTest, ResponseFixtures) { headers["set-cookie"] = "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU;" " max-age=3600; version=1"; - headers["multivalue"] = SpdyString("foo\0bar", 7); + headers["multivalue"] = std::string("foo\0bar", 7); EXPECT_TRUE(RoundTrip(headers)); } } @@ -144,7 +143,7 @@ TEST_P(HpackRoundTripTest, RequestFixtures) { headers[":scheme"] = "https"; headers["custom-key"] = "custom-value"; headers["cookie"] = "baz=bing; fizzle=fazzle; garbage"; - headers["multivalue"] = SpdyString("foo\0bar", 7); + headers["multivalue"] = std::string("foo\0bar", 7); EXPECT_TRUE(RoundTrip(headers)); } } @@ -153,7 +152,7 @@ TEST_P(HpackRoundTripTest, RandomizedExamples) { // Grow vectors of names & values, which are seeded with fixtures and then // expanded with dynamically generated data. Samples are taken using the // exponential distribution. - std::vector<SpdyString> pseudo_header_names, random_header_names; + std::vector<std::string> pseudo_header_names, random_header_names; pseudo_header_names.push_back(":authority"); pseudo_header_names.push_back(":path"); pseudo_header_names.push_back(":status"); @@ -161,7 +160,7 @@ TEST_P(HpackRoundTripTest, RandomizedExamples) { // TODO(jgraettinger): Enable "cookie" as a name fixture. Crumbs may be // reconstructed in any order, which breaks the simple validation used here. - std::vector<SpdyString> values; + std::vector<std::string> values; values.push_back("/"); values.push_back("/index.html"); values.push_back("200"); @@ -180,7 +179,7 @@ TEST_P(HpackRoundTripTest, RandomizedExamples) { std::min(header_count, 1 + SampleExponential(7, 50)); EXPECT_LE(pseudo_header_count, header_count); for (size_t j = 0; j != header_count; ++j) { - SpdyString name, value; + std::string name, value; // Pseudo headers must be added before regular headers. if (j < pseudo_header_count) { // Choose one of the defined pseudo headers at random. @@ -204,7 +203,8 @@ TEST_P(HpackRoundTripTest, RandomizedExamples) { // Randomly reuse an existing value, or generate a new one. size_t value_index = SampleExponential(20, 200); if (value_index >= values.size()) { - SpdyString newvalue = random_.RandString(1 + SampleExponential(15, 75)); + std::string newvalue = + random_.RandString(1 + SampleExponential(15, 75)); // Currently order is not preserved in the encoder. In particular, // when a value is decomposed at \0 delimiters, its parts might get // encoded out of order if some but not all of them already exist in diff --git a/chromium/net/third_party/quiche/src/spdy/core/http2_frame_decoder_adapter.cc b/chromium/net/third_party/quiche/src/spdy/core/http2_frame_decoder_adapter.cc index ac534965536..d5b20f07393 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/http2_frame_decoder_adapter.cc +++ b/chromium/net/third_party/quiche/src/spdy/core/http2_frame_decoder_adapter.cc @@ -21,7 +21,6 @@ #include "net/third_party/quiche/src/http2/decoder/http2_frame_decoder_listener.h" #include "net/third_party/quiche/src/http2/http2_constants.h" #include "net/third_party/quiche/src/http2/http2_structures.h" -#include "net/third_party/quiche/src/http2/platform/api/http2_string.h" #include "net/third_party/quiche/src/spdy/core/hpack/hpack_decoder_adapter.h" #include "net/third_party/quiche/src/spdy/core/hpack/hpack_header_table.h" #include "net/third_party/quiche/src/spdy/core/spdy_alt_svc_wire_format.h" diff --git a/chromium/net/third_party/quiche/src/spdy/core/http2_frame_decoder_adapter.h b/chromium/net/third_party/quiche/src/spdy/core/http2_frame_decoder_adapter.h index 3c05858672f..93da6844095 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/http2_frame_decoder_adapter.h +++ b/chromium/net/third_party/quiche/src/spdy/core/http2_frame_decoder_adapter.h @@ -9,6 +9,7 @@ #include <cstdint> #include <memory> +#include <string> #include "net/third_party/quiche/src/http2/decoder/http2_frame_decoder.h" #include "net/third_party/quiche/src/http2/platform/api/http2_optional.h" @@ -251,8 +252,8 @@ class SPDY_EXPORT_PRIVATE Http2DecoderAdapter Http2Optional<size_t> opt_pad_length_; // Temporary buffers for the AltSvc fields. - Http2String alt_svc_origin_; - Http2String alt_svc_value_; + std::string alt_svc_origin_; + std::string alt_svc_value_; // Listener used if we transition to an error state; the listener ignores all // the callbacks. diff --git a/chromium/net/third_party/quiche/src/spdy/core/http2_priority_write_scheduler.h b/chromium/net/third_party/quiche/src/spdy/core/http2_priority_write_scheduler.h index d08d125f933..125583926c1 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/http2_priority_write_scheduler.h +++ b/chromium/net/third_party/quiche/src/spdy/core/http2_priority_write_scheduler.h @@ -11,6 +11,7 @@ #include <memory> #include <queue> #include <set> +#include <string> #include <tuple> #include <unordered_map> #include <utility> @@ -24,7 +25,6 @@ #include "net/third_party/quiche/src/spdy/platform/api/spdy_logging.h" #include "net/third_party/quiche/src/spdy/platform/api/spdy_map_util.h" #include "net/third_party/quiche/src/spdy/platform/api/spdy_ptr_util.h" -#include "net/third_party/quiche/src/spdy/platform/api/spdy_string.h" #include "net/third_party/quiche/src/spdy/platform/api/spdy_string_utils.h" namespace spdy { @@ -77,7 +77,7 @@ class Http2PriorityWriteScheduler : public WriteScheduler<StreamIdType> { size_t NumReadyStreams() const override; bool IsStreamReady(StreamIdType stream_id) const override; size_t NumRegisteredStreams() const override; - SpdyString DebugString() const override; + std::string DebugString() const override; private: friend class test::Http2PriorityWriteSchedulerPeer<StreamIdType>; @@ -111,6 +111,16 @@ class Http2PriorityWriteScheduler : public WriteScheduler<StreamIdType> { // Time of latest write event for stream of this priority, in microseconds. int64_t last_event_time_usec = 0; + // Returns true if this stream is ancestor of |other|. + bool IsAncestorOf(const StreamInfo& other) const { + for (const StreamInfo* p = other.parent; p != nullptr; p = p->parent) { + if (p == this) { + return true; + } + } + return false; + } + // Whether this stream should be scheduled ahead of another stream. bool SchedulesBefore(const StreamInfo& other) const { return (priority != other.priority) ? priority > other.priority @@ -394,8 +404,10 @@ void Http2PriorityWriteScheduler<StreamIdType>::UpdateStreamParent( return; } - // If the new parent is already the stream's parent, we're done. - if (stream_info->parent == new_parent) { + if (stream_info->parent == new_parent && + (!exclusive || new_parent->children.size() == 1u)) { + // If the new parent is already the stream's parent, and exclusivity (if + // specified) is already satisfied, we are done. return; } @@ -500,13 +512,20 @@ bool Http2PriorityWriteScheduler<StreamIdType>::ShouldYield( SPDY_BUG << "Stream " << stream_id << " not registered"; return false; } + if (HasReadyAncestor(*stream_info)) { + return true; + } for (const StreamInfo& scheduled : scheduling_queue_) { - if (stream_info == &scheduled) { - return false; + if (HasReadyAncestor(scheduled)) { + // Skip streams which cannot be scheduled. + continue; } - if (!HasReadyAncestor(scheduled)) { - return true; + if (stream_info->IsAncestorOf(scheduled)) { + // Do not yield to descendants. + return false; } + // Yield to streams with higher priorities. + return scheduled.SchedulesBefore(*stream_info); } return false; } @@ -694,7 +713,7 @@ size_t Http2PriorityWriteScheduler<StreamIdType>::NumRegisteredStreams() const { } template <typename StreamIdType> -SpdyString Http2PriorityWriteScheduler<StreamIdType>::DebugString() const { +std::string Http2PriorityWriteScheduler<StreamIdType>::DebugString() const { return SpdyStrCat("Http2PriorityWriteScheduler {num_registered_streams=", NumRegisteredStreams(), " num_ready_streams=", NumReadyStreams(), "}"); @@ -703,8 +722,8 @@ SpdyString Http2PriorityWriteScheduler<StreamIdType>::DebugString() const { template <typename StreamIdType> bool Http2PriorityWriteScheduler<StreamIdType>::ValidateInvariantsForTests() const { - int total_streams = 0; - int streams_visited = 0; + size_t total_streams = 0; + size_t streams_visited = 0; // Iterate through all streams in the map. for (const auto& kv : all_stream_infos_) { ++total_streams; diff --git a/chromium/net/third_party/quiche/src/spdy/core/http2_priority_write_scheduler_test.cc b/chromium/net/third_party/quiche/src/spdy/core/http2_priority_write_scheduler_test.cc index 484e59a3a74..bf9f92feb2b 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/http2_priority_write_scheduler_test.cc +++ b/chromium/net/third_party/quiche/src/spdy/core/http2_priority_write_scheduler_test.cc @@ -1,3 +1,7 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + #include "net/third_party/quiche/src/spdy/core/http2_priority_write_scheduler.h" #include <initializer_list> @@ -74,7 +78,7 @@ TEST_F(Http2PriorityWriteSchedulerTest, RegisterAndUnregisterStreams) { EXPECT_TRUE(scheduler_.StreamRegistered(5)); ASSERT_TRUE(scheduler_.StreamRegistered(13)); EXPECT_EQ(130, scheduler_.GetStreamPrecedence(13).weight()); - EXPECT_EQ(5, scheduler_.GetStreamPrecedence(13).parent_id()); + EXPECT_EQ(5u, scheduler_.GetStreamPrecedence(13).parent_id()); scheduler_.UnregisterStream(5); // Cannot remove a stream that has already been removed. @@ -88,7 +92,7 @@ TEST_F(Http2PriorityWriteSchedulerTest, RegisterAndUnregisterStreams) { // The parent stream 19 doesn't exist, so this should use 0 as parent stream: scheduler_.RegisterStream(7, SpdyStreamPrecedence(19, 70, false)); EXPECT_TRUE(scheduler_.StreamRegistered(7)); - EXPECT_EQ(0, scheduler_.GetStreamPrecedence(7).parent_id()); + EXPECT_EQ(0u, scheduler_.GetStreamPrecedence(7).parent_id()); // Now stream 7 already exists, so this should fail: EXPECT_SPDY_BUG( scheduler_.RegisterStream(7, SpdyStreamPrecedence(1, 70, false)), @@ -105,7 +109,7 @@ TEST_F(Http2PriorityWriteSchedulerTest, RegisterAndUnregisterStreams) { TEST_F(Http2PriorityWriteSchedulerTest, RegisterStreamWithSpdy3Priority) { EXPECT_FALSE(scheduler_.StreamRegistered(1)); scheduler_.RegisterStream(1, SpdyStreamPrecedence(3)); - EXPECT_EQ(0, scheduler_.NumReadyStreams()); + EXPECT_EQ(0u, scheduler_.NumReadyStreams()); EXPECT_TRUE(scheduler_.StreamRegistered(1)); EXPECT_EQ(3, scheduler_.GetStreamPrecedence(1).spdy3_priority()); EXPECT_EQ(147, scheduler_.GetStreamPrecedence(1).weight()); @@ -113,7 +117,6 @@ TEST_F(Http2PriorityWriteSchedulerTest, RegisterStreamWithSpdy3Priority) { EXPECT_THAT(scheduler_.GetStreamChildren(1), IsEmpty()); EXPECT_SPDY_BUG(scheduler_.RegisterStream(1, SpdyStreamPrecedence(4)), - "Expected HTTP/2 stream dependency|" "Stream 1 already registered"); EXPECT_EQ(3, scheduler_.GetStreamPrecedence(1).spdy3_priority()); } @@ -147,7 +150,7 @@ TEST_F(Http2PriorityWriteSchedulerTest, GetStreamParent) { EXPECT_EQ(kHttp2RootStreamId, scheduler_.GetStreamPrecedence(3).parent_id()); scheduler_.RegisterStream(2, SpdyStreamPrecedence(0, 20, false)); scheduler_.RegisterStream(3, SpdyStreamPrecedence(2, 30, false)); - EXPECT_EQ(2, scheduler_.GetStreamPrecedence(3).parent_id()); + EXPECT_EQ(2u, scheduler_.GetStreamPrecedence(3).parent_id()); scheduler_.UnregisterStream(3); EXPECT_EQ(kHttp2RootStreamId, scheduler_.GetStreamPrecedence(3).parent_id()); } @@ -426,16 +429,139 @@ TEST_F(Http2PriorityWriteSchedulerTest, ASSERT_TRUE(peer_.ValidateInvariants()); } +TEST_F(Http2PriorityWriteSchedulerTest, RegisterStreamParentExclusive) { + /* 0 + / \ + 1 2 + */ + scheduler_.RegisterStream(1, SpdyStreamPrecedence(0, 100, false)); + scheduler_.RegisterStream(2, SpdyStreamPrecedence(0, 100, false)); + /* 0 + | + 3 + / \ + 1 2 + */ + scheduler_.RegisterStream(3, SpdyStreamPrecedence(0, 100, true)); + EXPECT_THAT(scheduler_.GetStreamChildren(0), ElementsAre(3)); + EXPECT_THAT(scheduler_.GetStreamChildren(3), UnorderedElementsAre(1, 2)); + EXPECT_THAT(scheduler_.GetStreamChildren(1), IsEmpty()); + EXPECT_THAT(scheduler_.GetStreamChildren(2), IsEmpty()); + ASSERT_TRUE(peer_.ValidateInvariants()); +} + +TEST_F(Http2PriorityWriteSchedulerTest, UpdateStreamParentExclusive) { + /* 0 + /|\ + 1 2 3 + */ + scheduler_.RegisterStream(1, SpdyStreamPrecedence(0, 100, false)); + scheduler_.RegisterStream(2, SpdyStreamPrecedence(0, 100, false)); + scheduler_.RegisterStream(3, SpdyStreamPrecedence(0, 100, false)); + /* 0 + | + 1 + / \ + 2 3 + */ + scheduler_.UpdateStreamPrecedence(1, SpdyStreamPrecedence(0, 100, true)); + EXPECT_THAT(scheduler_.GetStreamChildren(0), ElementsAre(1)); + EXPECT_THAT(scheduler_.GetStreamChildren(1), UnorderedElementsAre(2, 3)); + EXPECT_THAT(scheduler_.GetStreamChildren(2), IsEmpty()); + EXPECT_THAT(scheduler_.GetStreamChildren(3), IsEmpty()); + ASSERT_TRUE(peer_.ValidateInvariants()); +} + +TEST_F(Http2PriorityWriteSchedulerTest, UpdateStreamParentExclusive2) { + /* 0 + | + 1 + / \ + 2 3 + / \ + 4 5 + | + 6 + */ + scheduler_.RegisterStream(1, SpdyStreamPrecedence(0, 100, false)); + scheduler_.RegisterStream(2, SpdyStreamPrecedence(1, 100, false)); + scheduler_.RegisterStream(3, SpdyStreamPrecedence(1, 100, false)); + scheduler_.RegisterStream(4, SpdyStreamPrecedence(3, 100, false)); + scheduler_.RegisterStream(5, SpdyStreamPrecedence(3, 100, false)); + scheduler_.RegisterStream(6, SpdyStreamPrecedence(4, 100, false)); + // Update stream 1's parent to 4 exclusive. + /* 0 + | + 4 + | + 1 + /|\ + 2 3 6 + | + 5 + */ + scheduler_.UpdateStreamPrecedence(1, SpdyStreamPrecedence(4, 100, true)); + EXPECT_THAT(scheduler_.GetStreamChildren(0), ElementsAre(4)); + EXPECT_THAT(scheduler_.GetStreamChildren(4), ElementsAre(1)); + EXPECT_THAT(scheduler_.GetStreamChildren(1), UnorderedElementsAre(2, 3, 6)); + EXPECT_THAT(scheduler_.GetStreamChildren(2), IsEmpty()); + EXPECT_THAT(scheduler_.GetStreamChildren(3), ElementsAre(5)); + EXPECT_THAT(scheduler_.GetStreamChildren(6), IsEmpty()); + ASSERT_TRUE(peer_.ValidateInvariants()); +} + +TEST_F(Http2PriorityWriteSchedulerTest, UpdateStreamParentNonExclusive) { + /* 0 + | + 1 + / \ + 2 3 + / \ + 4 5 + | + 6 + */ + scheduler_.RegisterStream(1, SpdyStreamPrecedence(0, 100, false)); + scheduler_.RegisterStream(2, SpdyStreamPrecedence(1, 100, false)); + scheduler_.RegisterStream(3, SpdyStreamPrecedence(1, 100, false)); + scheduler_.RegisterStream(4, SpdyStreamPrecedence(3, 100, false)); + scheduler_.RegisterStream(5, SpdyStreamPrecedence(3, 100, false)); + scheduler_.RegisterStream(6, SpdyStreamPrecedence(4, 100, false)); + // Update stream 1's parent to 4. + /* 0 + | + 4 + / \ + 6 1 + / \ + 2 3 + | + 5 + */ + scheduler_.UpdateStreamPrecedence(1, SpdyStreamPrecedence(4, 100, false)); + EXPECT_THAT(scheduler_.GetStreamChildren(0), ElementsAre(4)); + EXPECT_THAT(scheduler_.GetStreamChildren(4), UnorderedElementsAre(6, 1)); + EXPECT_THAT(scheduler_.GetStreamChildren(6), IsEmpty()); + EXPECT_THAT(scheduler_.GetStreamChildren(1), UnorderedElementsAre(2, 3)); + EXPECT_THAT(scheduler_.GetStreamChildren(2), IsEmpty()); + EXPECT_THAT(scheduler_.GetStreamChildren(3), ElementsAre(5)); + EXPECT_THAT(scheduler_.GetStreamChildren(5), IsEmpty()); + ASSERT_TRUE(peer_.ValidateInvariants()); +} + TEST_F(Http2PriorityWriteSchedulerTest, UpdateStreamParentToParent) { scheduler_.RegisterStream(1, SpdyStreamPrecedence(0, 100, false)); scheduler_.RegisterStream(2, SpdyStreamPrecedence(1, 100, false)); scheduler_.RegisterStream(3, SpdyStreamPrecedence(1, 100, false)); + EXPECT_THAT(scheduler_.GetStreamChildren(1), UnorderedElementsAre(2, 3)); + EXPECT_THAT(scheduler_.GetStreamChildren(2), IsEmpty()); + EXPECT_THAT(scheduler_.GetStreamChildren(3), IsEmpty()); for (bool exclusive : {true, false}) { scheduler_.UpdateStreamPrecedence(2, SpdyStreamPrecedence(1, 100, exclusive)); EXPECT_THAT(scheduler_.GetStreamChildren(0), ElementsAre(1)); - EXPECT_THAT(scheduler_.GetStreamChildren(1), UnorderedElementsAre(2, 3)); - EXPECT_THAT(scheduler_.GetStreamChildren(2), IsEmpty()); + EXPECT_THAT(scheduler_.GetStreamChildren(1), UnorderedElementsAre(2)); + EXPECT_THAT(scheduler_.GetStreamChildren(2), UnorderedElementsAre(3)); EXPECT_THAT(scheduler_.GetStreamChildren(3), IsEmpty()); } ASSERT_TRUE(peer_.ValidateInvariants()); @@ -486,22 +612,22 @@ TEST_F(Http2PriorityWriteSchedulerTest, BlockAndUnblock) { scheduler_.RegisterStream(13, SpdyStreamPrecedence(7, 100, true)); scheduler_.RegisterStream(14, SpdyStreamPrecedence(7, 100, false)); scheduler_.UpdateStreamPrecedence(7, SpdyStreamPrecedence(3, 100, false)); - EXPECT_EQ(0, scheduler_.GetStreamPrecedence(1).parent_id()); - EXPECT_EQ(0, scheduler_.GetStreamPrecedence(2).parent_id()); - EXPECT_EQ(0, scheduler_.GetStreamPrecedence(3).parent_id()); - EXPECT_EQ(1, scheduler_.GetStreamPrecedence(4).parent_id()); - EXPECT_EQ(1, scheduler_.GetStreamPrecedence(5).parent_id()); - EXPECT_EQ(2, scheduler_.GetStreamPrecedence(6).parent_id()); - EXPECT_EQ(3, scheduler_.GetStreamPrecedence(7).parent_id()); - EXPECT_EQ(4, scheduler_.GetStreamPrecedence(8).parent_id()); - EXPECT_EQ(4, scheduler_.GetStreamPrecedence(9).parent_id()); - EXPECT_EQ(5, scheduler_.GetStreamPrecedence(10).parent_id()); - EXPECT_EQ(5, scheduler_.GetStreamPrecedence(11).parent_id()); - EXPECT_EQ(6, scheduler_.GetStreamPrecedence(12).parent_id()); - EXPECT_EQ(7, scheduler_.GetStreamPrecedence(13).parent_id()); - EXPECT_EQ(7, scheduler_.GetStreamPrecedence(14).parent_id()); - EXPECT_EQ(8, scheduler_.GetStreamPrecedence(15).parent_id()); - EXPECT_EQ(8, scheduler_.GetStreamPrecedence(16).parent_id()); + EXPECT_EQ(0u, scheduler_.GetStreamPrecedence(1).parent_id()); + EXPECT_EQ(0u, scheduler_.GetStreamPrecedence(2).parent_id()); + EXPECT_EQ(0u, scheduler_.GetStreamPrecedence(3).parent_id()); + EXPECT_EQ(1u, scheduler_.GetStreamPrecedence(4).parent_id()); + EXPECT_EQ(1u, scheduler_.GetStreamPrecedence(5).parent_id()); + EXPECT_EQ(2u, scheduler_.GetStreamPrecedence(6).parent_id()); + EXPECT_EQ(3u, scheduler_.GetStreamPrecedence(7).parent_id()); + EXPECT_EQ(4u, scheduler_.GetStreamPrecedence(8).parent_id()); + EXPECT_EQ(4u, scheduler_.GetStreamPrecedence(9).parent_id()); + EXPECT_EQ(5u, scheduler_.GetStreamPrecedence(10).parent_id()); + EXPECT_EQ(5u, scheduler_.GetStreamPrecedence(11).parent_id()); + EXPECT_EQ(6u, scheduler_.GetStreamPrecedence(12).parent_id()); + EXPECT_EQ(7u, scheduler_.GetStreamPrecedence(13).parent_id()); + EXPECT_EQ(7u, scheduler_.GetStreamPrecedence(14).parent_id()); + EXPECT_EQ(8u, scheduler_.GetStreamPrecedence(15).parent_id()); + EXPECT_EQ(8u, scheduler_.GetStreamPrecedence(16).parent_id()); ASSERT_TRUE(peer_.ValidateInvariants()); EXPECT_EQ(peer_.TotalChildWeights(0), @@ -604,19 +730,19 @@ TEST_F(Http2PriorityWriteSchedulerTest, MarkReadyFrontAndBack) { for (int i = 1; i < 6; ++i) { scheduler_.MarkStreamReady(i, false); } - EXPECT_EQ(5, scheduler_.PopNextReadyStream()); - EXPECT_EQ(2, scheduler_.PopNextReadyStream()); + EXPECT_EQ(5u, scheduler_.PopNextReadyStream()); + EXPECT_EQ(2u, scheduler_.PopNextReadyStream()); scheduler_.MarkStreamReady(2, false); - EXPECT_EQ(3, scheduler_.PopNextReadyStream()); + EXPECT_EQ(3u, scheduler_.PopNextReadyStream()); scheduler_.MarkStreamReady(3, false); - EXPECT_EQ(4, scheduler_.PopNextReadyStream()); + EXPECT_EQ(4u, scheduler_.PopNextReadyStream()); scheduler_.MarkStreamReady(4, false); - EXPECT_EQ(2, scheduler_.PopNextReadyStream()); + EXPECT_EQ(2u, scheduler_.PopNextReadyStream()); scheduler_.MarkStreamReady(2, true); - EXPECT_EQ(2, scheduler_.PopNextReadyStream()); + EXPECT_EQ(2u, scheduler_.PopNextReadyStream()); scheduler_.MarkStreamReady(5, false); scheduler_.MarkStreamReady(2, true); - EXPECT_EQ(5, scheduler_.PopNextReadyStream()); + EXPECT_EQ(5u, scheduler_.PopNextReadyStream()); } // Add ready streams at front and back and pop them with @@ -653,6 +779,46 @@ TEST_F(Http2PriorityWriteSchedulerTest, PopNextReadyStreamAndPrecedence) { scheduler_.PopNextReadyStreamAndPrecedence()); } +TEST_F(Http2PriorityWriteSchedulerTest, ShouldYield) { + /* + 0 + /|\ + 1 2 3 + /|\ \ + 4 5 6 7 + | + 8 + + */ + scheduler_.RegisterStream(1, SpdyStreamPrecedence(0, 100, false)); + scheduler_.RegisterStream(2, SpdyStreamPrecedence(0, 100, false)); + scheduler_.RegisterStream(3, SpdyStreamPrecedence(0, 100, false)); + scheduler_.RegisterStream(4, SpdyStreamPrecedence(1, 100, false)); + scheduler_.RegisterStream(5, SpdyStreamPrecedence(1, 200, false)); + scheduler_.RegisterStream(6, SpdyStreamPrecedence(1, 255, false)); + scheduler_.RegisterStream(7, SpdyStreamPrecedence(2, 100, false)); + scheduler_.RegisterStream(8, SpdyStreamPrecedence(5, 100, false)); + + scheduler_.MarkStreamReady(5, false); + + for (int i = 1; i <= 8; ++i) { + // Verify only 4 and 8 should yield to 5. + if (i == 4 || i == 8) { + EXPECT_TRUE(scheduler_.ShouldYield(i)) << "stream_id: " << i; + } else { + EXPECT_FALSE(scheduler_.ShouldYield(i)) << "stream_id: " << i; + } + } + + // Marks streams 1 and 2 ready. + scheduler_.MarkStreamReady(1, false); + scheduler_.MarkStreamReady(2, false); + // 1 should not yield. + EXPECT_FALSE(scheduler_.ShouldYield(1)); + // Verify 2 should yield to 1. + EXPECT_TRUE(scheduler_.ShouldYield(2)); +} + class PopNextReadyStreamTest : public Http2PriorityWriteSchedulerTest { protected: void SetUp() override { diff --git a/chromium/net/third_party/quiche/src/spdy/core/lifo_write_scheduler.h b/chromium/net/third_party/quiche/src/spdy/core/lifo_write_scheduler.h new file mode 100644 index 00000000000..b532c860950 --- /dev/null +++ b/chromium/net/third_party/quiche/src/spdy/core/lifo_write_scheduler.h @@ -0,0 +1,208 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef QUICHE_SPDY_CORE_LIFO_WRITE_SCHEDULER_H_ +#define QUICHE_SPDY_CORE_LIFO_WRITE_SCHEDULER_H_ + +#include <map> +#include <set> +#include <string> + +#include "net/third_party/quiche/src/spdy/core/write_scheduler.h" +#include "net/third_party/quiche/src/spdy/platform/api/spdy_containers.h" +#include "net/third_party/quiche/src/spdy/platform/api/spdy_string_utils.h" + +namespace spdy { + +namespace test { + +template <typename StreamIdType> +class LifoWriteSchedulerPeer; + +} // namespace test + +// Create a write scheduler where the stream added last will have the highest +// priority. +template <typename StreamIdType> +class LifoWriteScheduler : public WriteScheduler<StreamIdType> { + public: + using typename WriteScheduler<StreamIdType>::StreamPrecedenceType; + + LifoWriteScheduler() = default; + + void RegisterStream(StreamIdType stream_id, + const StreamPrecedenceType& /*precedence*/) override; + + void UnregisterStream(StreamIdType stream_id) override; + + bool StreamRegistered(StreamIdType stream_id) const override { + return registered_streams_.find(stream_id) != registered_streams_.end(); + } + + // Stream precedence is not supported by this scheduler. + StreamPrecedenceType GetStreamPrecedence( + StreamIdType /*stream_id*/) const override { + return StreamPrecedenceType(kV3LowestPriority); + } + + void UpdateStreamPrecedence( + StreamIdType /*stream_id*/, + const StreamPrecedenceType& /*precedence*/) override {} + + std::vector<StreamIdType> GetStreamChildren( + StreamIdType /*stream_id*/) const override { + return std::vector<StreamIdType>(); + } + + void RecordStreamEventTime(StreamIdType stream_id, + int64_t now_in_usec) override; + + int64_t GetLatestEventWithPrecedence(StreamIdType stream_id) const override; + + StreamIdType PopNextReadyStream() override; + + std::tuple<StreamIdType, StreamPrecedenceType> + PopNextReadyStreamAndPrecedence() override { + return std::make_tuple(PopNextReadyStream(), + StreamPrecedenceType(kV3LowestPriority)); + } + + bool ShouldYield(StreamIdType stream_id) const override { + return !ready_streams_.empty() && stream_id < *ready_streams_.rbegin(); + } + + void MarkStreamReady(StreamIdType stream_id, bool /*add_to_front*/) override; + + void MarkStreamNotReady(StreamIdType stream_id) override; + + bool HasReadyStreams() const override { return !ready_streams_.empty(); } + size_t NumReadyStreams() const override { return ready_streams_.size(); } + bool IsStreamReady(StreamIdType stream_id) const override; + size_t NumRegisteredStreams() const override; + std::string DebugString() const override; + + private: + friend class test::LifoWriteSchedulerPeer<StreamIdType>; + + std::set<StreamIdType> ready_streams_; + std::map<StreamIdType, int64_t> registered_streams_; +}; + +template <typename StreamIdType> +void LifoWriteScheduler<StreamIdType>::RegisterStream( + StreamIdType stream_id, + const StreamPrecedenceType& /*precedence*/) { + if (StreamRegistered(stream_id)) { + SPDY_BUG << "Stream " << stream_id << " already registered"; + return; + } + registered_streams_.emplace_hint(registered_streams_.end(), stream_id, 0); +} + +template <typename StreamIdType> +void LifoWriteScheduler<StreamIdType>::UnregisterStream( + StreamIdType stream_id) { + if (!StreamRegistered(stream_id)) { + SPDY_BUG << "Stream " << stream_id << " is not registered"; + return; + } + registered_streams_.erase(stream_id); + ready_streams_.erase(stream_id); +} + +template <typename StreamIdType> +void LifoWriteScheduler<StreamIdType>::RecordStreamEventTime( + StreamIdType stream_id, + int64_t now_in_usec) { + auto it = registered_streams_.find(stream_id); + if (it != registered_streams_.end()) { + it->second = now_in_usec; + } else { + SPDY_BUG << "Stream " << stream_id << " is not registered"; + } +} + +template <typename StreamIdType> +int64_t LifoWriteScheduler<StreamIdType>::GetLatestEventWithPrecedence( + StreamIdType stream_id) const { + if (!StreamRegistered(stream_id)) { + SPDY_BUG << "Stream " << stream_id << " is not registered"; + return 0; + } + int64_t latest_event_time_us = 0; + for (auto it = registered_streams_.rbegin(); it != registered_streams_.rend(); + ++it) { + if (stream_id < it->first) { + if (it->second > latest_event_time_us) { + latest_event_time_us = it->second; + } + } else { + break; + } + } + return latest_event_time_us; +} + +template <typename StreamIdType> +StreamIdType LifoWriteScheduler<StreamIdType>::PopNextReadyStream() { + if (ready_streams_.empty()) { + SPDY_BUG << "No ready streams available"; + return 0; + } + auto it = --ready_streams_.end(); + StreamIdType id = *it; + ready_streams_.erase(it); + return id; +} + +template <typename StreamIdType> +void LifoWriteScheduler<StreamIdType>::MarkStreamReady(StreamIdType stream_id, + bool /*add_to_front*/) { + if (!StreamRegistered(stream_id)) { + SPDY_BUG << "Stream " << stream_id << " is not registered"; + return; + } + if (ready_streams_.find(stream_id) != ready_streams_.end()) { + SPDY_VLOG(1) << "Stream already exists in the list"; + return; + } + ready_streams_.insert(stream_id); +} + +template <typename StreamIdType> +void LifoWriteScheduler<StreamIdType>::MarkStreamNotReady( + StreamIdType stream_id) { + auto it = ready_streams_.find(stream_id); + if (it == ready_streams_.end()) { + SPDY_VLOG(1) << "Try to remove a stream that is not on list"; + return; + } + ready_streams_.erase(it); +} + +template <typename StreamIdType> +bool LifoWriteScheduler<StreamIdType>::IsStreamReady( + StreamIdType stream_id) const { + if (!StreamRegistered(stream_id)) { + SPDY_BUG << "Stream " << stream_id << " is not registered"; + return false; + } + return ready_streams_.find(stream_id) != ready_streams_.end(); +} + +template <typename StreamIdType> +size_t LifoWriteScheduler<StreamIdType>::NumRegisteredStreams() const { + return registered_streams_.size(); +} + +template <typename StreamIdType> +std::string LifoWriteScheduler<StreamIdType>::DebugString() const { + return SpdyStrCat( + "LifoWriteScheduler {num_streams=", registered_streams_.size(), + " num_ready_streams=", NumReadyStreams(), "}"); +} + +} // namespace spdy + +#endif // QUICHE_SPDY_CORE_LIFO_WRITE_SCHEDULER_H_ diff --git a/chromium/net/third_party/quiche/src/spdy/core/lifo_write_scheduler_test.cc b/chromium/net/third_party/quiche/src/spdy/core/lifo_write_scheduler_test.cc new file mode 100644 index 00000000000..c9acc11d58b --- /dev/null +++ b/chromium/net/third_party/quiche/src/spdy/core/lifo_write_scheduler_test.cc @@ -0,0 +1,156 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/third_party/quiche/src/spdy/core/lifo_write_scheduler.h" + +#include "net/third_party/quiche/src/spdy/core/spdy_protocol.h" +#include "net/third_party/quiche/src/spdy/core/spdy_test_utils.h" +#include "net/third_party/quiche/src/spdy/platform/api/spdy_test.h" +#include "net/third_party/quiche/src/spdy/platform/api/spdy_test_helpers.h" + +namespace spdy { + +namespace test { + +template <typename StreamIdType> +class LifoWriteSchedulerPeer { + public: + explicit LifoWriteSchedulerPeer(LifoWriteScheduler<StreamIdType>* scheduler) + : scheduler_(scheduler) {} + + size_t NumRegisteredListStreams() const { + return scheduler_->registered_streams_.size(); + } + + std::set<StreamIdType>* GetReadyList() const { + return &scheduler_->ready_streams_; + } + + private: + LifoWriteScheduler<StreamIdType>* scheduler_; +}; + +// Test add and remove from ready list. +TEST(LifoWriteSchedulerTest, ReadyListTest) { + LifoWriteScheduler<SpdyStreamId> lifo; + LifoWriteSchedulerPeer<SpdyStreamId> peer(&lifo); + + EXPECT_SPDY_BUG( + EXPECT_EQ(0u, std::get<0>(lifo.PopNextReadyStreamAndPrecedence())), + "No ready streams available"); + EXPECT_SPDY_BUG(EXPECT_EQ(0u, lifo.PopNextReadyStream()), + "No ready streams available"); + EXPECT_FALSE(lifo.HasReadyStreams()); + EXPECT_SPDY_BUG(lifo.MarkStreamReady(9, true), "Stream 9 is not registered"); + EXPECT_SPDY_BUG(lifo.IsStreamReady(9), "Stream 9 is not registered"); + SpdyStreamPrecedence precedence(1); + lifo.RegisterStream(3, precedence); + EXPECT_FALSE(lifo.IsStreamReady(3)); + lifo.RegisterStream(7, precedence); + lifo.RegisterStream(9, precedence); + lifo.RegisterStream(11, precedence); + lifo.RegisterStream(13, precedence); + lifo.RegisterStream(15, precedence); + lifo.RegisterStream(17, precedence); + lifo.MarkStreamReady(9, true); + lifo.MarkStreamReady(15, true); + lifo.MarkStreamReady(7, true); + lifo.MarkStreamReady(13, true); + lifo.MarkStreamReady(11, true); + lifo.MarkStreamReady(3, true); + EXPECT_TRUE(lifo.IsStreamReady(3)); + lifo.MarkStreamReady(17, true); + EXPECT_TRUE(lifo.HasReadyStreams()); + EXPECT_EQ(7u, lifo.NumReadyStreams()); + + // Verify MarkStream(Not)Ready() can be called multiple times for the same + // stream. + lifo.MarkStreamReady(11, true); + lifo.MarkStreamNotReady(5); + lifo.MarkStreamNotReady(21); + + EXPECT_EQ(17u, lifo.PopNextReadyStream()); + EXPECT_EQ(15u, std::get<0>(lifo.PopNextReadyStreamAndPrecedence())); + EXPECT_TRUE(lifo.ShouldYield(9)); + EXPECT_FALSE(lifo.ShouldYield(13)); + EXPECT_FALSE(lifo.ShouldYield(15)); + + lifo.MarkStreamNotReady(3); + EXPECT_TRUE(peer.GetReadyList()->find(3) == peer.GetReadyList()->end()); + lifo.MarkStreamNotReady(13); + EXPECT_TRUE(peer.GetReadyList()->find(13) == peer.GetReadyList()->end()); + lifo.MarkStreamNotReady(7); + EXPECT_TRUE(peer.GetReadyList()->find(7) == peer.GetReadyList()->end()); + EXPECT_EQ(2u, lifo.NumReadyStreams()); + + lifo.MarkStreamNotReady(9); + lifo.MarkStreamNotReady(11); + EXPECT_FALSE(lifo.ShouldYield(1)); +} + +// Test add and remove from registered list. +TEST(LifoWriteSchedulerTest, RegisterListTest) { + LifoWriteScheduler<SpdyStreamId> lifo; + LifoWriteSchedulerPeer<SpdyStreamId> peer(&lifo); + SpdyStreamPrecedence precedence(1); + EXPECT_EQ(0u, lifo.NumRegisteredStreams()); + lifo.RegisterStream(3, precedence); + lifo.RegisterStream(5, precedence); + lifo.RegisterStream(7, precedence); + lifo.RegisterStream(9, precedence); + lifo.RegisterStream(11, precedence); + EXPECT_EQ(5u, lifo.NumRegisteredStreams()); + + EXPECT_TRUE(lifo.StreamRegistered(3)); + EXPECT_TRUE(lifo.StreamRegistered(5)); + EXPECT_TRUE(lifo.StreamRegistered(7)); + EXPECT_TRUE(lifo.StreamRegistered(9)); + EXPECT_TRUE(lifo.StreamRegistered(11)); + EXPECT_SPDY_BUG(lifo.RegisterStream(11, precedence), + "Stream 11 already registered"); + EXPECT_EQ(5u, peer.NumRegisteredListStreams()); + + lifo.UnregisterStream(3); + EXPECT_EQ(4u, lifo.NumRegisteredStreams()); + EXPECT_FALSE(lifo.StreamRegistered(3)); + EXPECT_SPDY_BUG(lifo.UnregisterStream(3), "Stream 3 is not registered"); + EXPECT_SPDY_BUG(lifo.UnregisterStream(13), "Stream 13 is not registered"); + lifo.UnregisterStream(11); + EXPECT_FALSE(lifo.StreamRegistered(11)); + lifo.UnregisterStream(7); + EXPECT_EQ(2u, lifo.NumRegisteredStreams()); + EXPECT_FALSE(lifo.StreamRegistered(7)); + EXPECT_TRUE(lifo.StreamRegistered(5)); + EXPECT_TRUE(lifo.StreamRegistered(9)); +} + +// Test mark latest event time. +TEST(LifoWriteSchedulerTest, GetLatestEventTest) { + LifoWriteScheduler<SpdyStreamId> lifo; + LifoWriteSchedulerPeer<SpdyStreamId> peer(&lifo); + SpdyStreamPrecedence precedence(1); + lifo.RegisterStream(1, precedence); + lifo.RegisterStream(3, precedence); + lifo.RegisterStream(5, precedence); + lifo.RegisterStream(7, precedence); + lifo.RegisterStream(9, precedence); + lifo.RecordStreamEventTime(1, 1); + lifo.RecordStreamEventTime(3, 8); + lifo.RecordStreamEventTime(5, 4); + lifo.RecordStreamEventTime(7, 2); + lifo.RecordStreamEventTime(9, 3); + EXPECT_SPDY_BUG(lifo.RecordStreamEventTime(11, 1), + "Stream 11 is not registered"); + EXPECT_EQ(0, lifo.GetLatestEventWithPrecedence(9)); + EXPECT_EQ(3, lifo.GetLatestEventWithPrecedence(7)); + EXPECT_EQ(3, lifo.GetLatestEventWithPrecedence(5)); + EXPECT_EQ(4, lifo.GetLatestEventWithPrecedence(3)); + EXPECT_EQ(8, lifo.GetLatestEventWithPrecedence(1)); + EXPECT_SPDY_BUG(lifo.GetLatestEventWithPrecedence(11), + "Stream 11 is not registered"); +} + +} // namespace test + +} // namespace spdy diff --git a/chromium/net/third_party/quiche/src/spdy/core/priority_write_scheduler.h b/chromium/net/third_party/quiche/src/spdy/core/priority_write_scheduler.h index e4385835315..15939e09102 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/priority_write_scheduler.h +++ b/chromium/net/third_party/quiche/src/spdy/core/priority_write_scheduler.h @@ -8,6 +8,7 @@ #include <algorithm> #include <cstddef> #include <cstdint> +#include <string> #include <tuple> #include <unordered_map> #include <utility> @@ -19,7 +20,6 @@ #include "net/third_party/quiche/src/spdy/platform/api/spdy_bug_tracker.h" #include "net/third_party/quiche/src/spdy/platform/api/spdy_logging.h" #include "net/third_party/quiche/src/spdy/platform/api/spdy_macros.h" -#include "net/third_party/quiche/src/spdy/platform/api/spdy_string.h" #include "net/third_party/quiche/src/spdy/platform/api/spdy_string_utils.h" namespace spdy { @@ -260,7 +260,7 @@ class PriorityWriteScheduler : public WriteScheduler<StreamIdType> { size_t NumRegisteredStreams() const override { return stream_infos_.size(); } - SpdyString DebugString() const override { + std::string DebugString() const override { return SpdyStrCat( "PriorityWriteScheduler {num_streams=", stream_infos_.size(), " num_ready_streams=", NumReadyStreams(), "}"); diff --git a/chromium/net/third_party/quiche/src/spdy/core/spdy_alt_svc_wire_format.cc b/chromium/net/third_party/quiche/src/spdy/core/spdy_alt_svc_wire_format.cc index d230ae09fdd..fcbfd16eb5d 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/spdy_alt_svc_wire_format.cc +++ b/chromium/net/third_party/quiche/src/spdy/core/spdy_alt_svc_wire_format.cc @@ -38,8 +38,8 @@ bool ParsePositiveIntegerImpl(SpdyStringPiece::const_iterator c, SpdyAltSvcWireFormat::AlternativeService::AlternativeService() = default; SpdyAltSvcWireFormat::AlternativeService::AlternativeService( - const SpdyString& protocol_id, - const SpdyString& host, + const std::string& protocol_id, + const std::string& host, uint16_t port, uint32_t max_age, VersionVector version) @@ -71,7 +71,7 @@ bool SpdyAltSvcWireFormat::ParseHeaderFieldValue( // Parse protocol-id. SpdyStringPiece::const_iterator percent_encoded_protocol_id_end = std::find(c, value.end(), '='); - SpdyString protocol_id; + std::string protocol_id; if (percent_encoded_protocol_id_end == c || !PercentDecode(c, percent_encoded_protocol_id_end, &protocol_id)) { return false; @@ -105,7 +105,7 @@ bool SpdyAltSvcWireFormat::ParseHeaderFieldValue( return false; } DCHECK_EQ('"', *c); - SpdyString host; + std::string host; uint16_t port; if (!ParseAltAuthority(alt_authority_begin, c, &host, &port)) { return false; @@ -129,7 +129,7 @@ bool SpdyAltSvcWireFormat::ParseHeaderFieldValue( if (c == parameters_end) { break; } - SpdyString parameter_name; + std::string parameter_name; for (; c != parameters_end && *c != '=' && *c != ' ' && *c != '\t'; ++c) { parameter_name.push_back(tolower(*c)); } @@ -211,13 +211,13 @@ bool SpdyAltSvcWireFormat::ParseHeaderFieldValue( } // static -SpdyString SpdyAltSvcWireFormat::SerializeHeaderFieldValue( +std::string SpdyAltSvcWireFormat::SerializeHeaderFieldValue( const AlternativeServiceVector& altsvc_vector) { if (altsvc_vector.empty()) { - return SpdyString("clear"); + return std::string("clear"); } const char kNibbleToHex[] = "0123456789ABCDEF"; - SpdyString value; + std::string value; for (const AlternativeService& altsvc : altsvc_vector) { if (!value.empty()) { value.push_back(','); @@ -300,7 +300,7 @@ void SpdyAltSvcWireFormat::SkipWhiteSpace(SpdyStringPiece::const_iterator* c, // static bool SpdyAltSvcWireFormat::PercentDecode(SpdyStringPiece::const_iterator c, SpdyStringPiece::const_iterator end, - SpdyString* output) { + std::string* output) { output->clear(); for (; c != end; ++c) { if (*c != '%') { @@ -328,7 +328,7 @@ bool SpdyAltSvcWireFormat::PercentDecode(SpdyStringPiece::const_iterator c, bool SpdyAltSvcWireFormat::ParseAltAuthority( SpdyStringPiece::const_iterator c, SpdyStringPiece::const_iterator end, - SpdyString* host, + std::string* host, uint16_t* port) { host->clear(); if (c == end) { diff --git a/chromium/net/third_party/quiche/src/spdy/core/spdy_alt_svc_wire_format.h b/chromium/net/third_party/quiche/src/spdy/core/spdy_alt_svc_wire_format.h index ac834bc851a..13142efe950 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/spdy_alt_svc_wire_format.h +++ b/chromium/net/third_party/quiche/src/spdy/core/spdy_alt_svc_wire_format.h @@ -11,11 +11,11 @@ #define QUICHE_SPDY_CORE_SPDY_ALT_SVC_WIRE_FORMAT_H_ #include <cstdint> +#include <string> #include <vector> #include "net/third_party/quiche/src/spdy/platform/api/spdy_containers.h" #include "net/third_party/quiche/src/spdy/platform/api/spdy_export.h" -#include "net/third_party/quiche/src/spdy/platform/api/spdy_string.h" #include "net/third_party/quiche/src/spdy/platform/api/spdy_string_piece.h" namespace spdy { @@ -29,8 +29,8 @@ class SPDY_EXPORT_PRIVATE SpdyAltSvcWireFormat { using VersionVector = SpdyInlinedVector<uint32_t, 8>; struct SPDY_EXPORT_PRIVATE AlternativeService { - SpdyString protocol_id; - SpdyString host; + std::string protocol_id; + std::string host; // Default is 0: invalid port. uint16_t port = 0; @@ -40,8 +40,8 @@ class SPDY_EXPORT_PRIVATE SpdyAltSvcWireFormat { VersionVector version; AlternativeService(); - AlternativeService(const SpdyString& protocol_id, - const SpdyString& host, + AlternativeService(const std::string& protocol_id, + const std::string& host, uint16_t port, uint32_t max_age, VersionVector version); @@ -62,7 +62,7 @@ class SPDY_EXPORT_PRIVATE SpdyAltSvcWireFormat { friend class test::SpdyAltSvcWireFormatPeer; static bool ParseHeaderFieldValue(SpdyStringPiece value, AlternativeServiceVector* altsvc_vector); - static SpdyString SerializeHeaderFieldValue( + static std::string SerializeHeaderFieldValue( const AlternativeServiceVector& altsvc_vector); private: @@ -70,10 +70,10 @@ class SPDY_EXPORT_PRIVATE SpdyAltSvcWireFormat { SpdyStringPiece::const_iterator end); static bool PercentDecode(SpdyStringPiece::const_iterator c, SpdyStringPiece::const_iterator end, - SpdyString* output); + std::string* output); static bool ParseAltAuthority(SpdyStringPiece::const_iterator c, SpdyStringPiece::const_iterator end, - SpdyString* host, + std::string* host, uint16_t* port); static bool ParsePositiveInteger16(SpdyStringPiece::const_iterator c, SpdyStringPiece::const_iterator end, diff --git a/chromium/net/third_party/quiche/src/spdy/core/spdy_alt_svc_wire_format_test.cc b/chromium/net/third_party/quiche/src/spdy/core/spdy_alt_svc_wire_format_test.cc index bda5bc519d4..7b5e0bb6eb4 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/spdy_alt_svc_wire_format_test.cc +++ b/chromium/net/third_party/quiche/src/spdy/core/spdy_alt_svc_wire_format_test.cc @@ -20,12 +20,12 @@ class SpdyAltSvcWireFormatPeer { } static bool PercentDecode(SpdyStringPiece::const_iterator c, SpdyStringPiece::const_iterator end, - SpdyString* output) { + std::string* output) { return SpdyAltSvcWireFormat::PercentDecode(c, end, output); } static bool ParseAltAuthority(SpdyStringPiece::const_iterator c, SpdyStringPiece::const_iterator end, - SpdyString* host, + std::string* host, uint16_t* port) { return SpdyAltSvcWireFormat::ParseAltAuthority(c, end, host, port); } @@ -49,7 +49,7 @@ namespace { // random case, and corresponding AlternativeService entries. void FuzzHeaderFieldValue( int i, - SpdyString* header_field_value, + std::string* header_field_value, SpdyAltSvcWireFormat::AlternativeService* expected_altsvc) { if (!header_field_value->empty()) { header_field_value->push_back(','); @@ -132,7 +132,7 @@ void FuzzHeaderFieldValue( // canonical form, that is, what SerializeHeaderFieldValue() should output. void FuzzAlternativeService(int i, SpdyAltSvcWireFormat::AlternativeService* altsvc, - SpdyString* expected_header_field_value) { + std::string* expected_header_field_value) { if (!expected_header_field_value->empty()) { expected_header_field_value->push_back(','); } @@ -183,7 +183,7 @@ TEST(SpdyAltSvcWireFormatTest, ParseHeaderFieldValueClear) { // separator, etc. Single alternative service at a time. TEST(SpdyAltSvcWireFormatTest, ParseHeaderFieldValue) { for (int i = 0; i < 1 << 13; ++i) { - SpdyString header_field_value; + std::string header_field_value; SpdyAltSvcWireFormat::AlternativeService expected_altsvc; FuzzHeaderFieldValue(i, &header_field_value, &expected_altsvc); SpdyAltSvcWireFormat::AlternativeServiceVector altsvc_vector; @@ -197,7 +197,7 @@ TEST(SpdyAltSvcWireFormatTest, ParseHeaderFieldValue) { EXPECT_EQ(expected_altsvc.version, altsvc_vector[0].version); // Roundtrip test starting with |altsvc_vector|. - SpdyString reserialized_header_field_value = + std::string reserialized_header_field_value = SpdyAltSvcWireFormat::SerializeHeaderFieldValue(altsvc_vector); SpdyAltSvcWireFormat::AlternativeServiceVector roundtrip_altsvc_vector; ASSERT_TRUE(SpdyAltSvcWireFormat::ParseHeaderFieldValue( @@ -217,7 +217,7 @@ TEST(SpdyAltSvcWireFormatTest, ParseHeaderFieldValue) { // separator, etc. Possibly multiple alternative service at a time. TEST(SpdyAltSvcWireFormatTest, ParseHeaderFieldValueMultiple) { for (int i = 0; i < 1 << 13;) { - SpdyString header_field_value; + std::string header_field_value; SpdyAltSvcWireFormat::AlternativeServiceVector expected_altsvc_vector; // This will generate almost two hundred header field values with two, // three, four, five, six, and seven alternative services each, and @@ -242,7 +242,7 @@ TEST(SpdyAltSvcWireFormatTest, ParseHeaderFieldValueMultiple) { } // Roundtrip test starting with |altsvc_vector|. - SpdyString reserialized_header_field_value = + std::string reserialized_header_field_value = SpdyAltSvcWireFormat::SerializeHeaderFieldValue(altsvc_vector); SpdyAltSvcWireFormat::AlternativeServiceVector roundtrip_altsvc_vector; ASSERT_TRUE(SpdyAltSvcWireFormat::ParseHeaderFieldValue( @@ -276,7 +276,7 @@ TEST(SpdyAltSvcWireFormatTest, SerializeEmptyHeaderFieldValue) { TEST(SpdyAltSvcWireFormatTest, RoundTrip) { for (int i = 0; i < 1 << 3; ++i) { SpdyAltSvcWireFormat::AlternativeService altsvc; - SpdyString expected_header_field_value; + std::string expected_header_field_value; FuzzAlternativeService(i, &altsvc, &expected_header_field_value); // Test ParseHeaderFieldValue(). @@ -304,7 +304,7 @@ TEST(SpdyAltSvcWireFormatTest, RoundTrip) { // parameter. Multiple alternative services at a time. TEST(SpdyAltSvcWireFormatTest, RoundTripMultiple) { SpdyAltSvcWireFormat::AlternativeServiceVector altsvc_vector; - SpdyString expected_header_field_value; + std::string expected_header_field_value; for (int i = 0; i < 1 << 3; ++i) { SpdyAltSvcWireFormat::AlternativeService altsvc; FuzzAlternativeService(i, &altsvc, &expected_header_field_value); @@ -374,7 +374,7 @@ TEST(SpdyAltSvcWireFormatTest, ParseTruncatedHeaderFieldValue) { SpdyAltSvcWireFormat::AlternativeServiceVector altsvc_vector; const char* field_value_array[] = {"a=\":137\"", "a=\"foo:137\"", "a%25=\"foo\\\"bar\\\\baz:137\""}; - for (const SpdyString& field_value : field_value_array) { + for (const std::string& field_value : field_value_array) { for (size_t len = 1; len < field_value.size(); ++len) { EXPECT_FALSE(SpdyAltSvcWireFormat::ParseHeaderFieldValue( field_value.substr(0, len), &altsvc_vector)) @@ -402,7 +402,7 @@ TEST(SpdyAltSvcWireFormatTest, SkipWhiteSpace) { // Test PercentDecode() on valid input. TEST(SpdyAltSvcWireFormatTest, PercentDecodeValid) { SpdyStringPiece input(""); - SpdyString output; + std::string output; ASSERT_TRUE(test::SpdyAltSvcWireFormatPeer::PercentDecode( input.begin(), input.end(), &output)); EXPECT_EQ("", output); @@ -425,7 +425,7 @@ TEST(SpdyAltSvcWireFormatTest, PercentDecodeInvalid) { const char* invalid_input_array[] = {"a%", "a%x", "a%b", "%J22", "%9z"}; for (const char* invalid_input : invalid_input_array) { SpdyStringPiece input(invalid_input); - SpdyString output; + std::string output; EXPECT_FALSE(test::SpdyAltSvcWireFormatPeer::PercentDecode( input.begin(), input.end(), &output)) << input; @@ -435,7 +435,7 @@ TEST(SpdyAltSvcWireFormatTest, PercentDecodeInvalid) { // Test ParseAltAuthority() on valid input. TEST(SpdyAltSvcWireFormatTest, ParseAltAuthorityValid) { SpdyStringPiece input(":42"); - SpdyString host; + std::string host; uint16_t port; ASSERT_TRUE(test::SpdyAltSvcWireFormatPeer::ParseAltAuthority( input.begin(), input.end(), &host, &port)); @@ -476,7 +476,7 @@ TEST(SpdyAltSvcWireFormatTest, ParseAltAuthorityInvalid) { "2003:8:0:16::509d:9615]:443"}; for (const char* invalid_input : invalid_input_array) { SpdyStringPiece input(invalid_input); - SpdyString host; + std::string host; uint16_t port; EXPECT_FALSE(test::SpdyAltSvcWireFormatPeer::ParseAltAuthority( input.begin(), input.end(), &host, &port)) diff --git a/chromium/net/third_party/quiche/src/spdy/core/spdy_deframer_visitor.cc b/chromium/net/third_party/quiche/src/spdy/core/spdy_deframer_visitor.cc index 39a2e51b3ef..0ea2020abdf 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/spdy_deframer_visitor.cc +++ b/chromium/net/third_party/quiche/src/spdy/core/spdy_deframer_visitor.cc @@ -214,7 +214,7 @@ class SpdyTestDeframerImpl : public SpdyTestDeframer, bool fin_ = false; bool got_hpack_end_ = false; - std::unique_ptr<SpdyString> data_; + std::unique_ptr<std::string> data_; // Total length of the data frame. size_t data_len_ = 0; @@ -223,7 +223,7 @@ class SpdyTestDeframerImpl : public SpdyTestDeframer, // Length field). size_t padding_len_ = 0; - std::unique_ptr<SpdyString> goaway_description_; + std::unique_ptr<std::string> goaway_description_; std::unique_ptr<StringPairVector> headers_; std::unique_ptr<SettingVector> settings_; std::unique_ptr<TestHeadersHandler> headers_handler_; @@ -417,7 +417,7 @@ void SpdyTestDeframerImpl::OnAltSvc( << " frame_type_=" << Http2FrameTypeToString(frame_type_); CHECK_GT(stream_id, 0u); auto ptr = SpdyMakeUnique<SpdyAltSvcIR>(stream_id); - ptr->set_origin(SpdyString(origin)); + ptr->set_origin(std::string(origin)); for (auto& altsvc : altsvc_vector) { ptr->add_altsvc(altsvc); } @@ -456,7 +456,7 @@ void SpdyTestDeframerImpl::OnDataFrameHeader(SpdyStreamId stream_id, stream_id_ = stream_id; fin_ = fin; data_len_ = length; - data_ = SpdyMakeUnique<SpdyString>(); + data_ = SpdyMakeUnique<std::string>(); } // The SpdyFramer will not process any more data at this point. @@ -482,7 +482,7 @@ void SpdyTestDeframerImpl::OnGoAway(SpdyStreamId last_good_stream_id, frame_type_ = GOAWAY; goaway_ir_ = SpdyMakeUnique<SpdyGoAwayIR>(last_good_stream_id, error_code, ""); - goaway_description_ = SpdyMakeUnique<SpdyString>(); + goaway_description_ = SpdyMakeUnique<std::string>(); } // If len==0 then we've reached the end of the GOAWAY frame. @@ -761,7 +761,8 @@ void SpdyTestDeframerImpl::OnHeader(SpdyStringPiece key, frame_type_ == PUSH_PROMISE) << " frame_type_=" << Http2FrameTypeToString(frame_type_); CHECK(!got_hpack_end_); - HTTP2_DIE_IF_NULL(headers_)->emplace_back(SpdyString(key), SpdyString(value)); + HTTP2_DIE_IF_NULL(headers_)->emplace_back(std::string(key), + std::string(value)); HTTP2_DIE_IF_NULL(headers_handler_)->OnHeader(key, value); } diff --git a/chromium/net/third_party/quiche/src/spdy/core/spdy_deframer_visitor.h b/chromium/net/third_party/quiche/src/spdy/core/spdy_deframer_visitor.h index 272a4f6200a..f95d513f8c7 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/spdy_deframer_visitor.h +++ b/chromium/net/third_party/quiche/src/spdy/core/spdy_deframer_visitor.h @@ -68,8 +68,8 @@ // are met. #include <cstdint> - #include <memory> +#include <string> #include <type_traits> #include <utility> #include <vector> @@ -79,7 +79,6 @@ #include "net/third_party/quiche/src/spdy/core/spdy_protocol_test_utils.h" #include "net/third_party/quiche/src/spdy/core/spdy_test_utils.h" #include "net/third_party/quiche/src/spdy/platform/api/spdy_logging.h" -#include "net/third_party/quiche/src/spdy/platform/api/spdy_string.h" namespace spdy { namespace test { @@ -91,7 +90,7 @@ typedef std::vector<std::pair<SpdyKnownSettingsId, uint32_t>> SettingVector; // particular the order of each header entry, though it doesn't expose the // inner details of the HPACK block, such as the type of encoding selected // for each header entry, nor dynamic table size changes. -typedef std::pair<SpdyString, SpdyString> StringPair; +typedef std::pair<std::string, std::string> StringPair; typedef std::vector<StringPair> StringPairVector; // Forward decl. diff --git a/chromium/net/third_party/quiche/src/spdy/core/spdy_framer.cc b/chromium/net/third_party/quiche/src/spdy/core/spdy_framer.cc index 7891d72e1f6..59f7c3ba91b 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/spdy_framer.cc +++ b/chromium/net/third_party/quiche/src/spdy/core/spdy_framer.cc @@ -96,7 +96,7 @@ uint8_t SerializePushPromiseFrameFlags(const SpdyPushPromiseIR& push_promise_ir, // block. Does not need or use the SpdyHeaderBlock inside SpdyHeadersIR. // Return false if the serialization fails. |encoding| should not be empty. bool SerializeHeadersGivenEncoding(const SpdyHeadersIR& headers, - const SpdyString& encoding, + const std::string& encoding, const bool end_headers, ZeroCopyOutputBuffer* output) { const size_t frame_size = @@ -124,7 +124,7 @@ bool SerializeHeadersGivenEncoding(const SpdyHeadersIR& headers, } if (ret && headers.padding_payload_len() > 0) { - SpdyString padding(headers.padding_payload_len(), 0); + std::string padding(headers.padding_payload_len(), 0); ret &= builder.WriteBytes(padding.data(), padding.length()); } @@ -138,7 +138,7 @@ bool SerializeHeadersGivenEncoding(const SpdyHeadersIR& headers, // encoded header block. Does not need or use the SpdyHeaderBlock inside // SpdyPushPromiseIR. bool SerializePushPromiseGivenEncoding(const SpdyPushPromiseIR& push_promise, - const SpdyString& encoding, + const std::string& encoding, const bool end_headers, ZeroCopyOutputBuffer* output) { const size_t frame_size = @@ -155,7 +155,7 @@ bool SerializePushPromiseGivenEncoding(const SpdyPushPromiseIR& push_promise, ok = ok && builder.WriteUInt32(push_promise.promised_stream_id()) && builder.WriteBytes(encoding.data(), encoding.size()); if (ok && push_promise.padding_payload_len() > 0) { - SpdyString padding(push_promise.padding_payload_len(), 0); + std::string padding(push_promise.padding_payload_len(), 0); ok = builder.WriteBytes(padding.data(), padding.length()); } @@ -166,7 +166,7 @@ bool SerializePushPromiseGivenEncoding(const SpdyPushPromiseIR& push_promise, } bool WritePayloadWithContinuation(SpdyFrameBuilder* builder, - const SpdyString& hpack_encoding, + const std::string& hpack_encoding, SpdyStreamId stream_id, SpdyFrameType type, int padding_payload_len) { @@ -191,7 +191,7 @@ bool WritePayloadWithContinuation(SpdyFrameBuilder* builder, bool ret = builder->WriteBytes(&hpack_encoding[0], hpack_encoding.size() - bytes_remaining); if (padding_payload_len > 0) { - SpdyString padding = SpdyString(padding_payload_len, 0); + std::string padding = std::string(padding_payload_len, 0); ret &= builder->WriteBytes(padding.data(), padding.length()); } @@ -262,7 +262,7 @@ void SerializeSettingsBuilderHelper(const SpdySettingsIR& settings, } void SerializeAltSvcBuilderHelper(const SpdyAltSvcIR& altsvc_ir, - SpdyString* value, + std::string* value, size_t* size) { *size = kGetAltSvcFrameMinimumSize; *size = *size + altsvc_ir.origin().length(); @@ -301,7 +301,7 @@ size_t SpdyFramer::SpdyFrameIterator::NextFrame(ZeroCopyOutputBuffer* output) { const size_t size_without_block = is_first_frame_ ? GetFrameSizeSansBlock() : kContinuationFrameMinimumSize; - auto encoding = SpdyMakeUnique<SpdyString>(); + auto encoding = SpdyMakeUnique<std::string>(); encoder_->Next(kHttp2MaxControlFrameSendSize - size_without_block, encoding.get()); has_next_frame_ = encoder_->HasNext(); @@ -353,7 +353,7 @@ size_t SpdyFramer::SpdyHeaderFrameIterator::GetFrameSizeSansBlock() const { } bool SpdyFramer::SpdyHeaderFrameIterator::SerializeGivenEncoding( - const SpdyString& encoding, + const std::string& encoding, ZeroCopyOutputBuffer* output) const { return SerializeHeadersGivenEncoding(*headers_ir_, encoding, !has_next_frame(), output); @@ -378,7 +378,7 @@ size_t SpdyFramer::SpdyPushPromiseFrameIterator::GetFrameSizeSansBlock() const { } bool SpdyFramer::SpdyPushPromiseFrameIterator::SerializeGivenEncoding( - const SpdyString& encoding, + const std::string& encoding, ZeroCopyOutputBuffer* output) const { return SerializePushPromiseGivenEncoding(*push_promise_ir_, encoding, !has_next_frame(), output); @@ -446,7 +446,7 @@ SpdySerializedFrame SpdyFramer::SerializeData(const SpdyDataIR& data_ir) { } builder.WriteBytes(data_ir.data(), data_ir.data_len()); if (data_ir.padding_payload_len() > 0) { - SpdyString padding(data_ir.padding_payload_len(), 0); + std::string padding(data_ir.padding_payload_len(), 0); builder.WriteBytes(padding.data(), padding.length()); } DCHECK_EQ(size_with_padding, builder.length()); @@ -552,7 +552,7 @@ SpdySerializedFrame SpdyFramer::SerializeGoAway( void SpdyFramer::SerializeHeadersBuilderHelper(const SpdyHeadersIR& headers, uint8_t* flags, size_t* size, - SpdyString* hpack_encoding, + std::string* hpack_encoding, int* weight, size_t* length_field) { if (headers.fin()) { @@ -608,7 +608,7 @@ SpdySerializedFrame SpdyFramer::SerializeHeaders(const SpdyHeadersIR& headers) { // The size of this frame, including padding (if there is any) and // variable-length header block. size_t size = 0; - SpdyString hpack_encoding; + std::string hpack_encoding; int weight = 0; size_t length_field = 0; SerializeHeadersBuilderHelper(headers, &flags, &size, &hpack_encoding, @@ -658,7 +658,7 @@ SpdySerializedFrame SpdyFramer::SerializeWindowUpdate( void SpdyFramer::SerializePushPromiseBuilderHelper( const SpdyPushPromiseIR& push_promise, uint8_t* flags, - SpdyString* hpack_encoding, + std::string* hpack_encoding, size_t* size) { *flags = 0; // This will get overwritten if we overflow into a CONTINUATION frame. @@ -686,7 +686,7 @@ SpdySerializedFrame SpdyFramer::SerializePushPromise( const SpdyPushPromiseIR& push_promise) { uint8_t flags = 0; size_t size = 0; - SpdyString hpack_encoding; + std::string hpack_encoding; SerializePushPromiseBuilderHelper(push_promise, &flags, &hpack_encoding, &size); @@ -725,7 +725,7 @@ SpdySerializedFrame SpdyFramer::SerializePushPromise( SpdySerializedFrame SpdyFramer::SerializeContinuation( const SpdyContinuationIR& continuation) const { - const SpdyString& encoding = continuation.encoding(); + const std::string& encoding = continuation.encoding(); size_t frame_size = kContinuationFrameMinimumSize + encoding.size(); SpdyFrameBuilder builder(frame_size); uint8_t flags = continuation.end_headers() ? HEADERS_FLAG_END_HEADERS : 0; @@ -738,7 +738,7 @@ SpdySerializedFrame SpdyFramer::SerializeContinuation( } SpdySerializedFrame SpdyFramer::SerializeAltSvc(const SpdyAltSvcIR& altsvc_ir) { - SpdyString value; + std::string value; size_t size = 0; SerializeAltSvcBuilderHelper(altsvc_ir, &value, &size); SpdyFrameBuilder builder(size); @@ -942,8 +942,8 @@ bool SpdyFramer::SerializeData(const SpdyDataIR& data_ir, ok = ok && builder.WriteBytes(data_ir.data(), data_ir.data_len()); if (data_ir.padding_payload_len() > 0) { - SpdyString padding; - padding = SpdyString(data_ir.padding_payload_len(), 0); + std::string padding; + padding = std::string(data_ir.padding_payload_len(), 0); ok = ok && builder.WriteBytes(padding.data(), padding.length()); } DCHECK_EQ(size_with_padding, builder.length()); @@ -1054,7 +1054,7 @@ bool SpdyFramer::SerializeHeaders(const SpdyHeadersIR& headers, // The size of this frame, including padding (if there is any) and // variable-length header block. size_t size = 0; - SpdyString hpack_encoding; + std::string hpack_encoding; int weight = 0; size_t length_field = 0; SerializeHeadersBuilderHelper(headers, &flags, &size, &hpack_encoding, @@ -1107,7 +1107,7 @@ bool SpdyFramer::SerializePushPromise(const SpdyPushPromiseIR& push_promise, ZeroCopyOutputBuffer* output) { uint8_t flags = 0; size_t size = 0; - SpdyString hpack_encoding; + std::string hpack_encoding; SerializePushPromiseBuilderHelper(push_promise, &flags, &hpack_encoding, &size); @@ -1148,7 +1148,7 @@ bool SpdyFramer::SerializePushPromise(const SpdyPushPromiseIR& push_promise, bool SpdyFramer::SerializeContinuation(const SpdyContinuationIR& continuation, ZeroCopyOutputBuffer* output) const { - const SpdyString& encoding = continuation.encoding(); + const std::string& encoding = continuation.encoding(); size_t frame_size = kContinuationFrameMinimumSize + encoding.size(); SpdyFrameBuilder builder(frame_size, output); uint8_t flags = continuation.end_headers() ? HEADERS_FLAG_END_HEADERS : 0; @@ -1163,7 +1163,7 @@ bool SpdyFramer::SerializeContinuation(const SpdyContinuationIR& continuation, bool SpdyFramer::SerializeAltSvc(const SpdyAltSvcIR& altsvc_ir, ZeroCopyOutputBuffer* output) { - SpdyString value; + std::string value; size_t size = 0; SerializeAltSvcBuilderHelper(altsvc_ir, &value, &size); SpdyFrameBuilder builder(size, output); diff --git a/chromium/net/third_party/quiche/src/spdy/core/spdy_framer.h b/chromium/net/third_party/quiche/src/spdy/core/spdy_framer.h index e268994e89b..45d1f86f33c 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/spdy_framer.h +++ b/chromium/net/third_party/quiche/src/spdy/core/spdy_framer.h @@ -10,6 +10,7 @@ #include <cstdint> #include <map> #include <memory> +#include <string> #include <utility> #include "net/third_party/quiche/src/spdy/core/hpack/hpack_encoder.h" @@ -19,7 +20,6 @@ #include "net/third_party/quiche/src/spdy/core/spdy_protocol.h" #include "net/third_party/quiche/src/spdy/core/zero_copy_output_buffer.h" #include "net/third_party/quiche/src/spdy/platform/api/spdy_export.h" -#include "net/third_party/quiche/src/spdy/platform/api/spdy_string.h" #include "net/third_party/quiche/src/spdy/platform/api/spdy_string_piece.h" namespace spdy { @@ -258,7 +258,7 @@ class SPDY_EXPORT_PRIVATE SpdyFramer { protected: virtual size_t GetFrameSizeSansBlock() const = 0; - virtual bool SerializeGivenEncoding(const SpdyString& encoding, + virtual bool SerializeGivenEncoding(const std::string& encoding, ZeroCopyOutputBuffer* output) const = 0; SpdyFramer* GetFramer() const { return framer_; } @@ -291,7 +291,7 @@ class SPDY_EXPORT_PRIVATE SpdyFramer { private: const SpdyFrameIR& GetIR() const override; size_t GetFrameSizeSansBlock() const override; - bool SerializeGivenEncoding(const SpdyString& encoding, + bool SerializeGivenEncoding(const std::string& encoding, ZeroCopyOutputBuffer* output) const override; const std::unique_ptr<const SpdyHeadersIR> headers_ir_; @@ -313,7 +313,7 @@ class SPDY_EXPORT_PRIVATE SpdyFramer { private: const SpdyFrameIR& GetIR() const override; size_t GetFrameSizeSansBlock() const override; - bool SerializeGivenEncoding(const SpdyString& encoding, + bool SerializeGivenEncoding(const std::string& encoding, ZeroCopyOutputBuffer* output) const override; const std::unique_ptr<const SpdyPushPromiseIR> push_promise_ir_; @@ -344,12 +344,12 @@ class SPDY_EXPORT_PRIVATE SpdyFramer { void SerializeHeadersBuilderHelper(const SpdyHeadersIR& headers, uint8_t* flags, size_t* size, - SpdyString* hpack_encoding, + std::string* hpack_encoding, int* weight, size_t* length_field); void SerializePushPromiseBuilderHelper(const SpdyPushPromiseIR& push_promise, uint8_t* flags, - SpdyString* hpack_encoding, + std::string* hpack_encoding, size_t* size); std::unique_ptr<HpackEncoder> hpack_encoder_; diff --git a/chromium/net/third_party/quiche/src/spdy/core/spdy_framer_test.cc b/chromium/net/third_party/quiche/src/spdy/core/spdy_framer_test.cc index abca5a790d3..a424adb565a 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/spdy_framer_test.cc +++ b/chromium/net/third_party/quiche/src/spdy/core/spdy_framer_test.cc @@ -24,7 +24,6 @@ #include "net/third_party/quiche/src/spdy/platform/api/spdy_flags.h" #include "net/third_party/quiche/src/spdy/platform/api/spdy_logging.h" #include "net/third_party/quiche/src/spdy/platform/api/spdy_ptr_util.h" -#include "net/third_party/quiche/src/spdy/platform/api/spdy_string.h" #include "net/third_party/quiche/src/spdy/platform/api/spdy_string_utils.h" #include "net/third_party/quiche/src/spdy/platform/api/spdy_test.h" @@ -411,7 +410,7 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface, << "\", altsvc_vector)"; test_altsvc_ir_ = SpdyMakeUnique<SpdyAltSvcIR>(stream_id); if (origin.length() > 0) { - test_altsvc_ir_->set_origin(SpdyString(origin)); + test_altsvc_ir_->set_origin(std::string(origin)); } for (const auto& altsvc : altsvc_vector) { test_altsvc_ir_->add_altsvc(altsvc); @@ -568,7 +567,7 @@ class TestExtension : public ExtensionVisitorInterface { size_t length_ = 0; uint8_t type_ = 0; uint8_t flags_ = 0; - SpdyString payload_; + std::string payload_; }; // Exposes SpdyUnknownIR::set_length() for testing purposes. @@ -600,7 +599,7 @@ class SpdyFramerTest : public ::testing::TestWithParam<Output> { } } - void CompareFrame(const SpdyString& description, + void CompareFrame(const std::string& description, const SpdySerializedFrame& actual_frame, const unsigned char* expected, const int expected_len) { @@ -1034,7 +1033,7 @@ TEST_P(SpdyFramerTest, ContinuationWithStreamIdZero) { SpdyContinuationIR continuation(/* stream_id = */ 0); auto some_nonsense_encoding = - SpdyMakeUnique<SpdyString>("some nonsense encoding"); + SpdyMakeUnique<std::string>("some nonsense encoding"); continuation.take_encoding(std::move(some_nonsense_encoding)); continuation.set_end_headers(true); SpdySerializedFrame frame(framer_.SerializeContinuation(continuation)); @@ -1101,11 +1100,11 @@ TEST_P(SpdyFramerTest, PushPromiseWithPromisedStreamIdZero) { TEST_P(SpdyFramerTest, MultiValueHeader) { SpdyFramer framer(SpdyFramer::DISABLE_COMPRESSION); - SpdyString value("value1\0value2", 13); + std::string value("value1\0value2", 13); // TODO(jgraettinger): If this pattern appears again, move to test class. SpdyHeaderBlock header_set; header_set["name"] = value; - SpdyString buffer; + std::string buffer; HpackEncoder encoder(ObtainHpackHuffmanTable()); encoder.DisableCompression(); encoder.EncodeHeaderSet(header_set, &buffer); @@ -2308,7 +2307,7 @@ TEST_P(SpdyFramerTest, CreateContinuationUncompressed) { SpdyHeaderBlock header_block; header_block["bar"] = "foo"; header_block["foo"] = "bar"; - auto buffer = SpdyMakeUnique<SpdyString>(); + auto buffer = SpdyMakeUnique<std::string>(); HpackEncoder encoder(ObtainHpackHuffmanTable()); encoder.DisableCompression(); encoder.EncodeHeaderSet(header_block, buffer.get()); @@ -2426,7 +2425,7 @@ TEST_P(SpdyFramerTest, CreatePushPromiseThenContinuationUncompressed) { SpdyPushPromiseIR push_promise(/* stream_id = */ 42, /* promised_stream_id = */ 57); push_promise.set_padding_len(1); - SpdyString big_value(kHttp2MaxControlFrameSendSize, 'x'); + std::string big_value(kHttp2MaxControlFrameSendSize, 'x'); push_promise.SetHeader("xxx", big_value); SpdySerializedFrame frame(SpdyFramerPeer::SerializePushPromise( &framer, push_promise, use_output_ ? &output_ : nullptr)); @@ -2617,7 +2616,7 @@ TEST_P(SpdyFramerTest, TooLargeHeadersFrameUsesContinuation) { // Exact payload length will change with HPACK, but this should be long // enough to cause an overflow. const size_t kBigValueSize = kHttp2MaxControlFrameSendSize; - SpdyString big_value(kBigValueSize, 'x'); + std::string big_value(kBigValueSize, 'x'); headers.SetHeader("aa", big_value); SpdySerializedFrame control_frame(SpdyFramerPeer::SerializeHeaders( &framer, headers, use_output_ ? &output_ : nullptr)); @@ -2642,9 +2641,9 @@ TEST_P(SpdyFramerTest, MultipleContinuationFramesWithIterator) { // Exact payload length will change with HPACK, but this should be long // enough to cause an overflow. const size_t kBigValueSize = kHttp2MaxControlFrameSendSize; - SpdyString big_valuex(kBigValueSize, 'x'); + std::string big_valuex(kBigValueSize, 'x'); headers->SetHeader("aa", big_valuex); - SpdyString big_valuez(kBigValueSize, 'z'); + std::string big_valuez(kBigValueSize, 'z'); headers->SetHeader("bb", big_valuez); SpdyFramer::SpdyHeaderFrameIterator frame_it(&framer, std::move(headers)); @@ -2707,9 +2706,9 @@ TEST_P(SpdyFramerTest, PushPromiseFramesWithIterator) { // Exact payload length will change with HPACK, but this should be long // enough to cause an overflow. const size_t kBigValueSize = kHttp2MaxControlFrameSendSize; - SpdyString big_valuex(kBigValueSize, 'x'); + std::string big_valuex(kBigValueSize, 'x'); push_promise->SetHeader("aa", big_valuex); - SpdyString big_valuez(kBigValueSize, 'z'); + std::string big_valuez(kBigValueSize, 'z'); push_promise->SetHeader("bb", big_valuez); SpdyFramer::SpdyPushPromiseFrameIterator frame_it(&framer, @@ -2836,7 +2835,7 @@ TEST_P(SpdyFramerTest, TooLargePushPromiseFrameUsesContinuation) { // Exact payload length will change with HPACK, but this should be long // enough to cause an overflow. const size_t kBigValueSize = kHttp2MaxControlFrameSendSize; - SpdyString big_value(kBigValueSize, 'x'); + std::string big_value(kBigValueSize, 'x'); push_promise.SetHeader("aa", big_value); SpdySerializedFrame control_frame(SpdyFramerPeer::SerializePushPromise( &framer, push_promise, use_output_ ? &output_ : nullptr)); @@ -2861,7 +2860,7 @@ TEST_P(SpdyFramerTest, ControlFrameMuchTooLarge) { const size_t kHeaderBufferSize = kHttp2DefaultFramePayloadLimit / kHeaderBufferChunks; const size_t kBigValueSize = kHeaderBufferSize * 2; - SpdyString big_value(kBigValueSize, 'x'); + std::string big_value(kBigValueSize, 'x'); SpdyHeadersIR headers(/* stream_id = */ 1); headers.set_fin(true); headers.SetHeader("aa", big_value); @@ -2898,7 +2897,7 @@ TEST_P(SpdyFramerTest, ControlFrameSizesAreValidated) { 0x00, 0x00, 0x00, // Truncated Status Field }; const size_t pad_length = length + kFrameHeaderSize - sizeof(kH2FrameData); - SpdyString pad(pad_length, 'A'); + std::string pad(pad_length, 'A'); TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION); visitor.SimulateInFramer(kH2FrameData, sizeof(kH2FrameData)); @@ -3649,7 +3648,7 @@ TEST_P(SpdyFramerTest, ReadUnknownExtensionFrameWithExtension) { EXPECT_EQ(20u, extension.length_); EXPECT_EQ(255, extension.type_); EXPECT_EQ(0xff, extension.flags_); - EXPECT_EQ(SpdyString(20, '\xff'), extension.payload_); + EXPECT_EQ(std::string(20, '\xff'), extension.payload_); // Follow it up with a valid control frame to make sure we handle // subsequent frames correctly. @@ -4646,7 +4645,7 @@ TEST_P(SpdyFramerTest, ProcessAllInput) { SPDY_VLOG(1) << "frame1_size = " << frame1_size; SPDY_VLOG(1) << "frame2_size = " << frame2_size; - SpdyString input_buffer; + std::string input_buffer; input_buffer.append(frame1.data(), frame1_size); input_buffer.append(frame2.data(), frame2_size); @@ -4693,7 +4692,7 @@ TEST_P(SpdyFramerTest, ProcessAtMostOneFrame) { SPDY_VLOG(1) << "frame1_size = " << frame1_size; SPDY_VLOG(1) << "frame2_size = " << frame2_size; - SpdyString input_buffer; + std::string input_buffer; input_buffer.append(frame1.data(), frame1_size); input_buffer.append(frame2.data(), frame2_size); @@ -4792,8 +4791,8 @@ TEST_P(SpdyFramerTest, SpdyFrameIRSize) { CheckFrameAndIRSize(&headers_ir, &framer, &output_); SpdyHeadersIR headers_ir_with_continuation(1); - headers_ir_with_continuation.SetHeader("alpha", SpdyString(100000, 'x')); - headers_ir_with_continuation.SetHeader("beta", SpdyString(100000, 'x')); + headers_ir_with_continuation.SetHeader("alpha", std::string(100000, 'x')); + headers_ir_with_continuation.SetHeader("beta", std::string(100000, 'x')); headers_ir_with_continuation.SetHeader("cookie", "key1=value1; key2=value2"); CheckFrameAndIRSize(&headers_ir_with_continuation, &framer, &output_); @@ -4801,8 +4800,8 @@ TEST_P(SpdyFramerTest, SpdyFrameIRSize) { CheckFrameAndIRSize(&window_update_ir, &framer, &output_); SpdyPushPromiseIR push_promise_ir(3, 8); - push_promise_ir.SetHeader("alpha", SpdyString(100000, 'x')); - push_promise_ir.SetHeader("beta", SpdyString(100000, 'x')); + push_promise_ir.SetHeader("alpha", std::string(100000, 'x')); + push_promise_ir.SetHeader("beta", std::string(100000, 'x')); push_promise_ir.SetHeader("cookie", "key1=value1; key2=value2"); CheckFrameAndIRSize(&push_promise_ir, &framer, &output_); diff --git a/chromium/net/third_party/quiche/src/spdy/core/spdy_header_block.cc b/chromium/net/third_party/quiche/src/spdy/core/spdy_header_block.cc index c99bbf32e3c..705038a573a 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/spdy_header_block.cc +++ b/chromium/net/third_party/quiche/src/spdy/core/spdy_header_block.cc @@ -218,11 +218,11 @@ SpdyHeaderBlock::ValueProxy& SpdyHeaderBlock::ValueProxy::operator=( return *this; } -SpdyString SpdyHeaderBlock::ValueProxy::as_string() const { +std::string SpdyHeaderBlock::ValueProxy::as_string() const { if (lookup_result_ == block_->end()) { return ""; } else { - return SpdyString(lookup_result_->second.value()); + return std::string(lookup_result_->second.value()); } } @@ -262,12 +262,12 @@ bool SpdyHeaderBlock::operator!=(const SpdyHeaderBlock& other) const { return !(operator==(other)); } -SpdyString SpdyHeaderBlock::DebugString() const { +std::string SpdyHeaderBlock::DebugString() const { if (empty()) { return "{}"; } - SpdyString output = "\n{\n"; + std::string output = "\n{\n"; for (auto it = begin(); it != end(); ++it) { SpdyStrAppend(&output, " ", it->first, " ", it->second, "\n"); } diff --git a/chromium/net/third_party/quiche/src/spdy/core/spdy_header_block.h b/chromium/net/third_party/quiche/src/spdy/core/spdy_header_block.h index f5795790e5e..71e1537831b 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/spdy_header_block.h +++ b/chromium/net/third_party/quiche/src/spdy/core/spdy_header_block.h @@ -9,13 +9,13 @@ #include <list> #include <memory> +#include <string> #include <utility> #include <vector> #include "net/third_party/quiche/src/spdy/platform/api/spdy_containers.h" #include "net/third_party/quiche/src/spdy/platform/api/spdy_export.h" #include "net/third_party/quiche/src/spdy/platform/api/spdy_macros.h" -#include "net/third_party/quiche/src/spdy/platform/api/spdy_string.h" #include "net/third_party/quiche/src/spdy/platform/api/spdy_string_piece.h" namespace spdy { @@ -149,7 +149,7 @@ class SPDY_EXPORT_PRIVATE SpdyHeaderBlock { // Provides a human readable multi-line representation of the stored header // keys and values. - SpdyString DebugString() const; + std::string DebugString() const; iterator begin() { return iterator(block_.begin()); } iterator end() { return iterator(block_.end()); } @@ -183,7 +183,8 @@ class SPDY_EXPORT_PRIVATE SpdyHeaderBlock { SPDY_MUST_USE_RESULT ValueProxy operator[](const SpdyStringPiece key); // This object provides automatic conversions that allow SpdyHeaderBlock to be - // nearly a drop-in replacement for SpdyLinkedHashMap<SpdyString, SpdyString>. + // nearly a drop-in replacement for + // SpdyLinkedHashMap<std::string, std::string>. // It reads data from or writes data to a SpdyHeaderBlock::Storage. class SPDY_EXPORT_PRIVATE ValueProxy { public: @@ -200,7 +201,7 @@ class SPDY_EXPORT_PRIVATE SpdyHeaderBlock { // Assignment modifies the underlying SpdyHeaderBlock. ValueProxy& operator=(const SpdyStringPiece other); - SpdyString as_string() const; + std::string as_string() const; private: friend class SpdyHeaderBlock; diff --git a/chromium/net/third_party/quiche/src/spdy/core/spdy_header_block_test.cc b/chromium/net/third_party/quiche/src/spdy/core/spdy_header_block_test.cc index f8694319d7c..c34b718a1a4 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/spdy_header_block_test.cc +++ b/chromium/net/third_party/quiche/src/spdy/core/spdy_header_block_test.cc @@ -69,15 +69,15 @@ TEST(SpdyHeaderBlockTest, KeyMemoryReclaimedOnLookup) { // This test verifies that headers can be set in a variety of ways. TEST(SpdyHeaderBlockTest, AddHeaders) { SpdyHeaderBlock block; - block["foo"] = SpdyString(300, 'x'); + block["foo"] = std::string(300, 'x'); block["bar"] = "baz"; block["qux"] = "qux1"; block["qux"] = "qux2"; block.insert(std::make_pair("key", "value")); - EXPECT_EQ(Pair("foo", SpdyString(300, 'x')), *block.find("foo")); + EXPECT_EQ(Pair("foo", std::string(300, 'x')), *block.find("foo")); EXPECT_EQ("baz", block["bar"]); - SpdyString qux("qux"); + std::string qux("qux"); EXPECT_EQ("qux2", block[qux]); ASSERT_NE(block.end(), block.find("key")); EXPECT_EQ(Pair("key", "value"), *block.find("key")); @@ -89,7 +89,7 @@ TEST(SpdyHeaderBlockTest, AddHeaders) { // This test verifies that SpdyHeaderBlock can be copied using Clone(). TEST(SpdyHeaderBlockTest, CopyBlocks) { SpdyHeaderBlock block1; - block1["foo"] = SpdyString(300, 'x'); + block1["foo"] = std::string(300, 'x'); block1["bar"] = "baz"; block1.insert(std::make_pair("qux", "qux1")); @@ -147,7 +147,7 @@ TEST(SpdyHeaderBlockTest, AppendHeaders) { SpdyHeaderBlock block; block["foo"] = "foo"; block.AppendValueOrAddHeader("foo", "bar"); - EXPECT_EQ(Pair("foo", SpdyString("foo\0bar", 7)), *block.find("foo")); + EXPECT_EQ(Pair("foo", std::string("foo\0bar", 7)), *block.find("foo")); block.insert(std::make_pair("foo", "baz")); EXPECT_EQ("baz", block["foo"]); @@ -171,9 +171,9 @@ TEST(SpdyHeaderBlockTest, AppendHeaders) { EXPECT_EQ("key1=value1; key2=value2; key3=value3", block["cookie"]); EXPECT_EQ("baz", block["foo"]); - EXPECT_EQ(SpdyString("h1v1\0h1v2\0h1v3", 14), block["h1"]); - EXPECT_EQ(SpdyString("h2v1\0h2v2\0h2v3", 14), block["h2"]); - EXPECT_EQ(SpdyString("h3v2\0h3v3", 9), block["h3"]); + EXPECT_EQ(std::string("h1v1\0h1v2\0h1v3", 14), block["h1"]); + EXPECT_EQ(std::string("h2v1\0h2v2\0h2v3", 14), block["h2"]); + EXPECT_EQ(std::string("h3v2\0h3v3", 9), block["h3"]); EXPECT_EQ("singleton", block["h4"]); } @@ -217,20 +217,20 @@ size_t SpdyHeaderBlockSize(const SpdyHeaderBlock& block) { TEST(SpdyHeaderBlockTest, TotalBytesUsed) { SpdyHeaderBlock block; const size_t value_size = 300; - block["foo"] = SpdyString(value_size, 'x'); + block["foo"] = std::string(value_size, 'x'); EXPECT_EQ(block.TotalBytesUsed(), SpdyHeaderBlockSize(block)); - block.insert(std::make_pair("key", SpdyString(value_size, 'x'))); + block.insert(std::make_pair("key", std::string(value_size, 'x'))); EXPECT_EQ(block.TotalBytesUsed(), SpdyHeaderBlockSize(block)); - block.AppendValueOrAddHeader("abc", SpdyString(value_size, 'x')); + block.AppendValueOrAddHeader("abc", std::string(value_size, 'x')); EXPECT_EQ(block.TotalBytesUsed(), SpdyHeaderBlockSize(block)); // Replace value for existing key. - block["foo"] = SpdyString(value_size, 'x'); + block["foo"] = std::string(value_size, 'x'); EXPECT_EQ(block.TotalBytesUsed(), SpdyHeaderBlockSize(block)); - block.insert(std::make_pair("key", SpdyString(value_size, 'x'))); + block.insert(std::make_pair("key", std::string(value_size, 'x'))); EXPECT_EQ(block.TotalBytesUsed(), SpdyHeaderBlockSize(block)); // Add value for existing key. - block.AppendValueOrAddHeader("abc", SpdyString(value_size, 'x')); + block.AppendValueOrAddHeader("abc", std::string(value_size, 'x')); EXPECT_EQ(block.TotalBytesUsed(), SpdyHeaderBlockSize(block)); // Copies/clones SpdyHeaderBlock. diff --git a/chromium/net/third_party/quiche/src/spdy/core/spdy_intrusive_list_test.cc b/chromium/net/third_party/quiche/src/spdy/core/spdy_intrusive_list_test.cc index eee65e34c29..7fecec4a166 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/spdy_intrusive_list_test.cc +++ b/chromium/net/third_party/quiche/src/spdy/core/spdy_intrusive_list_test.cc @@ -7,9 +7,9 @@ #include <algorithm> #include <cstddef> #include <list> +#include <string> #include <utility> -#include "net/third_party/quiche/src/spdy/platform/api/spdy_string.h" #include "net/third_party/quiche/src/spdy/platform/api/spdy_test.h" namespace spdy { @@ -72,18 +72,18 @@ class IntrusiveListTest : public ::testing::Test { TEST(NewIntrusiveListTest, Basic) { TestList list1; - CHECK_EQ(sizeof(SpdyIntrusiveLink<TestItem>), sizeof(void *) * 2); + EXPECT_EQ(sizeof(SpdyIntrusiveLink<TestItem>), sizeof(void*) * 2); for (int i = 0; i < 10; ++i) { TestItem *e = new TestItem; e->n = i; list1.push_front(e); } - CHECK_EQ(list1.size(), 10); + EXPECT_EQ(list1.size(), 10u); // Verify we can reverse a list because we defined swap for TestItem. std::reverse(list1.begin(), list1.end()); - CHECK_EQ(list1.size(), 10); + EXPECT_EQ(list1.size(), 10u); // Check both const and non-const forward iteration. const TestList& clist1 = list1; @@ -92,28 +92,28 @@ TEST(NewIntrusiveListTest, Basic) { for (; iter != list1.end(); ++iter, ++i) { - CHECK_EQ(iter->n, i); + EXPECT_EQ(iter->n, i); } - CHECK(iter == clist1.end()); - CHECK(iter != clist1.begin()); + EXPECT_EQ(iter, clist1.end()); + EXPECT_NE(iter, clist1.begin()); i = 0; iter = list1.begin(); for (; iter != list1.end(); ++iter, ++i) { - CHECK_EQ(iter->n, i); + EXPECT_EQ(iter->n, i); } - CHECK(iter == clist1.end()); - CHECK(iter != clist1.begin()); + EXPECT_EQ(iter, clist1.end()); + EXPECT_NE(iter, clist1.begin()); - CHECK_EQ(list1.front().n, 0); - CHECK_EQ(list1.back().n, 9); + EXPECT_EQ(list1.front().n, 0); + EXPECT_EQ(list1.back().n, 9); // Verify we can swap 2 lists. TestList list2; list2.swap(list1); - CHECK_EQ(list1.size(), 0); - CHECK_EQ(list2.size(), 10); + EXPECT_EQ(list1.size(), 0u); + EXPECT_EQ(list2.size(), 10u); // Check both const and non-const reverse iteration. const TestList& clist2 = list2; @@ -122,20 +122,20 @@ TEST(NewIntrusiveListTest, Basic) { for (; riter != list2.rend(); ++riter, --i) { - CHECK_EQ(riter->n, i); + EXPECT_EQ(riter->n, i); } - CHECK(riter == clist2.rend()); - CHECK(riter != clist2.rbegin()); + EXPECT_EQ(riter, clist2.rend()); + EXPECT_NE(riter, clist2.rbegin()); riter = list2.rbegin(); i = 9; for (; riter != list2.rend(); ++riter, --i) { - CHECK_EQ(riter->n, i); + EXPECT_EQ(riter->n, i); } - CHECK(riter == clist2.rend()); - CHECK(riter != clist2.rbegin()); + EXPECT_EQ(riter, clist2.rend()); + EXPECT_NE(riter, clist2.rbegin()); while (!list2.empty()) { TestItem *e = &list2.front(); @@ -156,12 +156,12 @@ TEST(NewIntrusiveListTest, Erase) { // Test that erase works. for (int i = 0; i < 10; ++i) { - CHECK_EQ(l.size(), (10 - i)); + EXPECT_EQ(l.size(), (10u - i)); TestList::iterator iter = l.erase(e[i]); - CHECK(iter != TestList::iterator(e[i])); + EXPECT_NE(iter, TestList::iterator(e[i])); - CHECK_EQ(l.size(), (10 - i - 1)); + EXPECT_EQ(l.size(), (10u - i - 1)); delete e[i]; } } @@ -175,15 +175,15 @@ TEST(NewIntrusiveListTest, Insert) { for (int i = 9; i >= 0; --i) { e[i] = new TestItem; iter = l.insert(iter, e[i]); - CHECK_EQ(&(*iter), e[i]); + EXPECT_EQ(&(*iter), e[i]); } - CHECK_EQ(l.size(), 10); + EXPECT_EQ(l.size(), 10u); // Verify insertion order. iter = l.begin(); for (TestItem *item : e) { - CHECK_EQ(&(*iter), item); + EXPECT_EQ(&(*iter), item); iter = l.erase(item); delete item; } @@ -264,7 +264,7 @@ TEST_F(IntrusiveListTest, Splice) { CheckLists(); - ASSERT_EQ(3, secondary_list.size()); + ASSERT_EQ(3u, secondary_list.size()); for (int i = 0; i < 3; ++i) { EXPECT_EQ(&e[i], &*std::next(secondary_list.begin(), i)); } @@ -282,24 +282,24 @@ struct DerivedLinkId {}; struct AbstractBase : public SpdyIntrusiveLink<AbstractBase, BaseLinkId> { virtual ~AbstractBase() = 0; - virtual SpdyString name() { return "AbstractBase"; } + virtual std::string name() { return "AbstractBase"; } }; AbstractBase::~AbstractBase() {} struct DerivedClass : public SpdyIntrusiveLink<DerivedClass, DerivedLinkId>, public AbstractBase { virtual ~DerivedClass() {} - virtual SpdyString name() { return "DerivedClass"; } + virtual std::string name() { return "DerivedClass"; } }; struct VirtuallyDerivedBaseClass : public virtual AbstractBase { virtual ~VirtuallyDerivedBaseClass() = 0; - virtual SpdyString name() { return "VirtuallyDerivedBaseClass"; } + virtual std::string name() { return "VirtuallyDerivedBaseClass"; } }; VirtuallyDerivedBaseClass::~VirtuallyDerivedBaseClass() {} struct VirtuallyDerivedClassA : public SpdyIntrusiveLink<VirtuallyDerivedClassA, DerivedLinkId>, public virtual VirtuallyDerivedBaseClass { virtual ~VirtuallyDerivedClassA() {} - virtual SpdyString name() { return "VirtuallyDerivedClassA"; } + virtual std::string name() { return "VirtuallyDerivedClassA"; } }; struct NonceClass { virtual ~NonceClass() {} @@ -310,7 +310,7 @@ struct VirtuallyDerivedClassB public virtual NonceClass, public virtual VirtuallyDerivedBaseClass { virtual ~VirtuallyDerivedClassB() {} - virtual SpdyString name() { return "VirtuallyDerivedClassB"; } + virtual std::string name() { return "VirtuallyDerivedClassB"; } }; struct VirtuallyDerivedClassC : public SpdyIntrusiveLink<VirtuallyDerivedClassC, DerivedLinkId>, @@ -318,7 +318,7 @@ struct VirtuallyDerivedClassC public virtual NonceClass, public virtual VirtuallyDerivedBaseClass { virtual ~VirtuallyDerivedClassC() {} - virtual SpdyString name() { return "VirtuallyDerivedClassC"; } + virtual std::string name() { return "VirtuallyDerivedClassC"; } }; // Test for multiple layers between the element type and the link. @@ -338,11 +338,11 @@ TEST(NewIntrusiveListTest, HandleInheritanceHierarchies) { DerivedClass elements[2]; EXPECT_TRUE(list.empty()); list.push_back(&elements[0]); - EXPECT_EQ(1, list.size()); + EXPECT_EQ(1u, list.size()); list.push_back(&elements[1]); - EXPECT_EQ(2, list.size()); + EXPECT_EQ(2u, list.size()); list.pop_back(); - EXPECT_EQ(1, list.size()); + EXPECT_EQ(1u, list.size()); list.pop_back(); EXPECT_TRUE(list.empty()); } @@ -351,11 +351,11 @@ TEST(NewIntrusiveListTest, HandleInheritanceHierarchies) { VirtuallyDerivedClassA elements[2]; EXPECT_TRUE(list.empty()); list.push_back(&elements[0]); - EXPECT_EQ(1, list.size()); + EXPECT_EQ(1u, list.size()); list.push_back(&elements[1]); - EXPECT_EQ(2, list.size()); + EXPECT_EQ(2u, list.size()); list.pop_back(); - EXPECT_EQ(1, list.size()); + EXPECT_EQ(1u, list.size()); list.pop_back(); EXPECT_TRUE(list.empty()); } @@ -364,11 +364,11 @@ TEST(NewIntrusiveListTest, HandleInheritanceHierarchies) { VirtuallyDerivedClassC elements[2]; EXPECT_TRUE(list.empty()); list.push_back(&elements[0]); - EXPECT_EQ(1, list.size()); + EXPECT_EQ(1u, list.size()); list.push_back(&elements[1]); - EXPECT_EQ(2, list.size()); + EXPECT_EQ(2u, list.size()); list.pop_back(); - EXPECT_EQ(1, list.size()); + EXPECT_EQ(1u, list.size()); list.pop_back(); EXPECT_TRUE(list.empty()); } @@ -380,13 +380,13 @@ TEST(NewIntrusiveListTest, HandleInheritanceHierarchies) { VirtuallyDerivedClassC d4; EXPECT_TRUE(list.empty()); list.push_back(&d1); - EXPECT_EQ(1, list.size()); + EXPECT_EQ(1u, list.size()); list.push_back(&d2); - EXPECT_EQ(2, list.size()); + EXPECT_EQ(2u, list.size()); list.push_back(&d3); - EXPECT_EQ(3, list.size()); + EXPECT_EQ(3u, list.size()); list.push_back(&d4); - EXPECT_EQ(4, list.size()); + EXPECT_EQ(4u, list.size()); SpdyIntrusiveList<AbstractBase, BaseLinkId>::iterator it = list.begin(); EXPECT_EQ("DerivedClass", (it++)->name()); EXPECT_EQ("VirtuallyDerivedClassA", (it++)->name()); @@ -398,11 +398,11 @@ TEST(NewIntrusiveListTest, HandleInheritanceHierarchies) { templated_base_link::DerivedClass elements[2]; EXPECT_TRUE(list.empty()); list.push_back(&elements[0]); - EXPECT_EQ(1, list.size()); + EXPECT_EQ(1u, list.size()); list.push_back(&elements[1]); - EXPECT_EQ(2, list.size()); + EXPECT_EQ(2u, list.size()); list.pop_back(); - EXPECT_EQ(1, list.size()); + EXPECT_EQ(1u, list.size()); list.pop_back(); EXPECT_TRUE(list.empty()); } diff --git a/chromium/net/third_party/quiche/src/spdy/core/spdy_pinnable_buffer_piece_test.cc b/chromium/net/third_party/quiche/src/spdy/core/spdy_pinnable_buffer_piece_test.cc index d62af5bf37a..9c7f1384fc7 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/spdy_pinnable_buffer_piece_test.cc +++ b/chromium/net/third_party/quiche/src/spdy/core/spdy_pinnable_buffer_piece_test.cc @@ -4,8 +4,9 @@ #include "net/third_party/quiche/src/spdy/core/spdy_pinnable_buffer_piece.h" +#include <string> + #include "net/third_party/quiche/src/spdy/core/spdy_prefixed_buffer_reader.h" -#include "net/third_party/quiche/src/spdy/platform/api/spdy_string.h" #include "net/third_party/quiche/src/spdy/platform/api/spdy_test.h" namespace spdy { @@ -14,14 +15,14 @@ namespace test { class SpdyPinnableBufferPieceTest : public ::testing::Test { protected: - SpdyPrefixedBufferReader Build(const SpdyString& prefix, - const SpdyString& suffix) { + SpdyPrefixedBufferReader Build(const std::string& prefix, + const std::string& suffix) { prefix_ = prefix; suffix_ = suffix; return SpdyPrefixedBufferReader(prefix_.data(), prefix_.length(), suffix_.data(), suffix_.length()); } - SpdyString prefix_, suffix_; + std::string prefix_, suffix_; }; TEST_F(SpdyPinnableBufferPieceTest, Pin) { diff --git a/chromium/net/third_party/quiche/src/spdy/core/spdy_prefixed_buffer_reader_test.cc b/chromium/net/third_party/quiche/src/spdy/core/spdy_prefixed_buffer_reader_test.cc index f570dfc411d..a0f9257f28a 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/spdy_prefixed_buffer_reader_test.cc +++ b/chromium/net/third_party/quiche/src/spdy/core/spdy_prefixed_buffer_reader_test.cc @@ -4,7 +4,8 @@ #include "net/third_party/quiche/src/spdy/core/spdy_prefixed_buffer_reader.h" -#include "net/third_party/quiche/src/spdy/platform/api/spdy_string.h" +#include <string> + #include "net/third_party/quiche/src/spdy/platform/api/spdy_string_piece.h" #include "net/third_party/quiche/src/spdy/platform/api/spdy_test.h" @@ -16,14 +17,14 @@ using testing::ElementsAreArray; class SpdyPrefixedBufferReaderTest : public ::testing::Test { protected: - SpdyPrefixedBufferReader Build(const SpdyString& prefix, - const SpdyString& suffix) { + SpdyPrefixedBufferReader Build(const std::string& prefix, + const std::string& suffix) { prefix_ = prefix; suffix_ = suffix; return SpdyPrefixedBufferReader(prefix_.data(), prefix_.length(), suffix_.data(), suffix_.length()); } - SpdyString prefix_, suffix_; + std::string prefix_, suffix_; }; TEST_F(SpdyPrefixedBufferReaderTest, ReadRawFromPrefix) { diff --git a/chromium/net/third_party/quiche/src/spdy/core/spdy_protocol.cc b/chromium/net/third_party/quiche/src/spdy/core/spdy_protocol.cc index d9b642f4649..dae7006ff37 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/spdy_protocol.cc +++ b/chromium/net/third_party/quiche/src/spdy/core/spdy_protocol.cc @@ -157,7 +157,7 @@ bool ParseSettingsId(SpdySettingsId wire_setting_id, return false; } -SpdyString SettingsIdToString(SpdySettingsId id) { +std::string SettingsIdToString(SpdySettingsId id) { SpdyKnownSettingsId known_id; if (!ParseSettingsId(id, &known_id)) { return SpdyStrCat("SETTINGS_UNKNOWN_", @@ -229,6 +229,20 @@ const char* ErrorCodeToString(SpdyErrorCode error_code) { return "UNKNOWN_ERROR_CODE"; } +const char* WriteSchedulerTypeToString(WriteSchedulerType type) { + switch (type) { + case WriteSchedulerType::LIFO: + return "LIFO"; + case WriteSchedulerType::SPDY: + return "SPDY"; + case WriteSchedulerType::HTTP2: + return "HTTP2"; + case WriteSchedulerType::FIFO: + return "FIFO"; + } + return "UNKNOWN"; +} + size_t GetNumberRequiredContinuationFrames(size_t size) { DCHECK_GT(size, kHttp2MaxControlFrameSendSize); size_t overflow = size - kHttp2MaxControlFrameSendSize; @@ -279,9 +293,9 @@ SpdyDataIR::SpdyDataIR(SpdyStreamId stream_id, SpdyStringPiece data) SpdyDataIR::SpdyDataIR(SpdyStreamId stream_id, const char* data) : SpdyDataIR(stream_id, SpdyStringPiece(data)) {} -SpdyDataIR::SpdyDataIR(SpdyStreamId stream_id, SpdyString data) +SpdyDataIR::SpdyDataIR(SpdyStreamId stream_id, std::string data) : SpdyFrameWithFinIR(stream_id), - data_store_(SpdyMakeUnique<SpdyString>(std::move(data))), + data_store_(SpdyMakeUnique<std::string>(std::move(data))), data_(data_store_->data()), data_len_(data_store_->size()), padded_(false), @@ -378,7 +392,7 @@ SpdyGoAwayIR::SpdyGoAwayIR(SpdyStreamId last_good_stream_id, SpdyGoAwayIR::SpdyGoAwayIR(SpdyStreamId last_good_stream_id, SpdyErrorCode error_code, - SpdyString description) + std::string description) : description_store_(std::move(description)), description_(description_store_) { set_last_good_stream_id(last_good_stream_id); @@ -401,7 +415,7 @@ size_t SpdyGoAwayIR::size() const { SpdyContinuationIR::SpdyContinuationIR(SpdyStreamId stream_id) : SpdyFrameIR(stream_id), end_headers_(false) { - encoding_ = SpdyMakeUnique<SpdyString>(); + encoding_ = SpdyMakeUnique<std::string>(); } SpdyContinuationIR::~SpdyContinuationIR() = default; @@ -505,7 +519,7 @@ size_t SpdyAltSvcIR::size() const { size_t size = kGetAltSvcFrameMinimumSize; size += origin_.length(); // TODO(yasong): estimates the size without serializing the vector. - SpdyString str = + std::string str = SpdyAltSvcWireFormat::SerializeHeaderFieldValue(altsvc_vector_); size += str.size(); return size; diff --git a/chromium/net/third_party/quiche/src/spdy/core/spdy_protocol.h b/chromium/net/third_party/quiche/src/spdy/core/spdy_protocol.h index 2d2ce967e6c..b014107a3ee 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/spdy_protocol.h +++ b/chromium/net/third_party/quiche/src/spdy/core/spdy_protocol.h @@ -16,6 +16,7 @@ #include <map> #include <memory> #include <new> +#include <string> #include <utility> #include "net/third_party/quiche/src/spdy/core/spdy_alt_svc_wire_format.h" @@ -25,7 +26,6 @@ #include "net/third_party/quiche/src/spdy/platform/api/spdy_export.h" #include "net/third_party/quiche/src/spdy/platform/api/spdy_logging.h" #include "net/third_party/quiche/src/spdy/platform/api/spdy_ptr_util.h" -#include "net/third_party/quiche/src/spdy/platform/api/spdy_string.h" #include "net/third_party/quiche/src/spdy/platform/api/spdy_string_piece.h" namespace spdy { @@ -203,6 +203,7 @@ enum class WriteSchedulerType { // https://www.chromium.org/spdy/spdy-protocol/spdy-protocol-draft3-1#TOC-2.3.3-Stream-priority. HTTP2, // Uses HTTP2 (tree-style) priority described in // https://tools.ietf.org/html/rfc7540#section-5.3. + FIFO, // Stream with the smallest stream ID has the highest priority. }; // A SPDY priority is a number between 0 and 7 (inclusive). @@ -273,7 +274,7 @@ SPDY_EXPORT_PRIVATE bool ParseSettingsId(SpdySettingsId wire_setting_id, // Returns a string representation of the |id| for logging/debugging. Returns // the |id| prefixed with "SETTINGS_UNKNOWN_" for unknown SETTINGS IDs. To parse // the |id| into a SpdyKnownSettingsId (if applicable), use ParseSettingsId(). -SPDY_EXPORT_PRIVATE SpdyString SettingsIdToString(SpdySettingsId id); +SPDY_EXPORT_PRIVATE std::string SettingsIdToString(SpdySettingsId id); // Parse |wire_error_code| to a SpdyErrorCode. // Treat unrecognized error codes as INTERNAL_ERROR @@ -284,6 +285,10 @@ SPDY_EXPORT_PRIVATE SpdyErrorCode ParseErrorCode(uint32_t wire_error_code); // for logging/debugging. const char* ErrorCodeToString(SpdyErrorCode error_code); +// Serialize |type| to string for logging/debugging. +SPDY_EXPORT_PRIVATE const char* WriteSchedulerTypeToString( + WriteSchedulerType type); + // Minimum size of a frame, in octets. const size_t kFrameMinimumSize = kFrameHeaderSize; @@ -519,7 +524,7 @@ class SPDY_EXPORT_PRIVATE SpdyDataIR : public SpdyFrameWithFinIR { SpdyDataIR(SpdyStreamId stream_id, const char* data); // Moves data into data_store_. Makes a copy if passed a non-movable string. - SpdyDataIR(SpdyStreamId stream_id, SpdyString data); + SpdyDataIR(SpdyStreamId stream_id, std::string data); // Use in conjunction with SetDataShallow() for shallow-copy on data. explicit SpdyDataIR(SpdyStreamId stream_id); @@ -545,7 +550,7 @@ class SPDY_EXPORT_PRIVATE SpdyDataIR : public SpdyFrameWithFinIR { // Deep-copy of data (keep private copy). void SetDataDeep(SpdyStringPiece data) { - data_store_ = SpdyMakeUnique<SpdyString>(data.data(), data.size()); + data_store_ = SpdyMakeUnique<std::string>(data.data(), data.size()); data_ = data_store_->data(); data_len_ = data.size(); } @@ -575,7 +580,7 @@ class SPDY_EXPORT_PRIVATE SpdyDataIR : public SpdyFrameWithFinIR { private: // Used to store data that this SpdyDataIR should own. - std::unique_ptr<SpdyString> data_store_; + std::unique_ptr<std::string> data_store_; const char* data_; size_t data_len_; @@ -669,7 +674,7 @@ class SPDY_EXPORT_PRIVATE SpdyGoAwayIR : public SpdyFrameIR { // keep description live after constructing this SpdyGoAwayIR. SpdyGoAwayIR(SpdyStreamId last_good_stream_id, SpdyErrorCode error_code, - SpdyString description); + std::string description); SpdyGoAwayIR(const SpdyGoAwayIR&) = delete; SpdyGoAwayIR& operator=(const SpdyGoAwayIR&) = delete; @@ -697,7 +702,7 @@ class SPDY_EXPORT_PRIVATE SpdyGoAwayIR : public SpdyFrameIR { private: SpdyStreamId last_good_stream_id_; SpdyErrorCode error_code_; - const SpdyString description_store_; + const std::string description_store_; const SpdyStringPiece description_; }; @@ -821,14 +826,14 @@ class SPDY_EXPORT_PRIVATE SpdyContinuationIR : public SpdyFrameIR { bool end_headers() const { return end_headers_; } void set_end_headers(bool end_headers) { end_headers_ = end_headers; } - const SpdyString& encoding() const { return *encoding_; } - void take_encoding(std::unique_ptr<SpdyString> encoding) { + const std::string& encoding() const { return *encoding_; } + void take_encoding(std::unique_ptr<std::string> encoding) { encoding_ = std::move(encoding); } size_t size() const override; private: - std::unique_ptr<SpdyString> encoding_; + std::unique_ptr<std::string> encoding_; bool end_headers_; }; @@ -839,12 +844,12 @@ class SPDY_EXPORT_PRIVATE SpdyAltSvcIR : public SpdyFrameIR { SpdyAltSvcIR& operator=(const SpdyAltSvcIR&) = delete; ~SpdyAltSvcIR() override; - SpdyString origin() const { return origin_; } + std::string origin() const { return origin_; } const SpdyAltSvcWireFormat::AlternativeServiceVector& altsvc_vector() const { return altsvc_vector_; } - void set_origin(SpdyString origin) { origin_ = std::move(origin); } + void set_origin(std::string origin) { origin_ = std::move(origin); } void add_altsvc(const SpdyAltSvcWireFormat::AlternativeService& altsvc) { altsvc_vector_.push_back(altsvc); } @@ -856,7 +861,7 @@ class SPDY_EXPORT_PRIVATE SpdyAltSvcIR : public SpdyFrameIR { size_t size() const override; private: - SpdyString origin_; + std::string origin_; SpdyAltSvcWireFormat::AlternativeServiceVector altsvc_vector_; }; @@ -894,7 +899,7 @@ class SPDY_EXPORT_PRIVATE SpdyUnknownIR : public SpdyFrameIR { SpdyUnknownIR(SpdyStreamId stream_id, uint8_t type, uint8_t flags, - SpdyString payload) + std::string payload) : SpdyFrameIR(stream_id), type_(type), flags_(flags), @@ -905,7 +910,7 @@ class SPDY_EXPORT_PRIVATE SpdyUnknownIR : public SpdyFrameIR { uint8_t type() const { return type_; } uint8_t flags() const { return flags_; } size_t length() const { return length_; } - const SpdyString& payload() const { return payload_; } + const std::string& payload() const { return payload_; } void Visit(SpdyFrameVisitor* visitor) const override; @@ -923,7 +928,7 @@ class SPDY_EXPORT_PRIVATE SpdyUnknownIR : public SpdyFrameIR { uint8_t type_; uint8_t flags_; size_t length_; - const SpdyString payload_; + const std::string payload_; }; class SPDY_EXPORT_PRIVATE SpdySerializedFrame { diff --git a/chromium/net/third_party/quiche/src/spdy/core/spdy_protocol_test.cc b/chromium/net/third_party/quiche/src/spdy/core/spdy_protocol_test.cc index e10d2d6c202..542d23d1283 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/spdy_protocol_test.cc +++ b/chromium/net/third_party/quiche/src/spdy/core/spdy_protocol_test.cc @@ -131,7 +131,7 @@ TEST(SpdyProtocolTest, ParseSettingsId) { TEST(SpdyProtocolTest, SettingsIdToString) { struct { SpdySettingsId setting_id; - const SpdyString expected_string; + const std::string expected_string; } test_cases[] = { {0, "SETTINGS_UNKNOWN_0"}, {SETTINGS_HEADER_TABLE_SIZE, "SETTINGS_HEADER_TABLE_SIZE"}, @@ -240,20 +240,20 @@ TEST(SpdyDataIRTest, Construct) { EXPECT_EQ((int)d1.data_len(), d1.flow_control_window_consumed()); // Confirm copies a const string. - const SpdyString foo = "foo"; + const std::string foo = "foo"; SpdyDataIR d3(/* stream_id = */ 3, foo); EXPECT_EQ(foo, d3.data()); EXPECT_EQ((int)d3.data_len(), d3.flow_control_window_consumed()); // Confirm copies a non-const string. - SpdyString bar = "bar"; + std::string bar = "bar"; SpdyDataIR d4(/* stream_id = */ 4, bar); EXPECT_EQ("bar", bar); EXPECT_EQ("bar", SpdyStringPiece(d4.data(), d4.data_len())); // Confirm moves an rvalue reference. Note that the test string "baz" is too // short to trigger the move optimization, and instead a copy occurs. - SpdyString baz = "the quick brown fox"; + std::string baz = "the quick brown fox"; SpdyDataIR d5(/* stream_id = */ 5, std::move(baz)); EXPECT_EQ("", baz); EXPECT_EQ(SpdyStringPiece(d5.data(), d5.data_len()), "the quick brown fox"); diff --git a/chromium/net/third_party/quiche/src/spdy/core/spdy_simple_arena_test.cc b/chromium/net/third_party/quiche/src/spdy/core/spdy_simple_arena_test.cc index 217e2d798e2..60bbe24e725 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/spdy_simple_arena_test.cc +++ b/chromium/net/third_party/quiche/src/spdy/core/spdy_simple_arena_test.cc @@ -4,9 +4,9 @@ #include "net/third_party/quiche/src/spdy/core/spdy_simple_arena.h" +#include <string> #include <vector> -#include "net/third_party/quiche/src/spdy/platform/api/spdy_string.h" #include "net/third_party/quiche/src/spdy/platform/api/spdy_string_piece.h" #include "net/third_party/quiche/src/spdy/platform/api/spdy_test.h" @@ -36,11 +36,11 @@ TEST(SpdySimpleArenaTest, MemdupLargeString) { TEST(SpdySimpleArenaTest, MultipleBlocks) { SpdySimpleArena arena(40 /* block size */); - std::vector<SpdyString> strings = { + std::vector<std::string> strings = { "One decently long string.", "Another string.", "A third string that will surely go in a different block."}; std::vector<SpdyStringPiece> copies; - for (const SpdyString& s : strings) { + for (const std::string& s : strings) { SpdyStringPiece sp(arena.Memdup(s.data(), s.size()), s.size()); copies.push_back(sp); } diff --git a/chromium/net/third_party/quiche/src/spdy/core/spdy_test_utils.cc b/chromium/net/third_party/quiche/src/spdy/core/spdy_test_utils.cc index bf54a1a8361..f248c86ae5d 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/spdy_test_utils.cc +++ b/chromium/net/third_party/quiche/src/spdy/core/spdy_test_utils.cc @@ -18,10 +18,10 @@ namespace spdy { namespace test { -SpdyString HexDumpWithMarks(const unsigned char* data, - int length, - const bool* marks, - int mark_length) { +std::string HexDumpWithMarks(const unsigned char* data, + int length, + const bool* marks, + int mark_length) { static const char kHexChars[] = "0123456789abcdef"; static const int kColumns = 4; @@ -32,7 +32,7 @@ SpdyString HexDumpWithMarks(const unsigned char* data, mark_length = std::min(mark_length, kSizeLimit); } - SpdyString hex; + std::string hex; for (const unsigned char* row = data; length > 0; row += kColumns, length -= kColumns) { for (const unsigned char* p = row; p < row + 4; ++p) { @@ -58,7 +58,7 @@ SpdyString HexDumpWithMarks(const unsigned char* data, return hex; } -void CompareCharArraysWithHexError(const SpdyString& description, +void CompareCharArraysWithHexError(const std::string& description, const unsigned char* actual, const int actual_len, const unsigned char* expected, diff --git a/chromium/net/third_party/quiche/src/spdy/core/spdy_test_utils.h b/chromium/net/third_party/quiche/src/spdy/core/spdy_test_utils.h index 03a6caf896d..e2c3b477017 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/spdy_test_utils.h +++ b/chromium/net/third_party/quiche/src/spdy/core/spdy_test_utils.h @@ -7,12 +7,12 @@ #include <cstddef> #include <cstdint> +#include <string> #include "net/third_party/quiche/src/spdy/core/spdy_header_block.h" #include "net/third_party/quiche/src/spdy/core/spdy_headers_handler_interface.h" #include "net/third_party/quiche/src/spdy/core/spdy_protocol.h" #include "net/third_party/quiche/src/spdy/platform/api/spdy_bug_tracker.h" -#include "net/third_party/quiche/src/spdy/platform/api/spdy_string.h" #include "net/third_party/quiche/src/spdy/platform/api/spdy_string_piece.h" namespace spdy { @@ -24,12 +24,12 @@ inline bool operator==(SpdyStringPiece x, namespace test { -SpdyString HexDumpWithMarks(const unsigned char* data, - int length, - const bool* marks, - int mark_length); +std::string HexDumpWithMarks(const unsigned char* data, + int length, + const bool* marks, + int mark_length); -void CompareCharArraysWithHexError(const SpdyString& description, +void CompareCharArraysWithHexError(const std::string& description, const unsigned char* actual, const int actual_len, const unsigned char* expected, diff --git a/chromium/net/third_party/quiche/src/spdy/core/write_scheduler.h b/chromium/net/third_party/quiche/src/spdy/core/write_scheduler.h index 98db442437e..c2c048f543b 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/write_scheduler.h +++ b/chromium/net/third_party/quiche/src/spdy/core/write_scheduler.h @@ -6,12 +6,12 @@ #define QUICHE_SPDY_CORE_WRITE_SCHEDULER_H_ #include <cstdint> +#include <string> #include <tuple> #include <vector> #include "net/third_party/quiche/src/spdy/core/spdy_protocol.h" #include "net/third_party/quiche/src/spdy/platform/api/spdy_export.h" -#include "net/third_party/quiche/src/spdy/platform/api/spdy_string.h" namespace spdy { @@ -154,7 +154,7 @@ class SPDY_EXPORT_PRIVATE WriteScheduler { virtual size_t NumRegisteredStreams() const = 0; // Returns summary of internal state, for logging/debugging. - virtual SpdyString DebugString() const = 0; + virtual std::string DebugString() const = 0; }; } // namespace spdy diff --git a/chromium/net/third_party/quiche/src/spdy/platform/api/spdy_string.h b/chromium/net/third_party/quiche/src/spdy/platform/api/spdy_string.h deleted file mode 100644 index 16eb9e5569d..00000000000 --- a/chromium/net/third_party/quiche/src/spdy/platform/api/spdy_string.h +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef QUICHE_SPDY_PLATFORM_API_SPDY_STRING_H_ -#define QUICHE_SPDY_PLATFORM_API_SPDY_STRING_H_ - -#include "net/spdy/platform/impl/spdy_string_impl.h" - -namespace spdy { - -using SpdyString = SpdyStringImpl; - -} // namespace spdy - -#endif // QUICHE_SPDY_PLATFORM_API_SPDY_STRING_H_ diff --git a/chromium/net/third_party/quiche/src/spdy/platform/api/spdy_string_utils.h b/chromium/net/third_party/quiche/src/spdy/platform/api/spdy_string_utils.h index 7554f796a97..e2f709df48a 100644 --- a/chromium/net/third_party/quiche/src/spdy/platform/api/spdy_string_utils.h +++ b/chromium/net/third_party/quiche/src/spdy/platform/api/spdy_string_utils.h @@ -5,6 +5,7 @@ #ifndef QUICHE_SPDY_PLATFORM_API_SPDY_STRING_UTILS_H_ #define QUICHE_SPDY_PLATFORM_API_SPDY_STRING_UTILS_H_ +#include <string> #include <utility> // The following header file has to be included from at least @@ -13,19 +14,18 @@ // non-test code. #include "net/third_party/quiche/src/spdy/platform/api/spdy_mem_slice.h" -#include "net/third_party/quiche/src/spdy/platform/api/spdy_string.h" #include "net/third_party/quiche/src/spdy/platform/api/spdy_string_piece.h" #include "net/spdy/platform/impl/spdy_string_utils_impl.h" namespace spdy { template <typename... Args> -inline SpdyString SpdyStrCat(const Args&... args) { +inline std::string SpdyStrCat(const Args&... args) { return SpdyStrCatImpl(std::forward<const Args&>(args)...); } template <typename... Args> -inline void SpdyStrAppend(SpdyString* output, const Args&... args) { +inline void SpdyStrAppend(std::string* output, const Args&... args) { SpdyStrAppendImpl(output, std::forward<const Args&>(args)...); } @@ -33,7 +33,7 @@ inline char SpdyHexDigitToInt(char c) { return SpdyHexDigitToIntImpl(c); } -inline SpdyString SpdyHexDecode(SpdyStringPiece data) { +inline std::string SpdyHexDecode(SpdyStringPiece data) { return SpdyHexDecodeImpl(data); } @@ -41,15 +41,15 @@ inline bool SpdyHexDecodeToUInt32(SpdyStringPiece data, uint32_t* out) { return SpdyHexDecodeToUInt32Impl(data, out); } -inline SpdyString SpdyHexEncode(const char* bytes, size_t size) { +inline std::string SpdyHexEncode(const char* bytes, size_t size) { return SpdyHexEncodeImpl(bytes, size); } -inline SpdyString SpdyHexEncodeUInt32AndTrim(uint32_t data) { +inline std::string SpdyHexEncodeUInt32AndTrim(uint32_t data) { return SpdyHexEncodeUInt32AndTrimImpl(data); } -inline SpdyString SpdyHexDump(SpdyStringPiece data) { +inline std::string SpdyHexDump(SpdyStringPiece data) { return SpdyHexDumpImpl(data); } diff --git a/chromium/net/third_party/quiche/src/spdy/platform/api/spdy_string_utils_test.cc b/chromium/net/third_party/quiche/src/spdy/platform/api/spdy_string_utils_test.cc index 91656ae165c..f6f094f999f 100644 --- a/chromium/net/third_party/quiche/src/spdy/platform/api/spdy_string_utils_test.cc +++ b/chromium/net/third_party/quiche/src/spdy/platform/api/spdy_string_utils_test.cc @@ -19,7 +19,7 @@ TEST(SpdyStringUtilsTest, SpdyStrCat) { // Single string-like argument. const char kFoo[] = "foo"; - const SpdyString string_foo(kFoo); + const std::string string_foo(kFoo); const SpdyStringPiece stringpiece_foo(string_foo); EXPECT_EQ("foo", SpdyStrCat(kFoo)); EXPECT_EQ("foo", SpdyStrCat(string_foo)); @@ -28,7 +28,7 @@ TEST(SpdyStringUtilsTest, SpdyStrCat) { // Two string-like arguments. const char kBar[] = "bar"; const SpdyStringPiece stringpiece_bar(kBar); - const SpdyString string_bar(kBar); + const std::string string_bar(kBar); EXPECT_EQ("foobar", SpdyStrCat(kFoo, kBar)); EXPECT_EQ("foobar", SpdyStrCat(kFoo, string_bar)); EXPECT_EQ("foobar", SpdyStrCat(kFoo, stringpiece_bar)); @@ -72,13 +72,13 @@ TEST(SpdyStringUtilsTest, SpdyStrCat) { TEST(SpdyStringUtilsTest, SpdyStrAppend) { // No arguments on empty string. - SpdyString output; + std::string output; SpdyStrAppend(&output); EXPECT_TRUE(output.empty()); // Single string-like argument. const char kFoo[] = "foo"; - const SpdyString string_foo(kFoo); + const std::string string_foo(kFoo); const SpdyStringPiece stringpiece_foo(string_foo); SpdyStrAppend(&output, kFoo); EXPECT_EQ("foo", output); @@ -96,7 +96,7 @@ TEST(SpdyStringUtilsTest, SpdyStrAppend) { // Two string-like arguments. const char kBar[] = "bar"; const SpdyStringPiece stringpiece_bar(kBar); - const SpdyString string_bar(kBar); + const std::string string_bar(kBar); SpdyStrAppend(&output, kFoo, kBar); EXPECT_EQ("foobar", output); SpdyStrAppend(&output, kFoo, string_bar); diff --git a/chromium/net/third_party/uri_template/uri_template_fuzzer.cc b/chromium/net/third_party/uri_template/uri_template_fuzzer.cc index 148dbbad8d3..08731dd1001 100644 --- a/chromium/net/third_party/uri_template/uri_template_fuzzer.cc +++ b/chromium/net/third_party/uri_template/uri_template_fuzzer.cc @@ -4,7 +4,7 @@ #include "net/third_party/uri_template/uri_template.h" -#include "third_party/libFuzzer/src/utils/FuzzedDataProvider.h" +#include <fuzzer/FuzzedDataProvider.h> // Entry point for LibFuzzer. extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { |