|  | // Copyright 2024 The Chromium Authors | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "net/quic/quic_socket_data_provider.h" | 
|  |  | 
|  | #include <algorithm> | 
|  | #include <map> | 
|  | #include <memory> | 
|  | #include <optional> | 
|  | #include <set> | 
|  | #include <sstream> | 
|  | #include <string> | 
|  |  | 
|  | #include "base/functional/callback.h" | 
|  | #include "base/run_loop.h" | 
|  | #include "base/strings/string_number_conversions.h" | 
|  | #include "base/task/sequenced_task_runner.h" | 
|  | #include "net/base/hex_utils.h" | 
|  | #include "net/socket/socket_test_util.h" | 
|  | #include "net/third_party/quiche/src/quiche/quic/core/crypto/null_encrypter.h" | 
|  | #include "net/third_party/quiche/src/quiche/quic/core/crypto/quic_crypto_server_config.h" | 
|  | #include "net/third_party/quiche/src/quiche/quic/core/http/quic_spdy_session.h" | 
|  | #include "net/third_party/quiche/src/quiche/quic/core/quic_packets.h" | 
|  | #include "net/third_party/quiche/src/quiche/quic/core/quic_time.h" | 
|  | #include "net/third_party/quiche/src/quiche/quic/core/quic_versions.h" | 
|  | #include "net/third_party/quiche/src/quiche/quic/test_tools/crypto_test_utils.h" | 
|  | #include "net/third_party/quiche/src/quiche/quic/test_tools/quic_test_utils.h" | 
|  | #include "net/third_party/quiche/src/quiche/quic/tools/quic_simple_server_session.h" | 
|  |  | 
|  | namespace quic { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | class HttpStreamPrinter : public Http3DebugVisitor { | 
|  | public: | 
|  | explicit HttpStreamPrinter(std::ostream* output) : output_(output) {} | 
|  |  | 
|  | void OnControlStreamCreated(QuicStreamId stream_id) override { | 
|  | if (output_) { | 
|  | *output_ << "OnControlStreamCreated: " << stream_id << "\n"; | 
|  | } | 
|  | } | 
|  |  | 
|  | void OnQpackEncoderStreamCreated(QuicStreamId stream_id) override { | 
|  | if (output_) { | 
|  | *output_ << "OnQpackEncoderStreamCreated: " << stream_id << "\n"; | 
|  | } | 
|  | } | 
|  |  | 
|  | void OnQpackDecoderStreamCreated(QuicStreamId stream_id) override { | 
|  | if (output_) { | 
|  | *output_ << "OnQpackDecoderStreamCreated: " << stream_id << "\n"; | 
|  | } | 
|  | } | 
|  |  | 
|  | void OnPeerControlStreamCreated(QuicStreamId stream_id) override { | 
|  | if (output_) { | 
|  | *output_ << "OnPeerControlStreamCreated: " << stream_id << "\n"; | 
|  | } | 
|  | } | 
|  |  | 
|  | void OnPeerQpackEncoderStreamCreated(QuicStreamId stream_id) override { | 
|  | if (output_) { | 
|  | *output_ << "OnPeerQpackEncoderStreamCreated: " << stream_id << "\n"; | 
|  | } | 
|  | } | 
|  |  | 
|  | void OnPeerQpackDecoderStreamCreated(QuicStreamId stream_id) override { | 
|  | if (output_) { | 
|  | *output_ << "OnPeerQpackDecoderStreamCreated: " << stream_id << "\n"; | 
|  | } | 
|  | } | 
|  |  | 
|  | void OnSettingsFrameReceived(const SettingsFrame& frame) override { | 
|  | if (output_) { | 
|  | *output_ << "OnSettingsFrameReceived: " << frame << "\n"; | 
|  | } | 
|  | } | 
|  |  | 
|  | void OnGoAwayFrameReceived(const GoAwayFrame& frame) override { | 
|  | if (output_) { | 
|  | *output_ << "OnGoAwayFrameReceived" << "\n"; | 
|  | } | 
|  | } | 
|  |  | 
|  | void OnPriorityUpdateFrameReceived( | 
|  | const PriorityUpdateFrame& frame) override { | 
|  | if (output_) { | 
|  | *output_ << "OnPriorityUpdateFrameReceived: " << frame << "\n"; | 
|  | } | 
|  | } | 
|  |  | 
|  | void OnDataFrameReceived(QuicStreamId stream_id, | 
|  | QuicByteCount payload_length) override { | 
|  | if (output_) { | 
|  | *output_ << "OnDataFrameReceived: " << stream_id << ", " | 
|  | << "received: " << payload_length << " bytes" << "\n"; | 
|  | } | 
|  | } | 
|  |  | 
|  | void OnHeadersFrameReceived( | 
|  | QuicStreamId stream_id, | 
|  | QuicByteCount compressed_headers_length) override { | 
|  | if (output_) { | 
|  | *output_ << "OnHeadersFrameReceived: " << stream_id << ", " | 
|  | << "received: " << compressed_headers_length << " bytes" << "\n"; | 
|  | } | 
|  | } | 
|  |  | 
|  | void OnHeadersDecoded(QuicStreamId stream_id, | 
|  | const QuicHeaderList& headers) override { | 
|  | if (output_) { | 
|  | *output_ << "OnHeadersDecoded: " << stream_id << ", " | 
|  | << "received: " << headers.DebugString() << "\n"; | 
|  | } | 
|  | } | 
|  |  | 
|  | void OnUnknownFrameReceived(QuicStreamId stream_id, | 
|  | uint64_t frame_type, | 
|  | QuicByteCount payload_length) override { | 
|  | if (output_) { | 
|  | *output_ << "OnUnknownFrameReceived: " << stream_id << ", " | 
|  | << "frame_type: " << frame_type << "," | 
|  | << "received: " << payload_length << "bytes " << "\n "; | 
|  | } | 
|  | } | 
|  |  | 
|  | void OnSettingsFrameSent(const SettingsFrame& frame) override { | 
|  | if (output_) { | 
|  | *output_ << "OnSettingsFrameSent: " << frame << "\n"; | 
|  | } | 
|  | } | 
|  |  | 
|  | void OnSettingsFrameResumed(const SettingsFrame& frame) override { | 
|  | if (output_) { | 
|  | *output_ << "OnSettingsFrameResumed: " << frame << "\n"; | 
|  | } | 
|  | } | 
|  |  | 
|  | void OnGoAwayFrameSent(QuicStreamId stream_id) override { | 
|  | if (output_) { | 
|  | *output_ << "OnGoAwayFrameSent: " << stream_id << "\n"; | 
|  | } | 
|  | } | 
|  |  | 
|  | void OnPriorityUpdateFrameSent(const PriorityUpdateFrame& frame) override { | 
|  | if (output_) { | 
|  | *output_ << "OnPriorityUpdateFrameSent: " << frame << "\n"; | 
|  | } | 
|  | } | 
|  |  | 
|  | void OnDataFrameSent(QuicStreamId stream_id, | 
|  | QuicByteCount payload_length) override { | 
|  | if (output_) { | 
|  | *output_ << "OnDataFrameSent: " << stream_id << ", " | 
|  | << "sent: " << payload_length << " bytes" << "\n"; | 
|  | } | 
|  | } | 
|  |  | 
|  | void OnHeadersFrameSent( | 
|  | QuicStreamId stream_id, | 
|  | const quiche::HttpHeaderBlock& header_block) override { | 
|  | if (output_) { | 
|  | *output_ << "OnHeadersFrameSent: " << stream_id << ", " | 
|  | << "sent: " << header_block.DebugString() << "\n"; | 
|  | } | 
|  | } | 
|  |  | 
|  | private: | 
|  | mutable raw_ptr<std::ostream> output_ = nullptr; | 
|  | }; | 
|  | }  // namespace | 
|  |  | 
|  | }  // namespace quic | 
|  |  | 
|  | namespace net::test { | 
|  |  | 
|  | QuicSimpleServerSessionForTest::~QuicSimpleServerSessionForTest() = default; | 
|  |  | 
|  | // Always return true if CryptoStream is created, so that the pending stream can | 
|  | // handle initial setting frames for tests. | 
|  | bool QuicSimpleServerSessionForTest::IsEncryptionEstablished() const { | 
|  | return GetCryptoStream() != nullptr; | 
|  | } | 
|  |  | 
|  | QuicSocketDataProvider::Expectation::Expectation( | 
|  | std::string name, | 
|  | Type type, | 
|  | int rv, | 
|  | std::unique_ptr<quic::QuicEncryptedPacket> packet) | 
|  | : name_(std::move(name)), | 
|  | type_(type), | 
|  | rv_(rv), | 
|  | packet_(std::move(packet)) {} | 
|  |  | 
|  | QuicSocketDataProvider::Expectation::Expectation( | 
|  | QuicSocketDataProvider::Expectation&&) = default; | 
|  |  | 
|  | QuicSocketDataProvider::Expectation::~Expectation() = default; | 
|  |  | 
|  | QuicSocketDataProvider::Expectation& QuicSocketDataProvider::Expectation::After( | 
|  | std::string name) { | 
|  | after_.insert(std::move(name)); | 
|  | return *this; | 
|  | } | 
|  |  | 
|  | std::string QuicSocketDataProvider::Expectation::TypeToString( | 
|  | QuicSocketDataProvider::Expectation::Type type) { | 
|  | switch (type) { | 
|  | case Expectation::Type::READ: | 
|  | return "READ"; | 
|  | case Expectation::Type::WRITE: | 
|  | return "WRITE"; | 
|  | case Expectation::Type::PAUSE: | 
|  | return "PAUSE"; | 
|  | } | 
|  | NOTREACHED(); | 
|  | } | 
|  |  | 
|  | void QuicSocketDataProvider::Expectation::Consume() { | 
|  | CHECK(!consumed_); | 
|  | VLOG(1) << "Consuming " << TypeToString(type_) << " expectation " << name_; | 
|  | consumed_ = true; | 
|  | } | 
|  |  | 
|  | QuicSocketDataProvider::QuicSocketDataProvider(quic::ParsedQuicVersion version) | 
|  | : printer_(version), | 
|  | crypto_config_(quic::QuicCryptoServerConfig::TESTING, | 
|  | quic::QuicRandom::GetInstance(), | 
|  | quic::test::crypto_test_utils::ProofSourceForTesting(), | 
|  | quic::KeyExchangeSource::Default()), | 
|  | compressed_certs_cache_( | 
|  | quic::QuicCompressedCertsCache::kQuicCompressedCertsCacheSize) { | 
|  | session_for_actual_ = GenSimpleServerSession(); | 
|  | session_for_expected_ = GenSimpleServerSession(); | 
|  | } | 
|  |  | 
|  | QuicSocketDataProvider::~QuicSocketDataProvider() = default; | 
|  |  | 
|  | std::unique_ptr<quic::QuicSimpleServerSession> | 
|  | QuicSocketDataProvider::GenSimpleServerSession() { | 
|  | quic::test::MockQuicConnection* connection = | 
|  | new quic::test::MockQuicConnection(  // IN-TEST | 
|  | &helper_, &alarm_factory_, quic::Perspective::IS_SERVER); | 
|  | connection->AdvanceTime(quic::QuicTime::Delta::FromSeconds(1)); | 
|  | connection->SetEncrypter( | 
|  | quic::ENCRYPTION_FORWARD_SECURE, | 
|  | std::make_unique<quic::NullEncrypter>(connection->perspective())); | 
|  |  | 
|  | std::unique_ptr<QuicSimpleServerSessionForTest> session = | 
|  | std::make_unique<QuicSimpleServerSessionForTest>( | 
|  | config_, quic::CurrentSupportedVersions(), connection, &owner_, | 
|  | &stream_helper_, &crypto_config_, &compressed_certs_cache_, | 
|  | &memory_cache_backend_); | 
|  | session->Initialize(); | 
|  | return session; | 
|  | } | 
|  |  | 
|  | std::string QuicSocketDataProvider::PrintWithQuicSession( | 
|  | quic::QuicSimpleServerSession* session, | 
|  | std::string data) { | 
|  | std::ostringstream output; | 
|  | quic::HttpStreamPrinter printer(&output); | 
|  | session->set_debug_visitor(&printer); | 
|  | std::string res = printer_.PrintWithQuicSession(data, output, session); | 
|  | session->set_debug_visitor(nullptr); | 
|  | return res; | 
|  | } | 
|  |  | 
|  | QuicSocketDataProvider::Expectation& QuicSocketDataProvider::AddRead( | 
|  | std::string name, | 
|  | std::unique_ptr<quic::QuicEncryptedPacket> packet) { | 
|  | expectations_.push_back(Expectation(std::move(name), Expectation::Type::READ, | 
|  | OK, std::move(packet))); | 
|  | return expectations_.back(); | 
|  | } | 
|  |  | 
|  | QuicSocketDataProvider::Expectation& QuicSocketDataProvider::AddRead( | 
|  | std::string name, | 
|  | std::unique_ptr<quic::QuicReceivedPacket> packet) { | 
|  | uint8_t tos_byte = static_cast<uint8_t>(packet->ecn_codepoint()); | 
|  | return AddRead(std::move(name), | 
|  | static_cast<std::unique_ptr<quic::QuicEncryptedPacket>>( | 
|  | std::move(packet))) | 
|  | .TosByte(tos_byte); | 
|  | } | 
|  |  | 
|  | QuicSocketDataProvider::Expectation& QuicSocketDataProvider::AddReadError( | 
|  | std::string name, | 
|  | int rv) { | 
|  | CHECK_NE(rv, OK); | 
|  | CHECK_NE(rv, ERR_IO_PENDING); | 
|  | expectations_.push_back( | 
|  | Expectation(std::move(name), Expectation::Type::READ, rv, nullptr)); | 
|  | return expectations_.back(); | 
|  | } | 
|  |  | 
|  | QuicSocketDataProvider::Expectation& QuicSocketDataProvider::AddWrite( | 
|  | std::string name, | 
|  | std::unique_ptr<quic::QuicEncryptedPacket> packet, | 
|  | int rv) { | 
|  | expectations_.push_back(Expectation(std::move(name), Expectation::Type::WRITE, | 
|  | rv, std::move(packet))); | 
|  | return expectations_.back(); | 
|  | } | 
|  |  | 
|  | QuicSocketDataProvider::Expectation& QuicSocketDataProvider::AddWriteError( | 
|  | std::string name, | 
|  | int rv) { | 
|  | CHECK_NE(rv, OK); | 
|  | CHECK_NE(rv, ERR_IO_PENDING); | 
|  | expectations_.push_back( | 
|  | Expectation(std::move(name), Expectation::Type::WRITE, rv, nullptr)); | 
|  | return expectations_.back(); | 
|  | } | 
|  |  | 
|  | QuicSocketDataProvider::PausePoint QuicSocketDataProvider::AddPause( | 
|  | std::string name) { | 
|  | expectations_.push_back( | 
|  | Expectation(std::move(name), Expectation::Type::PAUSE, OK, nullptr)); | 
|  | return expectations_.size() - 1; | 
|  | } | 
|  |  | 
|  | bool QuicSocketDataProvider::AllDataConsumed() const { | 
|  | return std::all_of( | 
|  | expectations_.begin(), expectations_.end(), | 
|  | [](const Expectation& expectation) { return expectation.consumed(); }); | 
|  | } | 
|  |  | 
|  | void QuicSocketDataProvider::RunUntilPause( | 
|  | QuicSocketDataProvider::PausePoint pause_point) { | 
|  | if (!paused_at_.has_value()) { | 
|  | run_until_run_loop_ = std::make_unique<base::RunLoop>(); | 
|  | run_until_run_loop_->Run(); | 
|  | run_until_run_loop_.reset(); | 
|  | } | 
|  | CHECK(paused_at_.has_value() && *paused_at_ == pause_point) | 
|  | << "Did not pause at '" << expectations_[pause_point].name() << "'."; | 
|  | } | 
|  |  | 
|  | void QuicSocketDataProvider::Resume() { | 
|  | CHECK(paused_at_.has_value()); | 
|  | VLOG(1) << "Resuming from pause point " << expectations_[*paused_at_].name(); | 
|  | expectations_[*paused_at_].Consume(); | 
|  | paused_at_ = std::nullopt; | 
|  | ExpectationConsumed(); | 
|  | } | 
|  |  | 
|  | void QuicSocketDataProvider::RunUntilAllConsumed() { | 
|  | if (!AllDataConsumed()) { | 
|  | run_until_run_loop_ = std::make_unique<base::RunLoop>(); | 
|  | run_until_run_loop_->Run(); | 
|  | run_until_run_loop_.reset(); | 
|  | } | 
|  |  | 
|  | // If that run timed out, then there will still be un-consumed data. | 
|  | if (!AllDataConsumed()) { | 
|  | std::vector<size_t> unconsumed; | 
|  | for (size_t i = 0; i < expectations_.size(); i++) { | 
|  | if (!expectations_[i].consumed()) { | 
|  | unconsumed.push_back(i); | 
|  | } | 
|  | } | 
|  | FAIL() << "All expectations were not consumed; remaining: " | 
|  | << ExpectationList(unconsumed); | 
|  | } | 
|  | } | 
|  |  | 
|  | MockRead QuicSocketDataProvider::OnRead() { | 
|  | CHECK(!read_pending_); | 
|  | read_pending_ = true; | 
|  | std::optional<MockRead> next_read = ConsumeNextRead(); | 
|  | if (!next_read.has_value()) { | 
|  | return MockRead(ASYNC, ERR_IO_PENDING); | 
|  | } | 
|  |  | 
|  | read_pending_ = false; | 
|  | return *next_read; | 
|  | } | 
|  |  | 
|  | MockWriteResult QuicSocketDataProvider::OnWrite(const std::string& data) { | 
|  | CHECK(!write_pending_.has_value()); | 
|  | write_pending_ = data; | 
|  | std::optional<MockWriteResult> next_write = ConsumeNextWrite(); | 
|  | if (!next_write.has_value()) { | 
|  | // If Write() was called when no corresponding expectation exists, that's | 
|  | // an error unless execution is currently paused, in which case it's just | 
|  | // pending. This rarely occurs because the only other type of expectation | 
|  | // that might be blocking a WRITE is a READ, and QUIC implementations | 
|  | // typically eagerly consume READs. | 
|  | if (paused_at_.has_value()) { | 
|  | return MockWriteResult(ASYNC, ERR_IO_PENDING); | 
|  | } else { | 
|  | ADD_FAILURE() << "Write call when none is expected:\n" | 
|  | << printer_.PrintWrite(data); | 
|  | return MockWriteResult(SYNCHRONOUS, ERR_UNEXPECTED); | 
|  | } | 
|  | } | 
|  |  | 
|  | write_pending_ = std::nullopt; | 
|  | return *next_write; | 
|  | } | 
|  |  | 
|  | bool QuicSocketDataProvider::AllReadDataConsumed() const { | 
|  | return AllDataConsumed(); | 
|  | } | 
|  |  | 
|  | bool QuicSocketDataProvider::AllWriteDataConsumed() const { | 
|  | return AllDataConsumed(); | 
|  | } | 
|  |  | 
|  | void QuicSocketDataProvider::CancelPendingRead() { | 
|  | read_pending_ = false; | 
|  | } | 
|  |  | 
|  | void QuicSocketDataProvider::Reset() { | 
|  | // Note that `Reset` is a parent-class method with a confusing name. It is | 
|  | // used to initialize the socket data provider before it is used. | 
|  |  | 
|  | // Map names to index, and incidentally check for duplicate names. | 
|  | std::map<std::string, size_t> names; | 
|  | for (size_t i = 0; i < expectations_.size(); i++) { | 
|  | Expectation& expectation = expectations_[i]; | 
|  | auto [_, inserted] = names.insert({expectation.name(), i}); | 
|  | CHECK(inserted) << "Another expectation named " << expectation.name() | 
|  | << " exists."; | 
|  | } | 
|  |  | 
|  | // Calculate `dependencies_` mapping indices in `expectations_` to indices | 
|  | // of the expectations they depend on. | 
|  | dependencies_.clear(); | 
|  | for (size_t i = 0; i < expectations_.size(); i++) { | 
|  | Expectation& expectation = expectations_[i]; | 
|  | if (expectation.after().empty()) { | 
|  | // If no other dependencies are given, make the expectation depend on | 
|  | // the previous expectation. | 
|  | if (i > 0) { | 
|  | dependencies_[i].insert(i - 1); | 
|  | } | 
|  | } else { | 
|  | for (auto& after : expectation.after()) { | 
|  | const auto dep = names.find(after); | 
|  | CHECK(dep != names.end()) << "No expectation named " << after; | 
|  | dependencies_[i].insert(dep->second); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | pending_maybe_consume_expectations_ = false; | 
|  | read_pending_ = false; | 
|  | write_pending_ = std::nullopt; | 
|  | MaybeConsumeExpectations(); | 
|  | } | 
|  |  | 
|  | std::optional<size_t> QuicSocketDataProvider::FindReadyExpectations( | 
|  | Expectation::Type type) { | 
|  | std::vector<size_t> matches; | 
|  | for (size_t i = 0; i < expectations_.size(); i++) { | 
|  | const Expectation& expectation = expectations_[i]; | 
|  | if (expectation.consumed() || expectation.type() != type) { | 
|  | continue; | 
|  | } | 
|  | bool found_unconsumed = false; | 
|  | for (auto dep : dependencies_[i]) { | 
|  | if (!expectations_[dep].consumed_) { | 
|  | found_unconsumed = true; | 
|  | break; | 
|  | } | 
|  | } | 
|  | if (!found_unconsumed) { | 
|  | matches.push_back(i); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (matches.size() > 1) { | 
|  | std::string exp_type = Expectation::TypeToString(type); | 
|  | std::string names = ExpectationList(matches); | 
|  | CHECK(matches.size() <= 1) | 
|  | << "Multiple expectations of type " << exp_type | 
|  | << " are ready: " << names << ". Use .After() to disambiguate."; | 
|  | } | 
|  |  | 
|  | return matches.empty() ? std::nullopt : std::make_optional(matches[0]); | 
|  | } | 
|  |  | 
|  | std::optional<MockRead> QuicSocketDataProvider::ConsumeNextRead() { | 
|  | CHECK(read_pending_); | 
|  | std::optional<size_t> ready = FindReadyExpectations(Expectation::Type::READ); | 
|  | if (!ready.has_value()) { | 
|  | return std::nullopt; | 
|  | } | 
|  |  | 
|  | // If there's exactly one matching expectation, return it. | 
|  | Expectation& ready_expectation = expectations_[*ready]; | 
|  | MockRead read(ready_expectation.mode(), ready_expectation.rv()); | 
|  | if (ready_expectation.packet()) { | 
|  | read.data = ready_expectation.packet()->AsStringPiece(); | 
|  | } | 
|  | read.tos = ready_expectation.tos_byte(); | 
|  | ready_expectation.Consume(); | 
|  | ExpectationConsumed(); | 
|  | return read; | 
|  | } | 
|  |  | 
|  | std::optional<MockWriteResult> QuicSocketDataProvider::ConsumeNextWrite() { | 
|  | CHECK(write_pending_.has_value()); | 
|  | std::optional<size_t> ready = FindReadyExpectations(Expectation::Type::WRITE); | 
|  | if (!ready.has_value()) { | 
|  | return std::nullopt; | 
|  | } | 
|  |  | 
|  | // If there's exactly one matching expectation, check if it matches the | 
|  | // write and return it. | 
|  | Expectation& ready_expectation = expectations_[*ready]; | 
|  | if (ready_expectation.packet()) { | 
|  | if (!VerifyWriteData(ready_expectation)) { | 
|  | return MockWriteResult(SYNCHRONOUS, ERR_UNEXPECTED); | 
|  | } | 
|  | } | 
|  | MockWriteResult write(ready_expectation.mode(), | 
|  | ready_expectation.packet() | 
|  | ? ready_expectation.packet()->length() | 
|  | : ready_expectation.rv()); | 
|  | ready_expectation.Consume(); | 
|  | ExpectationConsumed(); | 
|  | return write; | 
|  | } | 
|  |  | 
|  | void QuicSocketDataProvider::MaybeConsumeExpectations() { | 
|  | pending_maybe_consume_expectations_ = false; | 
|  | if (read_pending_) { | 
|  | std::optional<MockRead> next_read = ConsumeNextRead(); | 
|  | if (next_read.has_value()) { | 
|  | read_pending_ = false; | 
|  | if (socket()) { | 
|  | socket()->OnReadComplete(*next_read); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if (write_pending_.has_value()) { | 
|  | std::optional<MockWriteResult> next_write = ConsumeNextWrite(); | 
|  | if (next_write.has_value()) { | 
|  | write_pending_ = std::nullopt; | 
|  | if (socket()) { | 
|  | socket()->OnWriteComplete(next_write->result); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if (!paused_at_) { | 
|  | std::optional<size_t> ready = | 
|  | FindReadyExpectations(Expectation::Type::PAUSE); | 
|  | if (ready.has_value()) { | 
|  | VLOG(1) << "Pausing at " << expectations_[*ready].name(); | 
|  | paused_at_ = *ready; | 
|  | if (run_until_run_loop_) { | 
|  | run_until_run_loop_->Quit(); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if (run_until_run_loop_ && AllDataConsumed()) { | 
|  | run_until_run_loop_->Quit(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void QuicSocketDataProvider::ExpectationConsumed() { | 
|  | if (pending_maybe_consume_expectations_) { | 
|  | return; | 
|  | } | 
|  | pending_maybe_consume_expectations_ = true; | 
|  |  | 
|  | // Call `MaybeConsumeExpectations` in a task. That method may trigger | 
|  | // consumption of other expectations, and that consumption must happen | 
|  | // _after_ the current call to `Read` or `Write` has finished. | 
|  | base::SequencedTaskRunner::GetCurrentDefault()->PostTask( | 
|  | FROM_HERE, | 
|  | base::BindOnce(&QuicSocketDataProvider::MaybeConsumeExpectations, | 
|  | weak_factory_.GetWeakPtr())); | 
|  | } | 
|  |  | 
|  | bool QuicSocketDataProvider::VerifyWriteData( | 
|  | QuicSocketDataProvider::Expectation& expectation) { | 
|  | std::string expected_data(expectation.packet()->data(), | 
|  | expectation.packet()->length()); | 
|  | std::string& actual_data = *write_pending_; | 
|  | actual_log_.append( | 
|  | PrintWithQuicSession(session_for_actual_.get(), actual_data)); | 
|  | expected_log_.append( | 
|  | PrintWithQuicSession(session_for_expected_.get(), expected_data)); | 
|  |  | 
|  | bool write_matches = actual_data == expected_data; | 
|  | EXPECT_TRUE(write_matches) | 
|  | << "Expectation '" << expectation.name() | 
|  | << "' not met. Actual formatted write data:\n" | 
|  | << actual_log_ << "But expectation '" << expectation.name() | 
|  | << "' expected formatted write data:\n" | 
|  | << expected_log_ << "Actual raw write data:\n" | 
|  | << HexDump(actual_data) << "Expected raw write data:\n" | 
|  | << HexDump(expected_data); | 
|  | return write_matches; | 
|  | } | 
|  |  | 
|  | std::string QuicSocketDataProvider::ExpectationList( | 
|  | const std::vector<size_t>& indices) { | 
|  | std::ostringstream names; | 
|  | bool first = true; | 
|  | for (auto i : indices) { | 
|  | names << (first ? "" : ", ") << expectations_[i].name(); | 
|  | first = false; | 
|  | } | 
|  | return names.str(); | 
|  | } | 
|  |  | 
|  | }  // namespace net::test |