diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-03-11 11:32:04 +0100 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-03-18 13:40:17 +0000 |
commit | 31ccca0778db85c159634478b4ec7997f6704860 (patch) | |
tree | 3d33fc3afd9d5ec95541e1bbe074a9cf8da12a0e /chromium/net/third_party/quiche/src/quic/core/qpack | |
parent | 248b70b82a40964d5594eb04feca0fa36716185d (diff) | |
download | qtwebengine-chromium-31ccca0778db85c159634478b4ec7997f6704860.tar.gz |
BASELINE: Update Chromium to 80.0.3987.136
Change-Id: I98e1649aafae85ba3a83e67af00bb27ef301db7b
Reviewed-by: Jüri Valdmann <juri.valdmann@qt.io>
Diffstat (limited to 'chromium/net/third_party/quiche/src/quic/core/qpack')
47 files changed, 873 insertions, 1346 deletions
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/offline/README.md b/chromium/net/third_party/quiche/src/quic/core/qpack/offline/README.md deleted file mode 100644 index 4f6697c66b5..00000000000 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/offline/README.md +++ /dev/null @@ -1,28 +0,0 @@ -# QPACK Offline Interop Testing tools - -See -[QPACK Offline Interop](https://github.com/quicwg/base-drafts/wiki/QPACK-Offline-Interop) -for description of test data format. - -Example usage: - -```shell -$ # Download test data -$ cd $TEST_DATA -$ git clone https://github.com/qpackers/qifs.git -$ TEST_ENCODED_DATA=`pwd`/qifs/encoded/qpack-03 -$ TEST_QIF_DATA=`pwd`/qifs/qifs -$ -$ # Decode encoded test data in four files and verify that they match -$ # the original headers in corresponding files -$ $BIN/qpack_offline_decoder \ -> $TEST_ENCODED_DATA/f5/fb-req.qifencoded.4096.100.0 \ -> $TEST_QIF_DATA/fb-req.qif -> $TEST_ENCODED_DATA/h2o/fb-req-hq.out.512.0.1 \ -> $TEST_QIF_DATA/fb-req-hq.qif -> $TEST_ENCODED_DATA/ls-qpack/fb-resp-hq.out.0.0.0 \ -> $TEST_QIF_DATA/fb-resp-hq.qif -> $TEST_ENCODED_DATA/proxygen/netbsd.qif.proxygen.out.4096.0.0 \ -> $TEST_QIF_DATA/netbsd.qif -$ -``` 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 deleted file mode 100644 index 379cb6ad835..00000000000 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/offline/qpack_offline_decoder.cc +++ /dev/null @@ -1,312 +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/qpack/offline/qpack_offline_decoder.h" - -#include <cstdint> -#include <string> -#include <utility> - -#include "net/third_party/quiche/src/quic/core/qpack/qpack_test_utils.h" -#include "net/third_party/quiche/src/quic/core/quic_types.h" -#include "net/third_party/quiche/src/quic/platform/api/quic_endian.h" -#include "net/third_party/quiche/src/quic/platform/api/quic_file_utils.h" -#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" -#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h" - -namespace quic { - -QpackOfflineDecoder::QpackOfflineDecoder() - : encoder_stream_error_detected_(false) {} - -bool QpackOfflineDecoder::DecodeAndVerifyOfflineData( - QuicStringPiece input_filename, - QuicStringPiece expected_headers_filename) { - if (!ParseInputFilename(input_filename)) { - QUIC_LOG(ERROR) << "Error parsing input filename " << input_filename; - return false; - } - - if (!DecodeHeaderBlocksFromFile(input_filename)) { - QUIC_LOG(ERROR) << "Error decoding header blocks in " << input_filename; - return false; - } - - if (!VerifyDecodedHeaderLists(expected_headers_filename)) { - QUIC_LOG(ERROR) << "Header lists decoded from " << input_filename - << " to not match expected headers parsed from " - << expected_headers_filename; - return false; - } - - return true; -} - -void QpackOfflineDecoder::OnEncoderStreamError(QuicStringPiece error_message) { - QUIC_LOG(ERROR) << "Encoder stream error: " << error_message; - encoder_stream_error_detected_ = true; -} - -bool QpackOfflineDecoder::ParseInputFilename(QuicStringPiece input_filename) { - auto pieces = QuicTextUtils::Split(input_filename, '.'); - - if (pieces.size() < 3) { - QUIC_LOG(ERROR) << "Not enough fields in input filename " << input_filename; - return false; - } - - auto piece_it = pieces.rbegin(); - - // Acknowledgement mode: 1 for immediate, 0 for none. - bool immediate_acknowledgement = false; - if (*piece_it == "0") { - immediate_acknowledgement = false; - } else if (*piece_it == "1") { - immediate_acknowledgement = true; - } else { - QUIC_LOG(ERROR) - << "Header acknowledgement field must be 0 or 1 in input filename " - << input_filename; - return false; - } - - ++piece_it; - - // Maximum allowed number of blocked streams. - uint64_t max_blocked_streams = 0; - if (!QuicTextUtils::StringToUint64(*piece_it, &max_blocked_streams)) { - QUIC_LOG(ERROR) << "Error parsing part of input filename \"" << *piece_it - << "\" as an integer."; - return false; - } - - ++piece_it; - - // Maximum Dynamic Table Capacity in bytes - uint64_t maximum_dynamic_table_capacity = 0; - if (!QuicTextUtils::StringToUint64(*piece_it, - &maximum_dynamic_table_capacity)) { - QUIC_LOG(ERROR) << "Error parsing part of input filename \"" << *piece_it - << "\" as an integer."; - return false; - } - qpack_decoder_ = std::make_unique<QpackDecoder>( - maximum_dynamic_table_capacity, max_blocked_streams, this); - qpack_decoder_->set_qpack_stream_sender_delegate( - &decoder_stream_sender_delegate_); - - // The initial dynamic table capacity is zero according to - // https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#eviction. - // However, for historical reasons, offline interop encoders use - // |maximum_dynamic_table_capacity| as initial capacity. - qpack_decoder_->OnSetDynamicTableCapacity(maximum_dynamic_table_capacity); - - return true; -} - -bool QpackOfflineDecoder::DecodeHeaderBlocksFromFile( - QuicStringPiece input_filename) { - // Store data in |input_data_storage|; use a QuicStringPiece to efficiently - // keep track of remaining portion yet to be decoded. - std::string input_data_storage; - ReadFileContents(input_filename, &input_data_storage); - QuicStringPiece input_data(input_data_storage); - - while (!input_data.empty()) { - // Parse stream_id and length. - if (input_data.size() < sizeof(uint64_t) + sizeof(uint32_t)) { - QUIC_LOG(ERROR) << "Unexpected end of input file."; - return false; - } - - uint64_t stream_id = QuicEndian::NetToHost64( - *reinterpret_cast<const uint64_t*>(input_data.data())); - input_data = input_data.substr(sizeof(uint64_t)); - - uint32_t length = QuicEndian::NetToHost32( - *reinterpret_cast<const uint32_t*>(input_data.data())); - input_data = input_data.substr(sizeof(uint32_t)); - - if (input_data.size() < length) { - QUIC_LOG(ERROR) << "Unexpected end of input file."; - return false; - } - - // Parse data. - QuicStringPiece data = input_data.substr(0, length); - input_data = input_data.substr(length); - - // Process data. - if (stream_id == 0) { - qpack_decoder_->encoder_stream_receiver()->Decode(data); - - if (encoder_stream_error_detected_) { - QUIC_LOG(ERROR) << "Error detected on encoder stream."; - return false; - } - } else { - auto headers_handler = std::make_unique<test::TestHeadersHandler>(); - auto progressive_decoder = qpack_decoder_->CreateProgressiveDecoder( - stream_id, headers_handler.get()); - - progressive_decoder->Decode(data); - progressive_decoder->EndHeaderBlock(); - - if (headers_handler->decoding_error_detected()) { - QUIC_LOG(ERROR) << "Sync decoding error on stream " << stream_id << ": " - << headers_handler->error_message(); - return false; - } - - decoders_.push_back({std::move(headers_handler), - std::move(progressive_decoder), stream_id}); - } - - // Move decoded header lists from TestHeadersHandlers and append them to - // |decoded_header_lists_| while preserving the order in |decoders_|. - while (!decoders_.empty() && - decoders_.front().headers_handler->decoding_completed()) { - Decoder* decoder = &decoders_.front(); - - if (decoder->headers_handler->decoding_error_detected()) { - QUIC_LOG(ERROR) << "Async decoding error on stream " - << decoder->stream_id << ": " - << decoder->headers_handler->error_message(); - return false; - } - - if (!decoder->headers_handler->decoding_completed()) { - QUIC_LOG(ERROR) << "Decoding incomplete after reading entire" - " file, on stream " - << decoder->stream_id; - return false; - } - - decoded_header_lists_.push_back( - decoder->headers_handler->ReleaseHeaderList()); - decoders_.pop_front(); - } - } - - if (!decoders_.empty()) { - DCHECK(!decoders_.front().headers_handler->decoding_completed()); - - QUIC_LOG(ERROR) << "Blocked decoding uncomplete after reading entire" - " file, on stream " - << decoders_.front().stream_id; - return false; - } - - return true; -} - -bool QpackOfflineDecoder::VerifyDecodedHeaderLists( - QuicStringPiece expected_headers_filename) { - // Store data in |expected_headers_data_storage|; use a QuicStringPiece to - // efficiently keep track of remaining portion yet to be decoded. - std::string expected_headers_data_storage; - ReadFileContents(expected_headers_filename, &expected_headers_data_storage); - QuicStringPiece expected_headers_data(expected_headers_data_storage); - - while (!decoded_header_lists_.empty()) { - spdy::SpdyHeaderBlock decoded_header_list = - std::move(decoded_header_lists_.front()); - decoded_header_lists_.pop_front(); - - spdy::SpdyHeaderBlock expected_header_list; - if (!ReadNextExpectedHeaderList(&expected_headers_data, - &expected_header_list)) { - QUIC_LOG(ERROR) - << "Error parsing expected header list to match next decoded " - "header list."; - return false; - } - - if (!CompareHeaderBlocks(std::move(decoded_header_list), - std::move(expected_header_list))) { - QUIC_LOG(ERROR) << "Decoded header does not match expected header."; - return false; - } - } - - if (!expected_headers_data.empty()) { - QUIC_LOG(ERROR) - << "Not enough encoded header lists to match expected ones."; - return false; - } - - return true; -} - -bool QpackOfflineDecoder::ReadNextExpectedHeaderList( - QuicStringPiece* expected_headers_data, - spdy::SpdyHeaderBlock* expected_header_list) { - while (true) { - QuicStringPiece::size_type endline = expected_headers_data->find('\n'); - - // Even last header list must be followed by an empty line. - if (endline == QuicStringPiece::npos) { - QUIC_LOG(ERROR) << "Unexpected end of expected header list file."; - return false; - } - - if (endline == 0) { - // Empty line indicates end of header list. - *expected_headers_data = expected_headers_data->substr(1); - return true; - } - - QuicStringPiece header_field = expected_headers_data->substr(0, endline); - auto pieces = QuicTextUtils::Split(header_field, '\t'); - - if (pieces.size() != 2) { - QUIC_LOG(ERROR) << "Header key and value must be separated by TAB."; - return false; - } - - expected_header_list->AppendValueOrAddHeader(pieces[0], pieces[1]); - - *expected_headers_data = expected_headers_data->substr(endline + 1); - } -} - -bool QpackOfflineDecoder::CompareHeaderBlocks( - spdy::SpdyHeaderBlock decoded_header_list, - spdy::SpdyHeaderBlock expected_header_list) { - if (decoded_header_list == expected_header_list) { - return true; - } - - // The h2o decoder reshuffles the "content-length" header and pseudo-headers, - // see - // https://github.com/qpackers/qifs/blob/master/encoded/qpack-03/h2o/README.md. - // Remove such headers one by one if they match. - const char* kContentLength = "content-length"; - const char* kPseudoHeaderPrefix = ":"; - for (spdy::SpdyHeaderBlock::iterator decoded_it = decoded_header_list.begin(); - decoded_it != decoded_header_list.end();) { - const QuicStringPiece key = decoded_it->first; - if (key != kContentLength && - !QuicTextUtils::StartsWith(key, kPseudoHeaderPrefix)) { - ++decoded_it; - continue; - } - spdy::SpdyHeaderBlock::iterator expected_it = - expected_header_list.find(key); - if (expected_it == expected_header_list.end() || - decoded_it->second != expected_it->second) { - ++decoded_it; - continue; - } - // SpdyHeaderBlock does not support erasing by iterator, only by key. - ++decoded_it; - expected_header_list.erase(key); - // This will invalidate |key|. - decoded_header_list.erase(key); - } - - return decoded_header_list == expected_header_list; -} - -} // namespace quic 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 deleted file mode 100644 index c12c49181fa..00000000000 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/offline/qpack_offline_decoder.h +++ /dev/null @@ -1,86 +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_QPACK_OFFLINE_QPACK_OFFLINE_DECODER_H_ -#define QUICHE_QUIC_CORE_QPACK_OFFLINE_QPACK_OFFLINE_DECODER_H_ - -#include <list> - -#include "net/third_party/quiche/src/quic/core/qpack/qpack_decoder.h" -#include "net/third_party/quiche/src/quic/core/qpack/qpack_decoder_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_string_piece.h" -#include "net/third_party/quiche/src/spdy/core/spdy_header_block.h" - -namespace quic { - -// A decoder to read encoded data from a file, decode it, and compare to -// a list of expected header lists read from another file. File format is -// described at -// https://github.com/quicwg/base-drafts/wiki/QPACK-Offline-Interop. -class QpackOfflineDecoder : public QpackDecoder::EncoderStreamErrorDelegate { - public: - QpackOfflineDecoder(); - ~QpackOfflineDecoder() override = default; - - // Read encoded header blocks and encoder stream data from |input_filename| - // and decode them, read expected header lists from - // |expected_headers_filename|, and compare decoded header lists to expected - // ones. Returns true if there is an equal number of them and the - // corresponding ones match, false otherwise. - bool DecodeAndVerifyOfflineData(QuicStringPiece input_filename, - QuicStringPiece expected_headers_filename); - - // QpackDecoder::EncoderStreamErrorDelegate implementation: - void OnEncoderStreamError(QuicStringPiece error_message) override; - - private: - // Data structure to hold TestHeadersHandler and QpackProgressiveDecoder until - // decoding of a header header block (and all preceding header blocks) is - // complete. - struct Decoder { - std::unique_ptr<test::TestHeadersHandler> headers_handler; - std::unique_ptr<QpackProgressiveDecoder> progressive_decoder; - uint64_t stream_id; - }; - - // Parse decoder parameters from |input_filename| and set up |qpack_decoder_| - // accordingly. - bool ParseInputFilename(QuicStringPiece input_filename); - - // Read encoded header blocks and encoder stream data from |input_filename|, - // pass them to |qpack_decoder_| for decoding, and add decoded header lists to - // |decoded_header_lists_|. - bool DecodeHeaderBlocksFromFile(QuicStringPiece input_filename); - - // Read expected header lists from |expected_headers_filename| and verify - // decoded header lists in |decoded_header_lists_| against them. - bool VerifyDecodedHeaderLists(QuicStringPiece expected_headers_filename); - - // Parse next header list from |*expected_headers_data| into - // |*expected_header_list|, removing consumed data from the beginning of - // |*expected_headers_data|. Returns true on success, false if parsing fails. - bool ReadNextExpectedHeaderList(QuicStringPiece* expected_headers_data, - spdy::SpdyHeaderBlock* expected_header_list); - - // Compare two header lists. Allow for different orders of certain headers as - // described at - // https://github.com/qpackers/qifs/blob/master/encoded/qpack-03/h2o/README.md. - bool CompareHeaderBlocks(spdy::SpdyHeaderBlock decoded_header_list, - spdy::SpdyHeaderBlock expected_header_list); - - bool encoder_stream_error_detected_; - NoopQpackStreamSenderDelegate decoder_stream_sender_delegate_; - std::unique_ptr<QpackDecoder> qpack_decoder_; - - // Objects necessary for decoding, one list element for each header block. - std::list<Decoder> decoders_; - - // Decoded header lists. - std::list<spdy::SpdyHeaderBlock> decoded_header_lists_; -}; - -} // namespace quic - -#endif // QUICHE_QUIC_CORE_QPACK_OFFLINE_QPACK_OFFLINE_DECODER_H_ 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 index ffec17280f6..3544f1ca10b 100644 --- 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 @@ -24,7 +24,7 @@ bool QpackBlockingManager::OnHeaderAcknowledgement(QuicStreamId stream_id) { const uint64_t required_index_count = RequiredInsertCount(indices); if (known_received_count_ < required_index_count) { - IncreaseKnownReceivedCountTo(required_index_count); + known_received_count_ = required_index_count; } DecreaseReferenceCounts(indices); @@ -56,7 +56,7 @@ bool QpackBlockingManager::OnInsertCountIncrement(uint64_t increment) { return false; } - IncreaseKnownReceivedCountTo(known_received_count_ + increment); + known_received_count_ += increment; return true; } @@ -68,16 +68,6 @@ void QpackBlockingManager::OnHeaderBlockSent(QuicStreamId stream_id, 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}); -} - bool QpackBlockingManager::blocking_allowed_on_stream( QuicStreamId stream_id, uint64_t maximum_blocked_streams) const { @@ -141,26 +131,6 @@ 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); 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 index 60f7db7060a..6d2df9cfcf8 100644 --- 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 @@ -48,12 +48,6 @@ class QUIC_EXPORT_PRIVATE QpackBlockingManager { // 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 true if sending blocking references on stream |stream_id| would not // increase the total number of blocked streams above // |maximum_blocked_streams|. Note that if |stream_id| is already blocked @@ -85,11 +79,6 @@ class QUIC_EXPORT_PRIVATE QpackBlockingManager { 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); @@ -98,13 +87,7 @@ class QUIC_EXPORT_PRIVATE QpackBlockingManager { // 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. + // Number of references in |header_blocks_| for each entry index. std::map<uint64_t, uint64_t> entry_reference_counts_; uint64_t known_received_count_; 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 index 64bfb97f71d..d92dda549ef 100644 --- 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 @@ -236,88 +236,6 @@ TEST_F(QpackBlockingManagerTest, CancelStream) { 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. - EXPECT_TRUE(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. - EXPECT_TRUE(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. - EXPECT_TRUE(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()); -} - TEST_F(QpackBlockingManagerTest, BlockingAllowedOnStream) { const QuicStreamId kStreamId1 = 1; const QuicStreamId kStreamId2 = 2; diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoded_headers_accumulator.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoded_headers_accumulator.cc index 9cce57365b6..e16aa5a2de7 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoded_headers_accumulator.cc +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoded_headers_accumulator.cc @@ -8,6 +8,12 @@ namespace quic { +namespace { + +size_t kHeaderFieldSizeOverhead = 32; + +} + QpackDecodedHeadersAccumulator::QpackDecodedHeadersAccumulator( QuicStreamId id, QpackDecoder* qpack_decoder, @@ -15,12 +21,13 @@ QpackDecodedHeadersAccumulator::QpackDecodedHeadersAccumulator( size_t max_header_list_size) : decoder_(qpack_decoder->CreateProgressiveDecoder(id, this)), visitor_(visitor), - uncompressed_header_bytes_(0), + max_header_list_size_(max_header_list_size), + uncompressed_header_bytes_including_overhead_(0), + uncompressed_header_bytes_without_overhead_(0), compressed_header_bytes_(0), + header_list_size_limit_exceeded_(false), headers_decoded_(false), - blocked_(false), error_detected_(false) { - quic_header_list_.set_max_header_list_size(max_header_list_size); quic_header_list_.OnHeaderBlockStart(); } @@ -28,8 +35,21 @@ void QpackDecodedHeadersAccumulator::OnHeaderDecoded(QuicStringPiece name, QuicStringPiece value) { DCHECK(!error_detected_); - uncompressed_header_bytes_ += name.size() + value.size(); - quic_header_list_.OnHeader(name, value); + uncompressed_header_bytes_without_overhead_ += name.size() + value.size(); + + if (header_list_size_limit_exceeded_) { + return; + } + + uncompressed_header_bytes_including_overhead_ += + name.size() + value.size() + kHeaderFieldSizeOverhead; + + if (uncompressed_header_bytes_including_overhead_ > max_header_list_size_) { + header_list_size_limit_exceeded_ = true; + quic_header_list_.Clear(); + } else { + quic_header_list_.OnHeader(name, value); + } } void QpackDecodedHeadersAccumulator::OnDecodingCompleted() { @@ -37,12 +57,12 @@ void QpackDecodedHeadersAccumulator::OnDecodingCompleted() { DCHECK(!error_detected_); headers_decoded_ = true; - quic_header_list_.OnHeaderBlockEnd(uncompressed_header_bytes_, - compressed_header_bytes_); - if (blocked_) { - visitor_->OnHeadersDecoded(quic_header_list_); - } + quic_header_list_.OnHeaderBlockEnd( + uncompressed_header_bytes_without_overhead_, compressed_header_bytes_); + + // Might destroy |this|. + visitor_->OnHeadersDecoded(std::move(quic_header_list_)); } void QpackDecodedHeadersAccumulator::OnDecodingErrorDetected( @@ -51,51 +71,24 @@ void QpackDecodedHeadersAccumulator::OnDecodingErrorDetected( DCHECK(!headers_decoded_); error_detected_ = true; - // Copy error message to ensure it remains valid for the lifetime of |this|. - error_message_.assign(error_message.data(), error_message.size()); - - if (blocked_) { - visitor_->OnHeaderDecodingError(); - } + // Might destroy |this|. + visitor_->OnHeaderDecodingError(error_message); } -bool QpackDecodedHeadersAccumulator::Decode(QuicStringPiece data) { +void QpackDecodedHeadersAccumulator::Decode(QuicStringPiece data) { DCHECK(!error_detected_); compressed_header_bytes_ += data.size(); + // Might destroy |this|. decoder_->Decode(data); - - return !error_detected_; } -QpackDecodedHeadersAccumulator::Status -QpackDecodedHeadersAccumulator::EndHeaderBlock() { +void QpackDecodedHeadersAccumulator::EndHeaderBlock() { DCHECK(!error_detected_); DCHECK(!headers_decoded_); + // Might destroy |this|. decoder_->EndHeaderBlock(); - - if (error_detected_) { - DCHECK(!headers_decoded_); - return Status::kError; - } - - if (headers_decoded_) { - return Status::kSuccess; - } - - blocked_ = true; - return Status::kBlocked; -} - -const QuicHeaderList& QpackDecodedHeadersAccumulator::quic_header_list() const { - DCHECK(!error_detected_); - return quic_header_list_; -} - -QuicStringPiece QpackDecodedHeadersAccumulator::error_message() const { - DCHECK(error_detected_); - return error_message_; } } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoded_headers_accumulator.h b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoded_headers_accumulator.h index c64932214dc..0a2db6a348d 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoded_headers_accumulator.h +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoded_headers_accumulator.h @@ -20,32 +20,30 @@ class QpackDecoder; // A class that creates and owns a QpackProgressiveDecoder instance, accumulates // decoded headers in a QuicHeaderList, and keeps track of uncompressed and -// compressed size so that it can be passed to QuicHeaderList::EndHeaderBlock(). +// compressed size so that it can be passed to +// QuicHeaderList::OnHeaderBlockEnd(). class QUIC_EXPORT_PRIVATE QpackDecodedHeadersAccumulator : public QpackProgressiveDecoder::HeadersHandlerInterface { public: - // Return value for EndHeaderBlock(). - enum class Status { - // Headers have been successfully decoded. - kSuccess, - // An error has occurred. - kError, - // Decoding is blocked. - kBlocked - }; - - // Visitor interface used for blocked decoding. Exactly one visitor method - // will be called if EndHeaderBlock() returned kBlocked. No visitor method - // will be called if EndHeaderBlock() returned any other value. - class Visitor { + // Visitor interface to signal success or error. + // Exactly one method will be called. + // Methods may be called synchronously from Decode() and EndHeaderBlock(), + // or asynchronously. + // Method implementations are allowed to destroy |this|. + class QUIC_EXPORT_PRIVATE Visitor { public: virtual ~Visitor() = default; - // Called when headers are successfully decoded. + // Called when headers are successfully decoded. If header list size + // exceeds the limit specified via |max_header_list_size| in + // QpackDecodedHeadersAccumulator constructor, then |headers| will be empty, + // but will still have the correct compressed and uncompressed size + // information. However, header_list_size_limit_exceeded() is recommended + // instead of headers.empty() to check whether header size exceeds limit. virtual void OnHeadersDecoded(QuicHeaderList headers) = 0; // Called when an error has occurred. - virtual void OnHeaderDecodingError() = 0; + virtual void OnHeaderDecodingError(QuicStringPiece error_message) = 0; }; QpackDecodedHeadersAccumulator(QuicStreamId id, @@ -60,42 +58,48 @@ class QUIC_EXPORT_PRIVATE QpackDecodedHeadersAccumulator void OnDecodingCompleted() override; void OnDecodingErrorDetected(QuicStringPiece error_message) override; - // Decode payload data. Returns true on success, false on error. + // Decode payload data. // Must not be called if an error has been detected. // Must not be called after EndHeaderBlock(). - bool Decode(QuicStringPiece data); + void Decode(QuicStringPiece data); // Signal end of HEADERS frame. // Must not be called if an error has been detected. // Must not be called more that once. - // Returns kSuccess if headers can be readily decoded. - // Returns kError if an error occurred. - // Returns kBlocked if headers cannot be decoded at the moment, in which case - // exactly one Visitor method will be called as soon as sufficient data - // is received on the QPACK decoder stream. - Status EndHeaderBlock(); - - // Returns accumulated header list. - const QuicHeaderList& quic_header_list() const; - - // Returns error message. - // Must not be called unless an error has been detected. - // TODO(b/124216424): Add accessor for error code, return HTTP_EXCESSIVE_LOAD - // or HTTP_QPACK_DECOMPRESSION_FAILED. - QuicStringPiece error_message() const; + void EndHeaderBlock(); + + // Returns true if the uncompressed size of the header list, including an + // overhead for each header field, exceeds |max_header_list_size| passed in + // the constructor. + bool header_list_size_limit_exceeded() const { + return header_list_size_limit_exceeded_; + } private: std::unique_ptr<QpackProgressiveDecoder> decoder_; Visitor* visitor_; + // Maximum header list size including overhead. + size_t max_header_list_size_; + // Uncompressed header list size including overhead, for enforcing the limit. + size_t uncompressed_header_bytes_including_overhead_; QuicHeaderList quic_header_list_; - size_t uncompressed_header_bytes_; + // Uncompressed header list size with overhead, + // for passing in to QuicHeaderList::OnHeaderBlockEnd(). + size_t uncompressed_header_bytes_without_overhead_; + // Compressed header list size + // for passing in to QuicHeaderList::OnHeaderBlockEnd(). size_t compressed_header_bytes_; - // Set to true when OnDecodingCompleted() is called. + + // True if the header size limit has been exceeded. + // Input data is still fed to QpackProgressiveDecoder. + bool header_list_size_limit_exceeded_; + + // The following two members are only used for DCHECKs. + + // True if headers have been completedly and successfully decoded. bool headers_decoded_; - // Set to true when EndHeaderBlock() returns kBlocked. - bool blocked_; + // True if an error has been detected during decoding. bool error_detected_; - std::string error_message_; }; } // 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 1d60660e9c4..6616517330f 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 @@ -7,16 +7,17 @@ #include <cstring> #include "net/third_party/quiche/src/quic/core/qpack/qpack_decoder.h" -#include "net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test_utils.h" -#include "net/third_party/quiche/src/quic/core/qpack/qpack_test_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/qpack_decoder_test_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/qpack/qpack_test_utils.h" +using ::testing::_; using ::testing::ElementsAre; using ::testing::Eq; using ::testing::Pair; +using ::testing::SaveArg; using ::testing::StrictMock; -using Status = quic::QpackDecodedHeadersAccumulator::Status; namespace quic { namespace test { @@ -37,15 +38,15 @@ const uint64_t kMaximumBlockedStreams = 1; // Header Acknowledgement decoder stream instruction with stream_id = 1. const char* const kHeaderAcknowledgement = "\x81"; -} // anonymous namespace - -class NoopVisitor : public QpackDecodedHeadersAccumulator::Visitor { +class MockVisitor : public QpackDecodedHeadersAccumulator::Visitor { public: - ~NoopVisitor() override = default; - void OnHeadersDecoded(QuicHeaderList /* headers */) override {} - void OnHeaderDecodingError() override {} + ~MockVisitor() override = default; + MOCK_METHOD1(OnHeadersDecoded, void(QuicHeaderList headers)); + MOCK_METHOD1(OnHeaderDecodingError, void(QuicStringPiece error_message)); }; +} // anonymous namespace + class QpackDecodedHeadersAccumulatorTest : public QuicTest { protected: QpackDecodedHeadersAccumulatorTest() @@ -63,91 +64,143 @@ class QpackDecodedHeadersAccumulatorTest : public QuicTest { NoopEncoderStreamErrorDelegate encoder_stream_error_delegate_; StrictMock<MockQpackStreamSenderDelegate> decoder_stream_sender_delegate_; QpackDecoder qpack_decoder_; - NoopVisitor visitor_; + StrictMock<MockVisitor> visitor_; QpackDecodedHeadersAccumulator accumulator_; }; // HEADERS frame payload must have a complete Header Block Prefix. TEST_F(QpackDecodedHeadersAccumulatorTest, EmptyPayload) { - EXPECT_EQ(Status::kError, accumulator_.EndHeaderBlock()); - EXPECT_EQ("Incomplete header data prefix.", accumulator_.error_message()); + EXPECT_CALL(visitor_, + OnHeaderDecodingError(Eq("Incomplete header data prefix."))); + accumulator_.EndHeaderBlock(); } // HEADERS frame payload must have a complete Header Block Prefix. TEST_F(QpackDecodedHeadersAccumulatorTest, TruncatedHeaderBlockPrefix) { - EXPECT_TRUE(accumulator_.Decode(QuicTextUtils::HexDecode("00"))); - EXPECT_EQ(Status::kError, accumulator_.EndHeaderBlock()); - EXPECT_EQ("Incomplete header data prefix.", accumulator_.error_message()); + accumulator_.Decode(QuicTextUtils::HexDecode("00")); + + EXPECT_CALL(visitor_, + OnHeaderDecodingError(Eq("Incomplete header data prefix."))); + accumulator_.EndHeaderBlock(); } TEST_F(QpackDecodedHeadersAccumulatorTest, EmptyHeaderList) { - EXPECT_TRUE(accumulator_.Decode(QuicTextUtils::HexDecode("0000"))); - EXPECT_EQ(Status::kSuccess, accumulator_.EndHeaderBlock()); + std::string encoded_data(QuicTextUtils::HexDecode("0000")); + accumulator_.Decode(encoded_data); - EXPECT_TRUE(accumulator_.quic_header_list().empty()); + QuicHeaderList header_list; + EXPECT_CALL(visitor_, OnHeadersDecoded(_)).WillOnce(SaveArg<0>(&header_list)); + accumulator_.EndHeaderBlock(); + EXPECT_FALSE(accumulator_.header_list_size_limit_exceeded()); + + EXPECT_EQ(0u, header_list.uncompressed_header_bytes()); + EXPECT_EQ(encoded_data.size(), header_list.compressed_header_bytes()); + EXPECT_TRUE(header_list.empty()); } // This payload is the prefix of a valid payload, but EndHeaderBlock() is called // before it can be completely decoded. TEST_F(QpackDecodedHeadersAccumulatorTest, TruncatedPayload) { - EXPECT_TRUE(accumulator_.Decode(QuicTextUtils::HexDecode("00002366"))); - EXPECT_EQ(Status::kError, accumulator_.EndHeaderBlock()); - EXPECT_EQ("Incomplete header block.", accumulator_.error_message()); + accumulator_.Decode(QuicTextUtils::HexDecode("00002366")); + + EXPECT_CALL(visitor_, OnHeaderDecodingError(Eq("Incomplete header block."))); + accumulator_.EndHeaderBlock(); } // This payload is invalid because it refers to a non-existing static entry. TEST_F(QpackDecodedHeadersAccumulatorTest, InvalidPayload) { - EXPECT_FALSE(accumulator_.Decode(QuicTextUtils::HexDecode("0000ff23ff24"))); - EXPECT_EQ("Static table entry not found.", accumulator_.error_message()); + EXPECT_CALL(visitor_, + OnHeaderDecodingError(Eq("Static table entry not found."))); + accumulator_.Decode(QuicTextUtils::HexDecode("0000ff23ff24")); } TEST_F(QpackDecodedHeadersAccumulatorTest, Success) { std::string encoded_data(QuicTextUtils::HexDecode("000023666f6f03626172")); - EXPECT_TRUE(accumulator_.Decode(encoded_data)); - EXPECT_EQ(Status::kSuccess, accumulator_.EndHeaderBlock()); + accumulator_.Decode(encoded_data); - const QuicHeaderList& header_list = accumulator_.quic_header_list(); - EXPECT_THAT(header_list, ElementsAre(Pair("foo", "bar"))); + QuicHeaderList header_list; + EXPECT_CALL(visitor_, OnHeadersDecoded(_)).WillOnce(SaveArg<0>(&header_list)); + accumulator_.EndHeaderBlock(); + EXPECT_FALSE(accumulator_.header_list_size_limit_exceeded()); + EXPECT_THAT(header_list, ElementsAre(Pair("foo", "bar"))); EXPECT_EQ(strlen("foo") + strlen("bar"), header_list.uncompressed_header_bytes()); EXPECT_EQ(encoded_data.size(), header_list.compressed_header_bytes()); } -TEST_F(QpackDecodedHeadersAccumulatorTest, ExceedingLimit) { +// Test that Decode() calls are not ignored after header list limit is exceeded, +// otherwise decoding could fail with "incomplete header block" error. +TEST_F(QpackDecodedHeadersAccumulatorTest, ExceedLimitThenSplitInstruction) { // Total length of header list exceeds kMaxHeaderListSize. - EXPECT_TRUE(accumulator_.Decode(QuicTextUtils::HexDecode( + accumulator_.Decode(QuicTextUtils::HexDecode( "0000" // header block prefix "26666f6f626172" // header key: "foobar" "7d61616161616161616161616161616161616161" // header value: 'a' 125 times "616161616161616161616161616161616161616161616161616161616161616161616161" "616161616161616161616161616161616161616161616161616161616161616161616161" - "61616161616161616161616161616161616161616161616161616161616161616161"))); - EXPECT_EQ(Status::kSuccess, accumulator_.EndHeaderBlock()); + "61616161616161616161616161616161616161616161616161616161616161616161" + "ff")); // first byte of a two-byte long Indexed Header Field instruction + accumulator_.Decode(QuicTextUtils::HexDecode( + "0f" // second byte of a two-byte long Indexed Header Field instruction + )); + + EXPECT_CALL(visitor_, OnHeadersDecoded(_)); + accumulator_.EndHeaderBlock(); + EXPECT_TRUE(accumulator_.header_list_size_limit_exceeded()); +} - // QuicHeaderList signals header list over limit by clearing it. - EXPECT_TRUE(accumulator_.quic_header_list().empty()); +// Test that header list limit enforcement works with blocked encoding. +TEST_F(QpackDecodedHeadersAccumulatorTest, ExceedLimitBlocked) { + // Total length of header list exceeds kMaxHeaderListSize. + accumulator_.Decode(QuicTextUtils::HexDecode( + "0200" // header block prefix + "80" // reference to dynamic table entry not yet received + "26666f6f626172" // header key: "foobar" + "7d61616161616161616161616161616161616161" // header value: 'a' 125 times + "616161616161616161616161616161616161616161616161616161616161616161616161" + "616161616161616161616161616161616161616161616161616161616161616161616161" + "61616161616161616161616161616161616161616161616161616161616161616161")); + 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))); + + EXPECT_CALL(visitor_, OnHeadersDecoded(_)); + qpack_decoder_.OnInsertWithoutNameReference("foo", "bar"); + EXPECT_TRUE(accumulator_.header_list_size_limit_exceeded()); } TEST_F(QpackDecodedHeadersAccumulatorTest, BlockedDecoding) { // Reference to dynamic table entry not yet received. - EXPECT_TRUE(accumulator_.Decode(QuicTextUtils::HexDecode("020080"))); - EXPECT_EQ(Status::kBlocked, accumulator_.EndHeaderBlock()); + std::string encoded_data(QuicTextUtils::HexDecode("020080")); + accumulator_.Decode(encoded_data); + 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))); + + QuicHeaderList header_list; + EXPECT_CALL(visitor_, OnHeadersDecoded(_)).WillOnce(SaveArg<0>(&header_list)); qpack_decoder_.OnInsertWithoutNameReference("foo", "bar"); - EXPECT_THAT(accumulator_.quic_header_list(), ElementsAre(Pair("foo", "bar"))); + EXPECT_FALSE(accumulator_.header_list_size_limit_exceeded()); + EXPECT_THAT(header_list, ElementsAre(Pair("foo", "bar"))); + EXPECT_EQ(strlen("foo") + strlen("bar"), + header_list.uncompressed_header_bytes()); + EXPECT_EQ(encoded_data.size(), header_list.compressed_header_bytes()); } TEST_F(QpackDecodedHeadersAccumulatorTest, BlockedDecodingUnblockedBeforeEndOfHeaderBlock) { // Reference to dynamic table entry not yet received. - EXPECT_TRUE(accumulator_.Decode(QuicTextUtils::HexDecode("020080"))); + accumulator_.Decode(QuicTextUtils::HexDecode("020080")); // Set dynamic table capacity. qpack_decoder_.OnSetDynamicTableCapacity(kMaxDynamicTableCapacity); @@ -157,11 +210,34 @@ TEST_F(QpackDecodedHeadersAccumulatorTest, // Rest of header block: same entry again. EXPECT_CALL(decoder_stream_sender_delegate_, WriteStreamData(Eq(kHeaderAcknowledgement))); - EXPECT_TRUE(accumulator_.Decode(QuicTextUtils::HexDecode("80"))); - EXPECT_EQ(Status::kSuccess, accumulator_.EndHeaderBlock()); + accumulator_.Decode(QuicTextUtils::HexDecode("80")); - EXPECT_THAT(accumulator_.quic_header_list(), - ElementsAre(Pair("foo", "bar"), Pair("foo", "bar"))); + QuicHeaderList header_list; + EXPECT_CALL(visitor_, OnHeadersDecoded(_)).WillOnce(SaveArg<0>(&header_list)); + accumulator_.EndHeaderBlock(); + EXPECT_FALSE(accumulator_.header_list_size_limit_exceeded()); + + EXPECT_THAT(header_list, ElementsAre(Pair("foo", "bar"), Pair("foo", "bar"))); +} + +// Regression test for https://crbug.com/1024263. +TEST_F(QpackDecodedHeadersAccumulatorTest, + BlockedDecodingUnblockedAndErrorBeforeEndOfHeaderBlock) { + // Required Insert Count higher than number of entries causes decoding to be + // blocked. + accumulator_.Decode(QuicTextUtils::HexDecode("0200")); + // Indexed Header Field instruction addressing dynamic table entry with + // relative index 0, absolute index 0. + accumulator_.Decode(QuicTextUtils::HexDecode("80")); + // Relative index larger than or equal to Base is invalid. + accumulator_.Decode(QuicTextUtils::HexDecode("81")); + + // Set dynamic table capacity. + qpack_decoder_.OnSetDynamicTableCapacity(kMaxDynamicTableCapacity); + + // Adding dynamic table entry unblocks decoding. Error is detected. + EXPECT_CALL(visitor_, OnHeaderDecodingError(Eq("Invalid relative index."))); + qpack_decoder_.OnInsertWithoutNameReference("foo", "bar"); } } // namespace test 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 4f39e3b39b2..3ae6bce6e6b 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 @@ -27,10 +27,10 @@ QpackDecoder::QpackDecoder( 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); - decoder_stream_sender_.Flush(); + if (header_table_.maximum_dynamic_table_capacity() > 0) { + decoder_stream_sender_.SendStreamCancellation(stream_id); + decoder_stream_sender_.Flush(); + } } bool QpackDecoder::OnStreamBlocked(QuicStreamId stream_id) { 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 113c6ea8e4a..4ac1e449bc6 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 @@ -95,6 +95,11 @@ class QUIC_EXPORT_PRIVATE QpackDecoder return &encoder_stream_receiver_; } + // True if any dynamic table entries have been referenced from a header block. + bool dynamic_table_entry_referenced() const { + return header_table_.dynamic_table_entry_referenced(); + } + private: EncoderStreamErrorDelegate* const encoder_stream_error_delegate_; QpackEncoderStreamReceiver encoder_stream_receiver_; diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_receiver.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_receiver.cc index 559ce433376..2ba89d48a82 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_receiver.cc +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_receiver.cc @@ -6,7 +6,7 @@ #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/quic/core/qpack/qpack_constants.h" +#include "net/third_party/quiche/src/quic/core/qpack/qpack_instructions.h" namespace quic { diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_receiver.h b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_receiver.h index 60719399d8b..396c6df8779 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_receiver.h +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_receiver.h @@ -23,7 +23,7 @@ class QUIC_EXPORT_PRIVATE QpackDecoderStreamReceiver public: // An interface for handling instructions decoded from the decoder stream, see // https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#rfc.section.5.3 - class Delegate { + class QUIC_EXPORT_PRIVATE Delegate { public: virtual ~Delegate() = default; 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 68e4d67816c..72a446b420a 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 @@ -8,7 +8,7 @@ #include <limits> #include <string> -#include "net/third_party/quiche/src/quic/core/qpack/qpack_constants.h" +#include "net/third_party/quiche/src/quic/core/qpack/qpack_instructions.h" #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" namespace quic { @@ -16,25 +16,19 @@ namespace quic { QpackDecoderStreamSender::QpackDecoderStreamSender() : delegate_(nullptr) {} void QpackDecoderStreamSender::SendInsertCountIncrement(uint64_t increment) { - values_.varint = increment; - - instruction_encoder_.Encode(InsertCountIncrementInstruction(), values_, - &buffer_); + instruction_encoder_.Encode( + QpackInstructionWithValues::InsertCountIncrement(increment), &buffer_); } void QpackDecoderStreamSender::SendHeaderAcknowledgement( QuicStreamId stream_id) { - values_.varint = stream_id; - - instruction_encoder_.Encode(HeaderAcknowledgementInstruction(), values_, - &buffer_); + instruction_encoder_.Encode( + QpackInstructionWithValues::HeaderAcknowledgement(stream_id), &buffer_); } void QpackDecoderStreamSender::SendStreamCancellation(QuicStreamId stream_id) { - values_.varint = stream_id; - - instruction_encoder_.Encode(StreamCancellationInstruction(), values_, - &buffer_); + instruction_encoder_.Encode( + QpackInstructionWithValues::StreamCancellation(stream_id), &buffer_); } void QpackDecoderStreamSender::Flush() { 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 93d95d91476..d9033b04dee 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 @@ -44,7 +44,6 @@ class QUIC_EXPORT_PRIVATE QpackDecoderStreamSender { private: QpackStreamSenderDelegate* delegate_; QpackInstructionEncoder instruction_encoder_; - QpackInstructionEncoder::Values values_; std::string buffer_; }; 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 6e042590833..e3dc12497e3 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 @@ -4,9 +4,9 @@ #include "net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_sender.h" -#include "net/third_party/quiche/src/quic/core/qpack/qpack_test_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/qpack_test_utils.h" using ::testing::Eq; using ::testing::StrictMock; 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 d0ff30ff679..1fc5802d83c 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 @@ -6,14 +6,16 @@ #include <algorithm> -#include "net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test_utils.h" -#include "net/third_party/quiche/src/quic/core/qpack/qpack_test_utils.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" #include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/qpack/qpack_decoder_test_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/qpack/qpack_test_utils.h" #include "net/third_party/quiche/src/spdy/core/spdy_header_block.h" +using ::testing::_; using ::testing::Eq; +using ::testing::Invoke; using ::testing::Mock; using ::testing::Sequence; using ::testing::StrictMock; @@ -42,6 +44,15 @@ class QpackDecoderTest : public QuicTestWithParam<FragmentMode> { ~QpackDecoderTest() override = default; + void SetUp() override { + // Destroy QpackProgressiveDecoder on error to test that it does not crash. + // See https://crbug.com/1025209. + ON_CALL(handler_, OnDecodingErrorDetected(_)) + .WillByDefault(Invoke([this](QuicStringPiece /* error_message */) { + progressive_decoder_.reset(); + })); + } + void DecodeEncoderStreamData(QuicStringPiece data) { qpack_decoder_.encoder_stream_receiver()->Decode(data); } @@ -61,7 +72,7 @@ class QpackDecoderTest : public QuicTestWithParam<FragmentMode> { void DecodeData(QuicStringPiece data) { auto fragment_size_generator = FragmentModeToFragmentSizeGenerator(fragment_mode_); - while (!data.empty()) { + while (progressive_decoder_ && !data.empty()) { size_t fragment_size = std::min(fragment_size_generator(), data.size()); progressive_decoder_->Decode(data.substr(0, fragment_size)); data = data.substr(fragment_size); @@ -70,9 +81,11 @@ class QpackDecoderTest : public QuicTestWithParam<FragmentMode> { // Signal end of header block to QpackProgressiveDecoder. void EndDecoding() { - progressive_decoder_->EndHeaderBlock(); - // |progressive_decoder_| is kept alive so that it can - // handle callbacks later in case of blocked decoding. + if (progressive_decoder_) { + progressive_decoder_->EndHeaderBlock(); + } + // If no error was detected, |*progressive_decoder_| is kept alive so that + // it can handle callbacks later in case of blocked decoding. } // Decode an entire header block. @@ -105,6 +118,18 @@ TEST_P(QpackDecoderTest, NoPrefix) { DecodeHeaderBlock(QuicTextUtils::HexDecode("00")); } +// Regression test for https://1025209: QpackProgressiveDecoder must not crash +// in Decode() if it is destroyed by handler_.OnDecodingErrorDetected(). +TEST_P(QpackDecoderTest, InvalidPrefix) { + StartDecoding(); + + EXPECT_CALL(handler_, + OnDecodingErrorDetected(Eq("Encoded integer too large."))); + + // Encoded Required Insert Count in Header Data Prefix is too large. + DecodeData(QuicTextUtils::HexDecode("ffffffffffffffffffffffffffff")); +} + TEST_P(QpackDecoderTest, EmptyHeaderBlock) { EXPECT_CALL(handler_, OnDecodingCompleted()); @@ -780,16 +805,15 @@ TEST_P(QpackDecoderTest, BlockedDecodingUnblockedBeforeEndOfHeaderBlock) { // entry with relative index 0, absolute index 0. "d1")); // Static table entry with index 17. + // Set dynamic table capacity to 1024. + DecodeEncoderStreamData(QuicTextUtils::HexDecode("3fe107")); + // Add literal entry with name "foo" and value "bar". Decoding is now // unblocked because dynamic table Insert Count reached the Required Insert // Count of the header block. |handler_| methods are called immediately for // 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_); @@ -809,6 +833,29 @@ TEST_P(QpackDecoderTest, BlockedDecodingUnblockedBeforeEndOfHeaderBlock) { EndDecoding(); } +// Regression test for https://crbug.com/1024263. +TEST_P(QpackDecoderTest, + BlockedDecodingUnblockedAndErrorBeforeEndOfHeaderBlock) { + StartDecoding(); + DecodeData(QuicTextUtils::HexDecode( + "0200" // Required Insert Count 1 and Delta Base 0. + // Base is 1 + 0 = 1. + "80" // Indexed Header Field instruction addressing dynamic table + // entry with relative index 0, absolute index 0. + "81")); // Relative index 1 is equal to Base, therefore invalid. + + // Set dynamic table capacity to 1024. + DecodeEncoderStreamData(QuicTextUtils::HexDecode("3fe107")); + + // Add literal entry with name "foo" and value "bar". Decoding is now + // unblocked because dynamic table Insert Count reached the Required Insert + // Count of the header block. |handler_| methods are called immediately for + // the already consumed part of the header block. + EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("bar"))); + EXPECT_CALL(handler_, OnDecodingErrorDetected(Eq("Invalid relative index."))); + DecodeEncoderStreamData(QuicTextUtils::HexDecode("6294e703626172")); +} + // Make sure that Required Insert Count is compared to Insert Count, // not size of dynamic table. TEST_P(QpackDecoderTest, BlockedDecodingAndEvictedEntries) { 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 deleted file mode 100644 index eaf66648cb0..00000000000 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test_utils.cc +++ /dev/null @@ -1,88 +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/qpack/qpack_decoder_test_utils.h" - -#include <algorithm> -#include <cstddef> -#include <utility> - -#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" - -namespace quic { -namespace test { - -void NoopEncoderStreamErrorDelegate::OnEncoderStreamError( - QuicStringPiece /*error_message*/) {} - -TestHeadersHandler::TestHeadersHandler() - : decoding_completed_(false), decoding_error_detected_(false) {} - -void TestHeadersHandler::OnHeaderDecoded(QuicStringPiece name, - QuicStringPiece value) { - ASSERT_FALSE(decoding_completed_); - ASSERT_FALSE(decoding_error_detected_); - - header_list_.AppendValueOrAddHeader(name, value); -} - -void TestHeadersHandler::OnDecodingCompleted() { - ASSERT_FALSE(decoding_completed_); - ASSERT_FALSE(decoding_error_detected_); - - decoding_completed_ = true; -} - -void TestHeadersHandler::OnDecodingErrorDetected( - QuicStringPiece error_message) { - ASSERT_FALSE(decoding_completed_); - ASSERT_FALSE(decoding_error_detected_); - - decoding_error_detected_ = true; - error_message_.assign(error_message.data(), error_message.size()); -} - -spdy::SpdyHeaderBlock TestHeadersHandler::ReleaseHeaderList() { - DCHECK(decoding_completed_); - DCHECK(!decoding_error_detected_); - - return std::move(header_list_); -} - -bool TestHeadersHandler::decoding_completed() const { - return decoding_completed_; -} - -bool TestHeadersHandler::decoding_error_detected() const { - return decoding_error_detected_; -} - -const std::string& TestHeadersHandler::error_message() const { - DCHECK(decoding_error_detected_); - return error_message_; -} - -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(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()) { - size_t fragment_size = std::min(fragment_size_generator(), data.size()); - progressive_decoder->Decode(data.substr(0, fragment_size)); - data = data.substr(fragment_size); - } - progressive_decoder->EndHeaderBlock(); -} - -} // namespace test -} // namespace quic 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 deleted file mode 100644 index 6505b60a1fe..00000000000 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test_utils.h +++ /dev/null @@ -1,103 +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_QPACK_QPACK_DECODER_TEST_UTILS_H_ -#define QUICHE_QUIC_CORE_QPACK_QPACK_DECODER_TEST_UTILS_H_ - -#include <string> - -#include "net/third_party/quiche/src/quic/core/qpack/qpack_decoder.h" -#include "net/third_party/quiche/src/quic/core/qpack/qpack_progressive_decoder.h" -#include "net/third_party/quiche/src/quic/core/qpack/qpack_test_utils.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/spdy/core/spdy_header_block.h" - -namespace quic { -namespace test { - -// QpackDecoder::EncoderStreamErrorDelegate implementation that does nothing. -class NoopEncoderStreamErrorDelegate - : public QpackDecoder::EncoderStreamErrorDelegate { - public: - ~NoopEncoderStreamErrorDelegate() override = default; - - void OnEncoderStreamError(QuicStringPiece error_message) override; -}; - -// Mock QpackDecoder::EncoderStreamErrorDelegate implementation. -class MockEncoderStreamErrorDelegate - : public QpackDecoder::EncoderStreamErrorDelegate { - public: - ~MockEncoderStreamErrorDelegate() override = default; - - MOCK_METHOD1(OnEncoderStreamError, void(QuicStringPiece error_message)); -}; - -// HeadersHandlerInterface implementation that collects decoded headers -// into a SpdyHeaderBlock. -class TestHeadersHandler - : public QpackProgressiveDecoder::HeadersHandlerInterface { - public: - TestHeadersHandler(); - ~TestHeadersHandler() override = default; - - // HeadersHandlerInterface implementation: - void OnHeaderDecoded(QuicStringPiece name, QuicStringPiece value) override; - void OnDecodingCompleted() override; - void OnDecodingErrorDetected(QuicStringPiece error_message) override; - - // Release decoded header list. Must only be called if decoding is complete - // and no errors have been detected. - spdy::SpdyHeaderBlock ReleaseHeaderList(); - - bool decoding_completed() const; - bool decoding_error_detected() const; - const std::string& error_message() const; - - private: - spdy::SpdyHeaderBlock header_list_; - bool decoding_completed_; - bool decoding_error_detected_; - std::string error_message_; -}; - -class MockHeadersHandler - : public QpackProgressiveDecoder::HeadersHandlerInterface { - public: - MockHeadersHandler() = default; - MockHeadersHandler(const MockHeadersHandler&) = delete; - MockHeadersHandler& operator=(const MockHeadersHandler&) = delete; - ~MockHeadersHandler() override = default; - - MOCK_METHOD2(OnHeaderDecoded, - void(QuicStringPiece name, QuicStringPiece value)); - MOCK_METHOD0(OnDecodingCompleted, void()); - MOCK_METHOD1(OnDecodingErrorDetected, void(QuicStringPiece error_message)); -}; - -class NoOpHeadersHandler - : public QpackProgressiveDecoder::HeadersHandlerInterface { - public: - ~NoOpHeadersHandler() override = default; - - void OnHeaderDecoded(QuicStringPiece /*name*/, - QuicStringPiece /*value*/) override {} - void OnDecodingCompleted() override {} - void OnDecodingErrorDetected(QuicStringPiece /*error_message*/) override {} -}; - -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); - -} // namespace test -} // namespace quic - -#endif // QUICHE_QUIC_CORE_QPACK_QPACK_DECODER_TEST_UTILS_H_ 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 319af82115a..59e172ec75a 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 @@ -7,7 +7,6 @@ #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" @@ -42,47 +41,37 @@ QpackEncoder::QpackEncoder( QpackEncoder::~QpackEncoder() {} // static -QpackEncoder::InstructionWithValues QpackEncoder::EncodeIndexedHeaderField( +QpackInstructionWithValues 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; + return QpackInstructionWithValues::IndexedHeaderField(is_static, index); } // static -QpackEncoder::InstructionWithValues +QpackInstructionWithValues 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; + return QpackInstructionWithValues::LiteralHeaderFieldNameReference( + is_static, index, value); } // static -QpackEncoder::InstructionWithValues QpackEncoder::EncodeLiteralHeaderField( +QpackInstructionWithValues QpackEncoder::EncodeLiteralHeaderField( QuicStringPiece name, QuicStringPiece value) { - InstructionWithValues instruction{QpackLiteralHeaderFieldInstruction(), {}}; - instruction.values.name = name; - instruction.values.value = value; - return instruction; + return QpackInstructionWithValues::LiteralHeaderField(name, value); } QpackEncoder::Instructions QpackEncoder::FirstPassEncode( @@ -142,6 +131,7 @@ QpackEncoder::Instructions QpackEncoder::FirstPassEncode( instructions.push_back( EncodeIndexedHeaderField(is_static, index, referred_indices)); smallest_blocking_index = std::min(smallest_blocking_index, index); + header_table_.set_dynamic_table_entry_referenced(); break; } @@ -159,11 +149,10 @@ QpackEncoder::Instructions QpackEncoder::FirstPassEncode( 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); + header_table_.set_dynamic_table_entry_referenced(); break; } @@ -218,11 +207,10 @@ QpackEncoder::Instructions QpackEncoder::FirstPassEncode( 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); + header_table_.set_dynamic_table_entry_referenced(); break; } @@ -233,6 +221,7 @@ QpackEncoder::Instructions QpackEncoder::FirstPassEncode( instructions.push_back(EncodeLiteralHeaderFieldWithNameReference( is_static, index, value, referred_indices)); smallest_blocking_index = std::min(smallest_blocking_index, index); + header_table_.set_dynamic_table_entry_referenced(); break; } @@ -329,29 +318,24 @@ std::string QpackEncoder::SecondPassEncode( std::string encoded_headers; // Header block prefix. - QpackInstructionEncoder::Values values; - 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( + QpackInstructionWithValues::Prefix(QpackEncodeRequiredInsertCount( + required_insert_count, header_table_.max_entries())), + &encoded_headers); - instruction_encoder.Encode(QpackPrefixInstruction(), values, - &encoded_headers); + const uint64_t base = required_insert_count; for (auto& instruction : instructions) { // Dynamic table references must be transformed from absolute to relative // indices. - if ((instruction.instruction == QpackIndexedHeaderFieldInstruction() || - instruction.instruction == + if ((instruction.instruction() == QpackIndexedHeaderFieldInstruction() || + instruction.instruction() == QpackLiteralHeaderFieldNameReferenceInstruction()) && - !instruction.values.s_bit) { - instruction.values.varint = - QpackAbsoluteIndexToRequestStreamRelativeIndex( - instruction.values.varint, base); + !instruction.s_bit()) { + instruction.set_varint(QpackAbsoluteIndexToRequestStreamRelativeIndex( + instruction.varint(), base)); } - instruction_encoder.Encode(instruction.instruction, instruction.values, - &encoded_headers); + instruction_encoder.Encode(instruction, &encoded_headers); } return encoded_headers; 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 1ed56a82e11..e635e3bf04b 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 @@ -14,6 +14,7 @@ #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" +#include "net/third_party/quiche/src/quic/core/qpack/qpack_instructions.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_exported_stats.h" @@ -87,39 +88,35 @@ class QUIC_EXPORT_PRIVATE QpackEncoder return &decoder_stream_receiver_; } + // True if any dynamic table entries have been referenced from a header block. + bool dynamic_table_entry_referenced() const { + return header_table_.dynamic_table_entry_referenced(); + } + 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 - // InstructionWithValues struct instead of separate |instruction| and |values| - // arguments. - struct InstructionWithValues { - // |instruction| is not owned. - const QpackInstruction* instruction; - QpackInstructionEncoder::Values values; - }; - using Instructions = std::vector<InstructionWithValues>; + using Instructions = std::vector<QpackInstructionWithValues>; // Generate indexed header field instruction // and optionally update |*referred_indices|. - static InstructionWithValues EncodeIndexedHeaderField( + static QpackInstructionWithValues 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( + static QpackInstructionWithValues 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); + static QpackInstructionWithValues 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 diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_receiver.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_receiver.cc index 3f8ef08a7ee..c46cc3c0f41 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_receiver.cc +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_receiver.cc @@ -6,7 +6,7 @@ #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/quic/core/qpack/qpack_constants.h" +#include "net/third_party/quiche/src/quic/core/qpack/qpack_instructions.h" namespace quic { diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_receiver.h b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_receiver.h index 8da3147d1ea..b393b546b60 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_receiver.h +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_receiver.h @@ -22,7 +22,7 @@ class QUIC_EXPORT_PRIVATE QpackEncoderStreamReceiver public: // An interface for handling instructions decoded from the encoder stream, see // https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#rfc.section.5.2 - class Delegate { + class QUIC_EXPORT_PRIVATE Delegate { public: virtual ~Delegate() = default; 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 4a7f12cd3a3..5182864ce6d 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 @@ -8,7 +8,7 @@ #include <limits> #include <string> -#include "net/third_party/quiche/src/quic/core/qpack/qpack_constants.h" +#include "net/third_party/quiche/src/quic/core/qpack/qpack_instructions.h" #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" namespace quic { @@ -19,35 +19,28 @@ void QpackEncoderStreamSender::SendInsertWithNameReference( bool is_static, uint64_t name_index, QuicStringPiece value) { - values_.s_bit = is_static; - values_.varint = name_index; - values_.value = value; - - instruction_encoder_.Encode(InsertWithNameReferenceInstruction(), values_, - &buffer_); + instruction_encoder_.Encode( + QpackInstructionWithValues::InsertWithNameReference(is_static, name_index, + value), + &buffer_); } void QpackEncoderStreamSender::SendInsertWithoutNameReference( QuicStringPiece name, QuicStringPiece value) { - values_.name = name; - values_.value = value; - - instruction_encoder_.Encode(InsertWithoutNameReferenceInstruction(), values_, - &buffer_); + instruction_encoder_.Encode( + QpackInstructionWithValues::InsertWithoutNameReference(name, value), + &buffer_); } void QpackEncoderStreamSender::SendDuplicate(uint64_t index) { - values_.varint = index; - - instruction_encoder_.Encode(DuplicateInstruction(), values_, &buffer_); + instruction_encoder_.Encode(QpackInstructionWithValues::Duplicate(index), + &buffer_); } void QpackEncoderStreamSender::SendSetDynamicTableCapacity(uint64_t capacity) { - values_.varint = capacity; - - instruction_encoder_.Encode(SetDynamicTableCapacityInstruction(), values_, - &buffer_); + instruction_encoder_.Encode( + QpackInstructionWithValues::SetDynamicTableCapacity(capacity), &buffer_); } QuicByteCount QpackEncoderStreamSender::Flush() { 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 efbfbc632d5..de9e8f14839 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 @@ -50,7 +50,6 @@ class QUIC_EXPORT_PRIVATE QpackEncoderStreamSender { private: QpackStreamSenderDelegate* delegate_; QpackInstructionEncoder instruction_encoder_; - QpackInstructionEncoder::Values values_; std::string buffer_; }; 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 80a6ed3c06f..0a42df220cb 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 @@ -4,9 +4,9 @@ #include "net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_sender.h" -#include "net/third_party/quiche/src/quic/core/qpack/qpack_test_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/qpack_test_utils.h" using ::testing::Eq; using ::testing::StrictMock; 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 6f2efe3aecc..6b92e4bfc95 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,13 +7,12 @@ #include <limits> #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" +#include "net/third_party/quiche/src/quic/test_tools/qpack/qpack_encoder_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/qpack/qpack_encoder_test_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/qpack/qpack_header_table_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/qpack/qpack_test_utils.h" using ::testing::_; using ::testing::Eq; diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_test_utils.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_test_utils.cc deleted file mode 100644 index d91d3d13d5e..00000000000 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_test_utils.cc +++ /dev/null @@ -1,16 +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/qpack/qpack_encoder_test_utils.h" - -#include "net/third_party/quiche/src/spdy/core/hpack/hpack_encoder.h" - -namespace quic { -namespace test { - -void NoopDecoderStreamErrorDelegate::OnDecoderStreamError( - QuicStringPiece /*error_message*/) {} - -} // namespace test -} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_test_utils.h b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_test_utils.h deleted file mode 100644 index b1103dae6c3..00000000000 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_test_utils.h +++ /dev/null @@ -1,40 +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_QPACK_QPACK_ENCODER_TEST_UTILS_H_ -#define QUICHE_QUIC_CORE_QPACK_QPACK_ENCODER_TEST_UTILS_H_ - -#include <string> - -#include "net/third_party/quiche/src/quic/core/qpack/qpack_encoder.h" -#include "net/third_party/quiche/src/quic/core/qpack/qpack_test_utils.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/spdy/core/spdy_header_block.h" - -namespace quic { -namespace test { - -// QpackEncoder::DecoderStreamErrorDelegate implementation that does nothing. -class NoopDecoderStreamErrorDelegate - : public QpackEncoder::DecoderStreamErrorDelegate { - public: - ~NoopDecoderStreamErrorDelegate() override = default; - - void OnDecoderStreamError(QuicStringPiece error_message) override; -}; - -// Mock QpackEncoder::DecoderStreamErrorDelegate implementation. -class MockDecoderStreamErrorDelegate - : public QpackEncoder::DecoderStreamErrorDelegate { - public: - ~MockDecoderStreamErrorDelegate() override = default; - - MOCK_METHOD1(OnDecoderStreamError, void(QuicStringPiece error_message)); -}; - -} // namespace test -} // namespace quic - -#endif // QUICHE_QUIC_CORE_QPACK_QPACK_ENCODER_TEST_UTILS_H_ 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 ff9be9a79fd..4cafa196815 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 @@ -17,7 +17,8 @@ QpackHeaderTable::QpackHeaderTable() dynamic_table_capacity_(0), maximum_dynamic_table_capacity_(0), max_entries_(0), - dropped_entry_count_(0) {} + dropped_entry_count_(0), + dynamic_table_entry_referenced_(false) {} QpackHeaderTable::~QpackHeaderTable() { for (auto& entry : observers_) { 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 78d85f6ae29..dd5ca3ba3e5 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 @@ -40,7 +40,7 @@ class QUIC_EXPORT_PRIVATE QpackHeaderTable { enum class MatchType { kNameAndValue, kName, kNoMatch }; // Observer interface for dynamic table insertion. - class Observer { + class QUIC_EXPORT_PRIVATE Observer { public: virtual ~Observer() = default; @@ -98,6 +98,11 @@ class QUIC_EXPORT_PRIVATE QpackHeaderTable { // This method must only be called at most once. void SetMaximumDynamicTableCapacity(uint64_t maximum_dynamic_table_capacity); + // Get |maximum_dynamic_table_capacity_|. + uint64_t maximum_dynamic_table_capacity() const { + return maximum_dynamic_table_capacity_; + } + // Register an observer to be notified when inserted_entry_count() reaches // |required_insert_count|. After the notification, |observer| automatically // gets unregistered. Each observer must only be registered at most once. @@ -130,6 +135,13 @@ class QUIC_EXPORT_PRIVATE QpackHeaderTable { // The returned index might not be the index of a valid entry. uint64_t draining_index(float draining_fraction) const; + void set_dynamic_table_entry_referenced() { + dynamic_table_entry_referenced_ = true; + } + bool dynamic_table_entry_referenced() const { + return dynamic_table_entry_referenced_; + } + private: friend class test::QpackHeaderTablePeer; @@ -192,6 +204,10 @@ class QUIC_EXPORT_PRIVATE QpackHeaderTable { // Observers waiting to be notified, sorted by required insert count. std::multimap<uint64_t, Observer*> observers_; + + // True if any dynamic table entries have been referenced from a header block. + // Set directly by the encoder or decoder. Used for stats. + bool dynamic_table_entry_referenced_; }; } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_decoder.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_decoder.cc index 187894d374e..fede8e307df 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_decoder.cc +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_decoder.cc @@ -32,44 +32,48 @@ QpackInstructionDecoder::QpackInstructionDecoder(const QpackLanguage* language, error_detected_(false), state_(State::kStartInstruction) {} -void QpackInstructionDecoder::Decode(QuicStringPiece data) { +bool QpackInstructionDecoder::Decode(QuicStringPiece data) { DCHECK(!data.empty()); DCHECK(!error_detected_); while (true) { + bool success = true; size_t bytes_consumed = 0; switch (state_) { case State::kStartInstruction: - DoStartInstruction(data); + success = DoStartInstruction(data); break; case State::kStartField: - DoStartField(); + success = DoStartField(); break; case State::kReadBit: - DoReadBit(data); + success = DoReadBit(data); break; case State::kVarintStart: - bytes_consumed = DoVarintStart(data); + success = DoVarintStart(data, &bytes_consumed); break; case State::kVarintResume: - bytes_consumed = DoVarintResume(data); + success = DoVarintResume(data, &bytes_consumed); break; case State::kVarintDone: - DoVarintDone(); + success = DoVarintDone(); break; case State::kReadString: - bytes_consumed = DoReadString(data); + success = DoReadString(data, &bytes_consumed); break; case State::kReadStringDone: - DoReadStringDone(); + success = DoReadStringDone(); break; } - if (error_detected_) { - return; + if (!success) { + return false; } + // |success| must be false if an error is detected. + DCHECK(!error_detected_); + DCHECK_LE(bytes_consumed, data.size()); data = QuicStringPiece(data.data() + bytes_consumed, @@ -78,35 +82,37 @@ void QpackInstructionDecoder::Decode(QuicStringPiece data) { // Stop processing if no more data but next state would require it. if (data.empty() && (state_ != State::kStartField) && (state_ != State::kVarintDone) && (state_ != State::kReadStringDone)) { - return; + return true; } } + + return true; } bool QpackInstructionDecoder::AtInstructionBoundary() const { return state_ == State::kStartInstruction; } -void QpackInstructionDecoder::DoStartInstruction(QuicStringPiece data) { +bool QpackInstructionDecoder::DoStartInstruction(QuicStringPiece data) { DCHECK(!data.empty()); instruction_ = LookupOpcode(data[0]); field_ = instruction_->fields.begin(); state_ = State::kStartField; + return true; } -void QpackInstructionDecoder::DoStartField() { +bool QpackInstructionDecoder::DoStartField() { if (field_ == instruction_->fields.end()) { // Completed decoding this instruction. if (!delegate_->OnInstructionDecoded(instruction_)) { - error_detected_ = true; - return; + return false; } state_ = State::kStartInstruction; - return; + return true; } switch (field_->type) { @@ -114,15 +120,18 @@ void QpackInstructionDecoder::DoStartField() { case QpackInstructionFieldType::kName: case QpackInstructionFieldType::kValue: state_ = State::kReadBit; - return; + return true; case QpackInstructionFieldType::kVarint: case QpackInstructionFieldType::kVarint2: state_ = State::kVarintStart; - return; + return true; + default: + QUIC_BUG << "Invalid field type."; + return false; } } -void QpackInstructionDecoder::DoReadBit(QuicStringPiece data) { +bool QpackInstructionDecoder::DoReadBit(QuicStringPiece data) { DCHECK(!data.empty()); switch (field_->type) { @@ -133,7 +142,7 @@ void QpackInstructionDecoder::DoReadBit(QuicStringPiece data) { ++field_; state_ = State::kStartField; - return; + return true; } case QpackInstructionFieldType::kName: case QpackInstructionFieldType::kValue: { @@ -144,14 +153,16 @@ void QpackInstructionDecoder::DoReadBit(QuicStringPiece data) { state_ = State::kVarintStart; - return; + return true; } default: - DCHECK(false); + QUIC_BUG << "Invalid field type."; + return false; } } -size_t QpackInstructionDecoder::DoVarintStart(QuicStringPiece data) { +bool QpackInstructionDecoder::DoVarintStart(QuicStringPiece data, + size_t* bytes_consumed) { DCHECK(!data.empty()); DCHECK(field_->type == QpackInstructionFieldType::kVarint || field_->type == QpackInstructionFieldType::kVarint2 || @@ -162,24 +173,25 @@ size_t QpackInstructionDecoder::DoVarintStart(QuicStringPiece data) { http2::DecodeStatus status = varint_decoder_.Start(data[0], field_->param, &buffer); - size_t bytes_consumed = 1 + buffer.Offset(); + *bytes_consumed = 1 + buffer.Offset(); switch (status) { case http2::DecodeStatus::kDecodeDone: state_ = State::kVarintDone; - return bytes_consumed; + return true; case http2::DecodeStatus::kDecodeInProgress: state_ = State::kVarintResume; - return bytes_consumed; + return true; case http2::DecodeStatus::kDecodeError: OnError("Encoded integer too large."); - return bytes_consumed; + return false; default: QUIC_BUG << "Unknown decode status " << status; - return bytes_consumed; + return false; } } -size_t QpackInstructionDecoder::DoVarintResume(QuicStringPiece data) { +bool QpackInstructionDecoder::DoVarintResume(QuicStringPiece data, + size_t* bytes_consumed) { DCHECK(!data.empty()); DCHECK(field_->type == QpackInstructionFieldType::kVarint || field_->type == QpackInstructionFieldType::kVarint2 || @@ -189,25 +201,25 @@ size_t QpackInstructionDecoder::DoVarintResume(QuicStringPiece data) { http2::DecodeBuffer buffer(data); http2::DecodeStatus status = varint_decoder_.Resume(&buffer); - size_t bytes_consumed = buffer.Offset(); + *bytes_consumed = buffer.Offset(); switch (status) { case http2::DecodeStatus::kDecodeDone: state_ = State::kVarintDone; - return bytes_consumed; + return true; case http2::DecodeStatus::kDecodeInProgress: - DCHECK_EQ(bytes_consumed, data.size()); + DCHECK_EQ(*bytes_consumed, data.size()); DCHECK(buffer.Empty()); - return bytes_consumed; + return true; case http2::DecodeStatus::kDecodeError: OnError("Encoded integer too large."); - return bytes_consumed; + return false; default: QUIC_BUG << "Unknown decode status " << status; - return bytes_consumed; + return false; } } -void QpackInstructionDecoder::DoVarintDone() { +bool QpackInstructionDecoder::DoVarintDone() { DCHECK(field_->type == QpackInstructionFieldType::kVarint || field_->type == QpackInstructionFieldType::kVarint2 || field_->type == QpackInstructionFieldType::kName || @@ -218,7 +230,7 @@ void QpackInstructionDecoder::DoVarintDone() { ++field_; state_ = State::kStartField; - return; + return true; } if (field_->type == QpackInstructionFieldType::kVarint2) { @@ -226,13 +238,13 @@ void QpackInstructionDecoder::DoVarintDone() { ++field_; state_ = State::kStartField; - return; + return true; } string_length_ = varint_decoder_.value(); if (string_length_ > kStringLiteralLengthLimit) { OnError("String literal too long."); - return; + return false; } std::string* const string = @@ -242,15 +254,17 @@ void QpackInstructionDecoder::DoVarintDone() { if (string_length_ == 0) { ++field_; state_ = State::kStartField; - return; + return true; } string->reserve(string_length_); state_ = State::kReadString; + return true; } -size_t QpackInstructionDecoder::DoReadString(QuicStringPiece data) { +bool QpackInstructionDecoder::DoReadString(QuicStringPiece data, + size_t* bytes_consumed) { DCHECK(!data.empty()); DCHECK(field_->type == QpackInstructionFieldType::kName || field_->type == QpackInstructionFieldType::kValue); @@ -259,18 +273,17 @@ size_t QpackInstructionDecoder::DoReadString(QuicStringPiece data) { (field_->type == QpackInstructionFieldType::kName) ? &name_ : &value_; DCHECK_LT(string->size(), string_length_); - size_t bytes_consumed = - std::min(string_length_ - string->size(), data.size()); - string->append(data.data(), bytes_consumed); + *bytes_consumed = std::min(string_length_ - string->size(), data.size()); + string->append(data.data(), *bytes_consumed); DCHECK_LE(string->size(), string_length_); if (string->size() == string_length_) { state_ = State::kReadStringDone; } - return bytes_consumed; + return true; } -void QpackInstructionDecoder::DoReadStringDone() { +bool QpackInstructionDecoder::DoReadStringDone() { DCHECK(field_->type == QpackInstructionFieldType::kName || field_->type == QpackInstructionFieldType::kValue); @@ -285,13 +298,14 @@ void QpackInstructionDecoder::DoReadStringDone() { huffman_decoder_.Decode(*string, &decoded_value); if (!huffman_decoder_.InputProperlyTerminated()) { OnError("Error in Huffman-encoded string."); - return; + return false; } *string = std::move(decoded_value); } ++field_; state_ = State::kStartField; + return true; } const QpackInstruction* QpackInstructionDecoder::LookupOpcode( diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_decoder.h b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_decoder.h index f478c249b07..4c217731a92 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_decoder.h +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_decoder.h @@ -11,7 +11,7 @@ #include "net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_decoder.h" #include "net/third_party/quiche/src/http2/hpack/varint/hpack_varint_decoder.h" -#include "net/third_party/quiche/src/quic/core/qpack/qpack_constants.h" +#include "net/third_party/quiche/src/quic/core/qpack/qpack_instructions.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" @@ -33,11 +33,15 @@ class QUIC_EXPORT_PRIVATE QpackInstructionDecoder { // Returns true if decoded fields are valid. // Returns false otherwise, in which case QpackInstructionDecoder stops // decoding: Delegate methods will not be called, and Decode() must not be - // called. + // called. Implementations are allowed to destroy the + // QpackInstructionDecoder instance synchronously if OnInstructionDecoded() + // returns false. virtual bool OnInstructionDecoded(const QpackInstruction* instruction) = 0; // Called by QpackInstructionDecoder if an error has occurred. // No more data is processed afterwards. + // Implementations are allowed to destroy the QpackInstructionDecoder + // instance synchronously. virtual void OnError(QuicStringPiece error_message) = 0; }; @@ -48,8 +52,9 @@ class QUIC_EXPORT_PRIVATE QpackInstructionDecoder { QpackInstructionDecoder& operator=(const QpackInstructionDecoder&) = delete; // Provide a data fragment to decode. Must not be called after an error has - // occurred. Must not be called with empty |data|. - void Decode(QuicStringPiece data); + // occurred. Must not be called with empty |data|. Return true on success, + // false on error (in which case Delegate::OnError() is called synchronously). + bool Decode(QuicStringPiece data); // Returns true if no decoding has taken place yet or if the last instruction // has been entirely parsed. @@ -84,18 +89,19 @@ class QUIC_EXPORT_PRIVATE QpackInstructionDecoder { kReadStringDone }; - // One method for each state. Some take input data and return the number of - // octets processed. Some take input data but do have void return type - // because they not consume any bytes. Some do not take any arguments because - // they only change internal state. - void DoStartInstruction(QuicStringPiece data); - void DoStartField(); - void DoReadBit(QuicStringPiece data); - size_t DoVarintStart(QuicStringPiece data); - size_t DoVarintResume(QuicStringPiece data); - void DoVarintDone(); - size_t DoReadString(QuicStringPiece data); - void DoReadStringDone(); + // One method for each state. They each return true on success, false on + // error (in which case |this| might already be destroyed). Some take input + // data and set |*bytes_consumed| to the number of octets processed. Some + // take input data but do not consume any bytes. Some do not take any + // arguments because they only change internal state. + bool DoStartInstruction(QuicStringPiece data); + bool DoStartField(); + bool DoReadBit(QuicStringPiece data); + bool DoVarintStart(QuicStringPiece data, size_t* bytes_consumed); + bool DoVarintResume(QuicStringPiece data, size_t* bytes_consumed); + bool DoVarintDone(); + bool DoReadString(QuicStringPiece data, size_t* bytes_consumed); + bool DoReadStringDone(); // Identify instruction based on opcode encoded in |byte|. // Returns a pointer to an element of |*language_|. @@ -127,8 +133,8 @@ class QUIC_EXPORT_PRIVATE QpackInstructionDecoder { // Decoder instance for decoding Huffman encoded strings. http2::HpackHuffmanDecoder huffman_decoder_; - // True if a decoding error has been detected either by - // QpackInstructionDecoder or by Delegate. + // True if a decoding error has been detected by QpackInstructionDecoder. + // Only used in DCHECKs. bool error_detected_; // Decoding state. diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_decoder_test.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_decoder_test.cc index 2d57f5c5320..c066827d295 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_decoder_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_decoder_test.cc @@ -6,15 +6,16 @@ #include <algorithm> -#include "net/third_party/quiche/src/quic/core/qpack/qpack_constants.h" -#include "net/third_party/quiche/src/quic/core/qpack/qpack_test_utils.h" +#include "net/third_party/quiche/src/quic/core/qpack/qpack_instructions.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" #include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/qpack/qpack_test_utils.h" using ::testing::_; using ::testing::Eq; using ::testing::Expectation; +using ::testing::Invoke; using ::testing::Return; using ::testing::StrictMock; using ::testing::Values; @@ -64,36 +65,52 @@ class MockDelegate : public QpackInstructionDecoder::Delegate { }; class QpackInstructionDecoderTest : public QuicTestWithParam<FragmentMode> { - public: + protected: QpackInstructionDecoderTest() - : decoder_(TestLanguage(), &delegate_), fragment_mode_(GetParam()) {} + : decoder_(std::make_unique<QpackInstructionDecoder>(TestLanguage(), + &delegate_)), + fragment_mode_(GetParam()) {} ~QpackInstructionDecoderTest() override = default; - protected: + void SetUp() override { + // Destroy QpackInstructionDecoder on error to test that it does not crash. + // See https://crbug.com/1025209. + ON_CALL(delegate_, OnError(_)) + .WillByDefault(Invoke( + [this](QuicStringPiece /* error_message */) { decoder_.reset(); })); + } + // Decode one full instruction with fragment sizes dictated by // |fragment_mode_|. - // Verifies that AtInstructionBoundary() returns true before and after the + // Assumes that |data| is a single complete instruction, and accordingly + // verifies that AtInstructionBoundary() returns true before and after the // instruction, and returns false while decoding is in progress. + // Assumes that delegate methods destroy |decoder_| if they return false. void DecodeInstruction(QuicStringPiece data) { - EXPECT_TRUE(decoder_.AtInstructionBoundary()); + EXPECT_TRUE(decoder_->AtInstructionBoundary()); FragmentSizeGenerator fragment_size_generator = FragmentModeToFragmentSizeGenerator(fragment_mode_); while (!data.empty()) { size_t fragment_size = std::min(fragment_size_generator(), data.size()); - decoder_.Decode(data.substr(0, fragment_size)); + bool success = decoder_->Decode(data.substr(0, fragment_size)); + if (!decoder_) { + EXPECT_FALSE(success); + return; + } + EXPECT_TRUE(success); data = data.substr(fragment_size); if (!data.empty()) { - EXPECT_FALSE(decoder_.AtInstructionBoundary()); + EXPECT_FALSE(decoder_->AtInstructionBoundary()); } } - EXPECT_TRUE(decoder_.AtInstructionBoundary()); + EXPECT_TRUE(decoder_->AtInstructionBoundary()); } StrictMock<MockDelegate> delegate_; - QpackInstructionDecoder decoder_; + std::unique_ptr<QpackInstructionDecoder> decoder_; private: const FragmentMode fragment_mode_; @@ -108,60 +125,82 @@ TEST_P(QpackInstructionDecoderTest, SBitAndVarint2) { EXPECT_CALL(delegate_, OnInstructionDecoded(TestInstruction1())); DecodeInstruction(QuicTextUtils::HexDecode("7f01ff65")); - EXPECT_TRUE(decoder_.s_bit()); - EXPECT_EQ(64u, decoder_.varint()); - EXPECT_EQ(356u, decoder_.varint2()); + EXPECT_TRUE(decoder_->s_bit()); + EXPECT_EQ(64u, decoder_->varint()); + EXPECT_EQ(356u, decoder_->varint2()); EXPECT_CALL(delegate_, OnInstructionDecoded(TestInstruction1())); DecodeInstruction(QuicTextUtils::HexDecode("05c8")); - EXPECT_FALSE(decoder_.s_bit()); - EXPECT_EQ(5u, decoder_.varint()); - EXPECT_EQ(200u, decoder_.varint2()); + EXPECT_FALSE(decoder_->s_bit()); + EXPECT_EQ(5u, decoder_->varint()); + EXPECT_EQ(200u, decoder_->varint2()); } TEST_P(QpackInstructionDecoderTest, NameAndValue) { EXPECT_CALL(delegate_, OnInstructionDecoded(TestInstruction2())); DecodeInstruction(QuicTextUtils::HexDecode("83666f6f03626172")); - EXPECT_EQ("foo", decoder_.name()); - EXPECT_EQ("bar", decoder_.value()); + EXPECT_EQ("foo", decoder_->name()); + EXPECT_EQ("bar", decoder_->value()); EXPECT_CALL(delegate_, OnInstructionDecoded(TestInstruction2())); DecodeInstruction(QuicTextUtils::HexDecode("8000")); - EXPECT_EQ("", decoder_.name()); - EXPECT_EQ("", decoder_.value()); + EXPECT_EQ("", decoder_->name()); + EXPECT_EQ("", decoder_->value()); EXPECT_CALL(delegate_, OnInstructionDecoded(TestInstruction2())); DecodeInstruction(QuicTextUtils::HexDecode("c294e7838c767f")); - EXPECT_EQ("foo", decoder_.name()); - EXPECT_EQ("bar", decoder_.value()); + EXPECT_EQ("foo", decoder_->name()); + EXPECT_EQ("bar", decoder_->value()); } TEST_P(QpackInstructionDecoderTest, InvalidHuffmanEncoding) { EXPECT_CALL(delegate_, OnError(Eq("Error in Huffman-encoded string."))); - decoder_.Decode(QuicTextUtils::HexDecode("c1ff")); + DecodeInstruction(QuicTextUtils::HexDecode("c1ff")); } TEST_P(QpackInstructionDecoderTest, InvalidVarintEncoding) { EXPECT_CALL(delegate_, OnError(Eq("Encoded integer too large."))); - decoder_.Decode(QuicTextUtils::HexDecode("ffffffffffffffffffffff")); + DecodeInstruction(QuicTextUtils::HexDecode("ffffffffffffffffffffff")); } TEST_P(QpackInstructionDecoderTest, DelegateSignalsError) { // First instruction is valid. Expectation first_call = EXPECT_CALL(delegate_, OnInstructionDecoded(TestInstruction1())) - .WillOnce(Return(true)); + .WillOnce(Invoke( + [this](const QpackInstruction * /* instruction */) -> bool { + EXPECT_EQ(1u, decoder_->varint()); + return true; + })); + // Second instruction is invalid. Decoding must halt. EXPECT_CALL(delegate_, OnInstructionDecoded(TestInstruction1())) .After(first_call) - .WillOnce(Return(false)); - decoder_.Decode(QuicTextUtils::HexDecode("01000200030004000500")); + .WillOnce( + Invoke([this](const QpackInstruction * /* instruction */) -> bool { + EXPECT_EQ(2u, decoder_->varint()); + return false; + })); + + EXPECT_FALSE( + decoder_->Decode(QuicTextUtils::HexDecode("01000200030004000500"))); +} - EXPECT_EQ(2u, decoder_.varint()); +// QpackInstructionDecoder must not crash if it is destroyed from a +// Delegate::OnInstructionDecoded() call as long as it returns false. +TEST_P(QpackInstructionDecoderTest, DelegateSignalsErrorAndDestroysDecoder) { + EXPECT_CALL(delegate_, OnInstructionDecoded(TestInstruction1())) + .WillOnce( + Invoke([this](const QpackInstruction * /* instruction */) -> bool { + EXPECT_EQ(1u, decoder_->varint()); + decoder_.reset(); + return false; + })); + DecodeInstruction(QuicTextUtils::HexDecode("0100")); } } // namespace diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_encoder.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_encoder.cc index 5845a748ffc..a87489d9e67 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_encoder.cc +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_encoder.cc @@ -16,13 +16,13 @@ namespace quic { QpackInstructionEncoder::QpackInstructionEncoder() : byte_(0), state_(State::kOpcode), instruction_(nullptr) {} -void QpackInstructionEncoder::Encode(const QpackInstruction* instruction, - const Values& values, - std::string* output) { - DCHECK(instruction); +void QpackInstructionEncoder::Encode( + const QpackInstructionWithValues& instruction_with_values, + std::string* output) { + DCHECK(instruction_with_values.instruction()); state_ = State::kOpcode; - instruction_ = instruction; + instruction_ = instruction_with_values.instruction(); field_ = instruction_->fields.begin(); // Field list must not be empty. @@ -37,13 +37,15 @@ void QpackInstructionEncoder::Encode(const QpackInstruction* instruction, DoStartField(); break; case State::kSbit: - DoSBit(values.s_bit); + DoSBit(instruction_with_values.s_bit()); break; case State::kVarintEncode: - DoVarintEncode(values.varint, values.varint2, output); + DoVarintEncode(instruction_with_values.varint(), + instruction_with_values.varint2(), output); break; case State::kStartString: - DoStartString(values.name, values.value); + DoStartString(instruction_with_values.name(), + instruction_with_values.value()); break; case State::kWriteString: DoWriteString(output); diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_encoder.h b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_encoder.h index 1ca52e667ec..04b2888172e 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_encoder.h +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_encoder.h @@ -8,7 +8,7 @@ #include <cstdint> #include <string> -#include "net/third_party/quiche/src/quic/core/qpack/qpack_constants.h" +#include "net/third_party/quiche/src/quic/core/qpack/qpack_instructions.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" @@ -19,23 +19,12 @@ namespace quic { // fields that follow each instruction. class QUIC_EXPORT_PRIVATE QpackInstructionEncoder { public: - // Storage for field values to be encoded. - // The encoded instruction determines which values are actually used. - struct Values { - bool s_bit; - uint64_t varint; - uint64_t varint2; - QuicStringPiece name; - QuicStringPiece value; - }; - QpackInstructionEncoder(); QpackInstructionEncoder(const QpackInstructionEncoder&) = delete; QpackInstructionEncoder& operator=(const QpackInstructionEncoder&) = delete; // Append encoded instruction to |output|. - void Encode(const QpackInstruction* instruction, - const Values& values, + void Encode(const QpackInstructionWithValues& instruction_with_values, std::string* output); private: diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_encoder_test.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_encoder_test.cc index 0d172cd6847..79dfe2af379 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_encoder_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_encoder_test.cc @@ -8,10 +8,44 @@ #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" -using ::testing::Values; - namespace quic { namespace test { + +class QpackInstructionWithValuesPeer { + public: + static QpackInstructionWithValues CreateQpackInstructionWithValues( + const QpackInstruction* instruction) { + QpackInstructionWithValues instruction_with_values; + instruction_with_values.instruction_ = instruction; + return instruction_with_values; + } + + static void set_s_bit(QpackInstructionWithValues* instruction_with_values, + bool s_bit) { + instruction_with_values->s_bit_ = s_bit; + } + + static void set_varint(QpackInstructionWithValues* instruction_with_values, + uint64_t varint) { + instruction_with_values->varint_ = varint; + } + + static void set_varint2(QpackInstructionWithValues* instruction_with_values, + uint64_t varint2) { + instruction_with_values->varint2_ = varint2; + } + + static void set_name(QpackInstructionWithValues* instruction_with_values, + QuicStringPiece name) { + instruction_with_values->name_ = name; + } + + static void set_value(QpackInstructionWithValues* instruction_with_values, + QuicStringPiece value) { + instruction_with_values->value_ = value; + } +}; + namespace { class QpackInstructionEncoderTest : public QuicTest { @@ -20,9 +54,9 @@ class QpackInstructionEncoderTest : public QuicTest { ~QpackInstructionEncoderTest() override = default; // Append encoded |instruction| to |output_|. - void EncodeInstruction(const QpackInstruction* instruction, - const QpackInstructionEncoder::Values& values) { - encoder_.Encode(instruction, values, &output_); + void EncodeInstruction( + const QpackInstructionWithValues& instruction_with_values) { + encoder_.Encode(instruction_with_values, &output_); } // Compare substring appended to |output_| since last EncodedSegmentMatches() @@ -44,13 +78,15 @@ TEST_F(QpackInstructionEncoderTest, Varint) { const QpackInstruction instruction{QpackInstructionOpcode{0x00, 0x80}, {{QpackInstructionFieldType::kVarint, 7}}}; - QpackInstructionEncoder::Values values; - values.varint = 5; - EncodeInstruction(&instruction, values); + auto instruction_with_values = + QpackInstructionWithValuesPeer::CreateQpackInstructionWithValues( + &instruction); + QpackInstructionWithValuesPeer::set_varint(&instruction_with_values, 5); + EncodeInstruction(instruction_with_values); EXPECT_TRUE(EncodedSegmentMatches("05")); - values.varint = 127; - EncodeInstruction(&instruction, values); + QpackInstructionWithValuesPeer::set_varint(&instruction_with_values, 127); + EncodeInstruction(instruction_with_values); EXPECT_TRUE(EncodedSegmentMatches("7f00")); } @@ -61,17 +97,19 @@ TEST_F(QpackInstructionEncoderTest, SBitAndTwoVarint2) { {QpackInstructionFieldType::kVarint, 5}, {QpackInstructionFieldType::kVarint2, 8}}}; - QpackInstructionEncoder::Values values; - values.s_bit = true; - values.varint = 5; - values.varint2 = 200; - EncodeInstruction(&instruction, values); + auto instruction_with_values = + QpackInstructionWithValuesPeer::CreateQpackInstructionWithValues( + &instruction); + QpackInstructionWithValuesPeer::set_s_bit(&instruction_with_values, true); + QpackInstructionWithValuesPeer::set_varint(&instruction_with_values, 5); + QpackInstructionWithValuesPeer::set_varint2(&instruction_with_values, 200); + EncodeInstruction(instruction_with_values); EXPECT_TRUE(EncodedSegmentMatches("a5c8")); - values.s_bit = false; - values.varint = 31; - values.varint2 = 356; - EncodeInstruction(&instruction, values); + QpackInstructionWithValuesPeer::set_s_bit(&instruction_with_values, false); + QpackInstructionWithValuesPeer::set_varint(&instruction_with_values, 31); + QpackInstructionWithValuesPeer::set_varint2(&instruction_with_values, 356); + EncodeInstruction(instruction_with_values); EXPECT_TRUE(EncodedSegmentMatches("9f00ff65")); } @@ -81,17 +119,19 @@ TEST_F(QpackInstructionEncoderTest, SBitAndVarintAndValue) { {QpackInstructionFieldType::kVarint, 5}, {QpackInstructionFieldType::kValue, 7}}}; - QpackInstructionEncoder::Values values; - values.s_bit = true; - values.varint = 100; - values.value = "foo"; - EncodeInstruction(&instruction, values); + auto instruction_with_values = + QpackInstructionWithValuesPeer::CreateQpackInstructionWithValues( + &instruction); + QpackInstructionWithValuesPeer::set_s_bit(&instruction_with_values, true); + QpackInstructionWithValuesPeer::set_varint(&instruction_with_values, 100); + QpackInstructionWithValuesPeer::set_value(&instruction_with_values, "foo"); + EncodeInstruction(instruction_with_values); EXPECT_TRUE(EncodedSegmentMatches("ff458294e7")); - values.s_bit = false; - values.varint = 3; - values.value = "bar"; - EncodeInstruction(&instruction, values); + QpackInstructionWithValuesPeer::set_s_bit(&instruction_with_values, false); + QpackInstructionWithValuesPeer::set_varint(&instruction_with_values, 3); + QpackInstructionWithValuesPeer::set_value(&instruction_with_values, "bar"); + EncodeInstruction(instruction_with_values); EXPECT_TRUE(EncodedSegmentMatches("c303626172")); } @@ -99,17 +139,19 @@ TEST_F(QpackInstructionEncoderTest, Name) { const QpackInstruction instruction{QpackInstructionOpcode{0xe0, 0xe0}, {{QpackInstructionFieldType::kName, 4}}}; - QpackInstructionEncoder::Values values; - values.name = ""; - EncodeInstruction(&instruction, values); + auto instruction_with_values = + QpackInstructionWithValuesPeer::CreateQpackInstructionWithValues( + &instruction); + QpackInstructionWithValuesPeer::set_name(&instruction_with_values, ""); + EncodeInstruction(instruction_with_values); EXPECT_TRUE(EncodedSegmentMatches("e0")); - values.name = "foo"; - EncodeInstruction(&instruction, values); + QpackInstructionWithValuesPeer::set_name(&instruction_with_values, "foo"); + EncodeInstruction(instruction_with_values); EXPECT_TRUE(EncodedSegmentMatches("f294e7")); - values.name = "bar"; - EncodeInstruction(&instruction, values); + QpackInstructionWithValuesPeer::set_name(&instruction_with_values, "bar"); + EncodeInstruction(instruction_with_values); EXPECT_TRUE(EncodedSegmentMatches("e3626172")); } @@ -117,17 +159,19 @@ TEST_F(QpackInstructionEncoderTest, Value) { const QpackInstruction instruction{QpackInstructionOpcode{0xf0, 0xf0}, {{QpackInstructionFieldType::kValue, 3}}}; - QpackInstructionEncoder::Values values; - values.value = ""; - EncodeInstruction(&instruction, values); + auto instruction_with_values = + QpackInstructionWithValuesPeer::CreateQpackInstructionWithValues( + &instruction); + QpackInstructionWithValuesPeer::set_value(&instruction_with_values, ""); + EncodeInstruction(instruction_with_values); EXPECT_TRUE(EncodedSegmentMatches("f0")); - values.value = "foo"; - EncodeInstruction(&instruction, values); + QpackInstructionWithValuesPeer::set_value(&instruction_with_values, "foo"); + EncodeInstruction(instruction_with_values); EXPECT_TRUE(EncodedSegmentMatches("fa94e7")); - values.value = "bar"; - EncodeInstruction(&instruction, values); + QpackInstructionWithValuesPeer::set_value(&instruction_with_values, "bar"); + EncodeInstruction(instruction_with_values); EXPECT_TRUE(EncodedSegmentMatches("f3626172")); } @@ -137,17 +181,19 @@ TEST_F(QpackInstructionEncoderTest, SBitAndNameAndValue) { {QpackInstructionFieldType::kName, 2}, {QpackInstructionFieldType::kValue, 7}}}; - QpackInstructionEncoder::Values values; - values.s_bit = false; - values.name = ""; - values.value = ""; - EncodeInstruction(&instruction, values); + auto instruction_with_values = + QpackInstructionWithValuesPeer::CreateQpackInstructionWithValues( + &instruction); + QpackInstructionWithValuesPeer::set_s_bit(&instruction_with_values, false); + QpackInstructionWithValuesPeer::set_name(&instruction_with_values, ""); + QpackInstructionWithValuesPeer::set_value(&instruction_with_values, ""); + EncodeInstruction(instruction_with_values); EXPECT_TRUE(EncodedSegmentMatches("f000")); - values.s_bit = true; - values.name = "foo"; - values.value = "bar"; - EncodeInstruction(&instruction, values); + QpackInstructionWithValuesPeer::set_s_bit(&instruction_with_values, true); + QpackInstructionWithValuesPeer::set_name(&instruction_with_values, "foo"); + QpackInstructionWithValuesPeer::set_value(&instruction_with_values, "bar"); + EncodeInstruction(instruction_with_values); EXPECT_TRUE(EncodedSegmentMatches("fe94e703626172")); } diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_constants.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instructions.cc index 6644918b34a..a6a7529bf09 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_constants.cc +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instructions.cc @@ -2,7 +2,7 @@ // 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_constants.h" +#include "net/third_party/quiche/src/quic/core/qpack/qpack_instructions.h" #include <limits> @@ -199,4 +199,133 @@ const QpackLanguage* QpackRequestStreamLanguage() { return language; } +// static +QpackInstructionWithValues QpackInstructionWithValues::InsertWithNameReference( + bool is_static, + uint64_t name_index, + QuicStringPiece value) { + QpackInstructionWithValues instruction_with_values; + instruction_with_values.instruction_ = InsertWithNameReferenceInstruction(); + instruction_with_values.s_bit_ = is_static; + instruction_with_values.varint_ = name_index; + instruction_with_values.value_ = value; + + return instruction_with_values; +} + +// static +QpackInstructionWithValues +QpackInstructionWithValues::InsertWithoutNameReference(QuicStringPiece name, + QuicStringPiece value) { + QpackInstructionWithValues instruction_with_values; + instruction_with_values.instruction_ = + InsertWithoutNameReferenceInstruction(); + instruction_with_values.name_ = name; + instruction_with_values.value_ = value; + + return instruction_with_values; +} + +// static +QpackInstructionWithValues QpackInstructionWithValues::Duplicate( + uint64_t index) { + QpackInstructionWithValues instruction_with_values; + instruction_with_values.instruction_ = DuplicateInstruction(); + instruction_with_values.varint_ = index; + + return instruction_with_values; +} + +// static +QpackInstructionWithValues QpackInstructionWithValues::SetDynamicTableCapacity( + uint64_t capacity) { + QpackInstructionWithValues instruction_with_values; + instruction_with_values.instruction_ = SetDynamicTableCapacityInstruction(); + instruction_with_values.varint_ = capacity; + + return instruction_with_values; +} + +// static +QpackInstructionWithValues QpackInstructionWithValues::InsertCountIncrement( + uint64_t increment) { + QpackInstructionWithValues instruction_with_values; + instruction_with_values.instruction_ = InsertCountIncrementInstruction(); + instruction_with_values.varint_ = increment; + + return instruction_with_values; +} + +// static +QpackInstructionWithValues QpackInstructionWithValues::HeaderAcknowledgement( + uint64_t stream_id) { + QpackInstructionWithValues instruction_with_values; + instruction_with_values.instruction_ = HeaderAcknowledgementInstruction(); + instruction_with_values.varint_ = stream_id; + + return instruction_with_values; +} + +// static +QpackInstructionWithValues QpackInstructionWithValues::StreamCancellation( + uint64_t stream_id) { + QpackInstructionWithValues instruction_with_values; + instruction_with_values.instruction_ = StreamCancellationInstruction(); + instruction_with_values.varint_ = stream_id; + + return instruction_with_values; +} + +// static +QpackInstructionWithValues QpackInstructionWithValues::Prefix( + uint64_t required_insert_count) { + QpackInstructionWithValues instruction_with_values; + instruction_with_values.instruction_ = QpackPrefixInstruction(); + instruction_with_values.varint_ = required_insert_count; + instruction_with_values.varint2_ = 0; // Delta Base. + instruction_with_values.s_bit_ = false; // Delta Base sign. + + return instruction_with_values; +} + +// static +QpackInstructionWithValues QpackInstructionWithValues::IndexedHeaderField( + bool is_static, + uint64_t index) { + QpackInstructionWithValues instruction_with_values; + instruction_with_values.instruction_ = QpackIndexedHeaderFieldInstruction(); + instruction_with_values.s_bit_ = is_static; + instruction_with_values.varint_ = index; + + return instruction_with_values; +} + +// static +QpackInstructionWithValues +QpackInstructionWithValues::LiteralHeaderFieldNameReference( + bool is_static, + uint64_t index, + QuicStringPiece value) { + QpackInstructionWithValues instruction_with_values; + instruction_with_values.instruction_ = + QpackLiteralHeaderFieldNameReferenceInstruction(); + instruction_with_values.s_bit_ = is_static; + instruction_with_values.varint_ = index; + instruction_with_values.value_ = value; + + return instruction_with_values; +} + +// static +QpackInstructionWithValues QpackInstructionWithValues::LiteralHeaderField( + QuicStringPiece name, + QuicStringPiece value) { + QpackInstructionWithValues instruction_with_values; + instruction_with_values.instruction_ = QpackLiteralHeaderFieldInstruction(); + instruction_with_values.name_ = name; + instruction_with_values.value_ = value; + + return instruction_with_values; +} + } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_constants.h b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instructions.h index 35e4d560bef..0ff18bff254 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_constants.h +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instructions.h @@ -2,8 +2,8 @@ // 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_CONSTANTS_H_ -#define QUICHE_QUIC_CORE_QPACK_QPACK_CONSTANTS_H_ +#ifndef QUICHE_QUIC_CORE_QPACK_QPACK_INSTRUCTIONS_H_ +#define QUICHE_QUIC_CORE_QPACK_QPACK_INSTRUCTIONS_H_ #include <cstdint> #include <string> @@ -11,9 +11,14 @@ #include <vector> #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" namespace quic { +namespace test { +class QpackInstructionWithValuesPeer; +} // namespace test + // Each instruction is identified with an opcode in the first byte. // |mask| determines which bits are part of the opcode. // |value| is the value of these bits. (Other bits in value must be zero.) @@ -137,6 +142,66 @@ const QpackInstruction* QpackLiteralHeaderFieldInstruction(); // Request and push stream language. const QpackLanguage* QpackRequestStreamLanguage(); +// Storage for instruction and field values to be encoded. +// This class can only be instantiated using factory methods that take exactly +// the arguments that the corresponding instruction needs. +class QUIC_EXPORT_PRIVATE QpackInstructionWithValues { + public: + // 5.2 Encoder stream instructions + static QpackInstructionWithValues InsertWithNameReference( + bool is_static, + uint64_t name_index, + QuicStringPiece value); + static QpackInstructionWithValues InsertWithoutNameReference( + QuicStringPiece name, + QuicStringPiece value); + static QpackInstructionWithValues Duplicate(uint64_t index); + static QpackInstructionWithValues SetDynamicTableCapacity(uint64_t capacity); + + // 5.3 Decoder stream instructions + static QpackInstructionWithValues InsertCountIncrement(uint64_t increment); + static QpackInstructionWithValues HeaderAcknowledgement(uint64_t stream_id); + static QpackInstructionWithValues StreamCancellation(uint64_t stream_id); + + // 5.4.1. Header data prefix. Delta Base is hardcoded to be zero. + static QpackInstructionWithValues Prefix(uint64_t required_insert_count); + + // 5.4.2. Request and push stream instructions + static QpackInstructionWithValues IndexedHeaderField(bool is_static, + uint64_t index); + static QpackInstructionWithValues LiteralHeaderFieldNameReference( + bool is_static, + uint64_t index, + QuicStringPiece value); + static QpackInstructionWithValues LiteralHeaderField(QuicStringPiece name, + QuicStringPiece value); + + const QpackInstruction* instruction() const { return instruction_; } + bool s_bit() const { return s_bit_; } + uint64_t varint() const { return varint_; } + uint64_t varint2() const { return varint2_; } + QuicStringPiece name() const { return name_; } + QuicStringPiece value() const { return value_; } + + // Used by QpackEncoder, because in the first pass it stores absolute indices, + // which are converted into relative indices in the second pass after base is + // determined. + void set_varint(uint64_t varint) { varint_ = varint; } + + private: + friend test::QpackInstructionWithValuesPeer; + + QpackInstructionWithValues() = default; + + // |*instruction| is not owned. + const QpackInstruction* instruction_; + bool s_bit_; + uint64_t varint_; + uint64_t varint2_; + QuicStringPiece name_; + QuicStringPiece value_; +}; + } // namespace quic -#endif // QUICHE_QUIC_CORE_QPACK_QPACK_CONSTANTS_H_ +#endif // QUICHE_QUIC_CORE_QPACK_QPACK_INSTRUCTIONS_H_ diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/offline/qpack_offline_decoder_bin.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_offline_decoder_bin.cc index d72b0003da4..327816e8cb2 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/offline/qpack_offline_decoder_bin.cc +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_offline_decoder_bin.cc @@ -2,14 +2,13 @@ // 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/offline/qpack_offline_decoder.h" - #include <cstddef> #include <iostream> #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_string_piece.h" +#include "net/third_party/quiche/src/quic/test_tools/qpack/qpack_offline_decoder.h" int main(int argc, char* argv[]) { const char* usage = 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 3c0b0f546fa..c9e19d2d013 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,8 +8,8 @@ #include <limits> #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_instructions.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" @@ -57,11 +57,13 @@ void QpackProgressiveDecoder::Decode(QuicStringPiece data) { while (!prefix_decoded_) { DCHECK(!blocked_); - prefix_decoder_->Decode(data.substr(0, 1)); - if (error_detected_) { + if (!prefix_decoder_->Decode(data.substr(0, 1))) { return; } + // |prefix_decoder_->Decode()| must return false if an error is detected. + DCHECK(!error_detected_); + data = data.substr(1); if (data.empty()) { return; @@ -115,20 +117,28 @@ void QpackProgressiveDecoder::OnError(QuicStringPiece error_message) { DCHECK(!error_detected_); error_detected_ = true; + // Might destroy |this|. handler_->OnDecodingErrorDetected(error_message); } void QpackProgressiveDecoder::OnInsertCountReachedThreshold() { DCHECK(blocked_); + // Clear |blocked_| before calling instruction_decoder_.Decode() below, + // because that might destroy |this| and ~QpackProgressiveDecoder() needs to + // know not to call UnregisterObserver(). + blocked_ = false; + enforcer_->OnStreamUnblocked(stream_id_); + if (!buffer_.empty()) { - instruction_decoder_.Decode(buffer_); + std::string buffer(std::move(buffer_)); buffer_.clear(); + if (!instruction_decoder_.Decode(buffer)) { + // |this| might be destroyed. + return; + } } - blocked_ = false; - enforcer_->OnStreamUnblocked(stream_id_); - if (!decoding_) { FinishDecoding(); } @@ -163,6 +173,7 @@ bool QpackProgressiveDecoder::DoIndexedHeaderFieldInstruction() { return false; } + header_table_->set_dynamic_table_entry_referenced(); handler_->OnHeaderDecoded(entry->name(), entry->value()); return true; } @@ -202,6 +213,7 @@ bool QpackProgressiveDecoder::DoIndexedHeaderFieldPostBaseInstruction() { return false; } + header_table_->set_dynamic_table_entry_referenced(); handler_->OnHeaderDecoded(entry->name(), entry->value()); return true; } @@ -231,6 +243,7 @@ bool QpackProgressiveDecoder::DoLiteralHeaderFieldNameReferenceInstruction() { return false; } + header_table_->set_dynamic_table_entry_referenced(); handler_->OnHeaderDecoded(entry->name(), instruction_decoder_.value()); return true; } @@ -270,6 +283,7 @@ bool QpackProgressiveDecoder::DoLiteralHeaderFieldPostBaseInstruction() { return false; } + header_table_->set_dynamic_table_entry_referenced(); handler_->OnHeaderDecoded(entry->name(), instruction_decoder_.value()); return true; } 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 2f306c8e19c..6599c1a3ff8 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 @@ -44,7 +44,8 @@ class QUIC_EXPORT_PRIVATE QpackProgressiveDecoder virtual void OnDecodingCompleted() = 0; // Called when a decoding error has occurred. No other methods will be - // called afterwards. + // called afterwards. Implementations are allowed to destroy + // the QpackProgressiveDecoder instance synchronously. virtual void OnDecodingErrorDetected(QuicStringPiece error_message) = 0; }; 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 567676c75d2..f0dc797fa3b 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 @@ -5,15 +5,13 @@ #include <string> #include <tuple> -#include "net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test_utils.h" -#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_string_piece.h" #include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/test_tools/qpack/qpack_decoder_test_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/qpack/qpack_encoder_test_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/qpack/qpack_test_utils.h" #include "net/third_party/quiche/src/spdy/core/spdy_header_block.h" -using ::testing::Combine; using ::testing::Values; namespace quic { diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_test_utils.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_test_utils.cc deleted file mode 100644 index 2d4a72e71b0..00000000000 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_test_utils.cc +++ /dev/null @@ -1,23 +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/qpack/qpack_test_utils.h" - -#include <limits> - -namespace quic { -namespace test { - -FragmentSizeGenerator FragmentModeToFragmentSizeGenerator( - FragmentMode fragment_mode) { - switch (fragment_mode) { - case FragmentMode::kSingleChunk: - return []() { return std::numeric_limits<size_t>::max(); }; - case FragmentMode::kOctetByOctet: - return []() { return 1; }; - } -} - -} // namespace test -} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_test_utils.h b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_test_utils.h deleted file mode 100644 index 42fa383a3ea..00000000000 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_test_utils.h +++ /dev/null @@ -1,40 +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_QPACK_QPACK_TEST_UTILS_H_ -#define QUICHE_QUIC_CORE_QPACK_QPACK_TEST_UTILS_H_ - -#include <cstddef> -#include <functional> - -#include "net/third_party/quiche/src/quic/core/qpack/qpack_stream_sender_delegate.h" -#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" - -namespace quic { -namespace test { - -// Called repeatedly to determine the size of each fragment when encoding or -// decoding. Must return a positive value. -using FragmentSizeGenerator = std::function<size_t()>; - -enum class FragmentMode { - kSingleChunk, - kOctetByOctet, -}; - -FragmentSizeGenerator FragmentModeToFragmentSizeGenerator( - FragmentMode fragment_mode); - -// Mock QpackUnidirectionalStreamSenderDelegate implementation. -class MockQpackStreamSenderDelegate : public QpackStreamSenderDelegate { - public: - ~MockQpackStreamSenderDelegate() override = default; - - MOCK_METHOD1(WriteStreamData, void(QuicStringPiece data)); -}; - -} // namespace test -} // namespace quic - -#endif // QUICHE_QUIC_CORE_QPACK_QPACK_TEST_UTILS_H_ diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_utils.h b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_utils.h deleted file mode 100644 index 1b63422d4ac..00000000000 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_utils.h +++ /dev/null @@ -1,23 +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. - -#ifndef QUICHE_QUIC_CORE_QPACK_QPACK_UTILS_H_ -#define QUICHE_QUIC_CORE_QPACK_QPACK_UTILS_H_ - -#include "net/third_party/quiche/src/quic/core/qpack/qpack_stream_sender_delegate.h" - -namespace quic { -// TODO(renjietang): Move this class to qpack_test_utils.h once it is not needed -// in QuicSpdySession. -class QUIC_EXPORT_PRIVATE NoopQpackStreamSenderDelegate - : public QpackStreamSenderDelegate { - public: - ~NoopQpackStreamSenderDelegate() override = default; - - void WriteStreamData(QuicStringPiece /*data*/) override {} -}; - -} // namespace quic - -#endif // QUICHE_QUIC_CORE_QPACK_QPACK_UTILS_H_ diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/value_splitting_header_list_test.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/value_splitting_header_list_test.cc index 03a5eb03def..bab52386512 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/value_splitting_header_list_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/value_splitting_header_list_test.cc @@ -60,10 +60,16 @@ TEST(ValueSplittingHeaderListTest, Comparison) { EXPECT_FALSE(it1 == it2); EXPECT_TRUE(it1 != it2); } - ++it2; + if (j < kEnd - 1) { + ASSERT_NE(it2, headers.end()); + ++it2; + } } - ++it1; + if (i < kEnd - 1) { + ASSERT_NE(it1, headers.end()); + ++it1; + } } } @@ -75,37 +81,37 @@ TEST(ValueSplittingHeaderListTest, Empty) { EXPECT_EQ(headers.begin(), headers.end()); } -struct { - const char* name; - QuicStringPiece value; - std::vector<const char*> expected_values; -} kTestData[]{ - // Empty value. - {"foo", "", {""}}, - // Trivial case. - {"foo", "bar", {"bar"}}, - // Simple split. - {"foo", {"bar\0baz", 7}, {"bar", "baz"}}, - {"cookie", "foo;bar", {"foo", "bar"}}, - {"cookie", "foo; bar", {"foo", "bar"}}, - // Empty fragments with \0 separator. - {"foo", {"\0", 1}, {"", ""}}, - {"bar", {"foo\0", 4}, {"foo", ""}}, - {"baz", {"\0bar", 4}, {"", "bar"}}, - {"qux", {"\0foobar\0", 8}, {"", "foobar", ""}}, - // Empty fragments with ";" separator. - {"cookie", ";", {"", ""}}, - {"cookie", "foo;", {"foo", ""}}, - {"cookie", ";bar", {"", "bar"}}, - {"cookie", ";foobar;", {"", "foobar", ""}}, - // Empty fragments with "; " separator. - {"cookie", "; ", {"", ""}}, - {"cookie", "foo; ", {"foo", ""}}, - {"cookie", "; bar", {"", "bar"}}, - {"cookie", "; foobar; ", {"", "foobar", ""}}, -}; - TEST(ValueSplittingHeaderListTest, Split) { + struct { + const char* name; + QuicStringPiece value; + std::vector<const char*> expected_values; + } kTestData[]{ + // Empty value. + {"foo", "", {""}}, + // Trivial case. + {"foo", "bar", {"bar"}}, + // Simple split. + {"foo", {"bar\0baz", 7}, {"bar", "baz"}}, + {"cookie", "foo;bar", {"foo", "bar"}}, + {"cookie", "foo; bar", {"foo", "bar"}}, + // Empty fragments with \0 separator. + {"foo", {"\0", 1}, {"", ""}}, + {"bar", {"foo\0", 4}, {"foo", ""}}, + {"baz", {"\0bar", 4}, {"", "bar"}}, + {"qux", {"\0foobar\0", 8}, {"", "foobar", ""}}, + // Empty fragments with ";" separator. + {"cookie", ";", {"", ""}}, + {"cookie", "foo;", {"foo", ""}}, + {"cookie", ";bar", {"", "bar"}}, + {"cookie", ";foobar;", {"", "foobar", ""}}, + // Empty fragments with "; " separator. + {"cookie", "; ", {"", ""}}, + {"cookie", "foo; ", {"foo", ""}}, + {"cookie", "; bar", {"", "bar"}}, + {"cookie", "; foobar; ", {"", "foobar", ""}}, + }; + for (size_t i = 0; i < QUIC_ARRAYSIZE(kTestData); ++i) { spdy::SpdyHeaderBlock block; block[kTestData[i].name] = kTestData[i].value; |