diff options
Diffstat (limited to 'chromium/net/tools/quic')
98 files changed, 3683 insertions, 2237 deletions
diff --git a/chromium/net/tools/quic/chlo_extractor.cc b/chromium/net/tools/quic/chlo_extractor.cc index 55e68a09ccf..3e5ce787f7a 100644 --- a/chromium/net/tools/quic/chlo_extractor.cc +++ b/chromium/net/tools/quic/chlo_extractor.cc @@ -4,12 +4,13 @@ #include "net/tools/quic/chlo_extractor.h" -#include "net/quic/crypto/crypto_framer.h" -#include "net/quic/crypto/crypto_handshake_message.h" -#include "net/quic/crypto/crypto_protocol.h" -#include "net/quic/crypto/quic_decrypter.h" -#include "net/quic/crypto/quic_encrypter.h" -#include "net/quic/quic_framer.h" +#include "base/strings/string_util.h" +#include "net/quic/core/crypto/crypto_framer.h" +#include "net/quic/core/crypto/crypto_handshake_message.h" +#include "net/quic/core/crypto/crypto_protocol.h" +#include "net/quic/core/crypto/quic_decrypter.h" +#include "net/quic/core/crypto/quic_encrypter.h" +#include "net/quic/core/quic_framer.h" using base::StringPiece; @@ -92,7 +93,7 @@ bool ChloFramerVisitor::OnPacketHeader(const QuicPacketHeader& header) { bool ChloFramerVisitor::OnStreamFrame(const QuicStreamFrame& frame) { StringPiece data(frame.data_buffer, frame.data_length); if (frame.stream_id == kCryptoStreamId && frame.offset == 0 && - data.starts_with("CHLO")) { + base::StartsWith(data, "CHLO", base::CompareCase::INSENSITIVE_ASCII)) { CryptoFramer crypto_framer; crypto_framer.set_visitor(this); if (!crypto_framer.ProcessInput(data)) { @@ -148,7 +149,9 @@ void ChloFramerVisitor::OnError(CryptoFramer* framer) {} void ChloFramerVisitor::OnHandshakeMessage( const CryptoHandshakeMessage& message) { - delegate_->OnChlo(framer_->version(), connection_id_, message); + if (delegate_ != nullptr) { + delegate_->OnChlo(framer_->version(), connection_id_, message); + } found_chlo_ = true; } diff --git a/chromium/net/tools/quic/chlo_extractor.h b/chromium/net/tools/quic/chlo_extractor.h index eefaae3f51d..7820bb596ca 100644 --- a/chromium/net/tools/quic/chlo_extractor.h +++ b/chromium/net/tools/quic/chlo_extractor.h @@ -5,8 +5,8 @@ #ifndef NET_TOOLS_QUIC_CHLO_EXTRACTOR_H_ #define NET_TOOLS_QUIC_CHLO_EXTRACTOR_H_ -#include "net/quic/crypto/crypto_handshake_message.h" -#include "net/quic/quic_protocol.h" +#include "net/quic/core/crypto/crypto_handshake_message.h" +#include "net/quic/core/quic_protocol.h" namespace net { @@ -31,7 +31,7 @@ class ChloExtractor { const QuicVersionVector& versions, Delegate* delegate); - ChloExtractor(ChloExtractor&) = delete; + ChloExtractor(const ChloExtractor&) = delete; ChloExtractor operator=(const ChloExtractor&) = delete; }; diff --git a/chromium/net/tools/quic/chlo_extractor_test.cc b/chromium/net/tools/quic/chlo_extractor_test.cc index 320df5ae390..1be18208c91 100644 --- a/chromium/net/tools/quic/chlo_extractor_test.cc +++ b/chromium/net/tools/quic/chlo_extractor_test.cc @@ -4,7 +4,7 @@ #include "net/tools/quic/chlo_extractor.h" -#include "net/quic/quic_framer.h" +#include "net/quic/core/quic_framer.h" #include "net/quic/test_tools/crypto_test_utils.h" #include "net/quic/test_tools/quic_test_utils.h" @@ -48,7 +48,7 @@ class ChloExtractorTest : public ::testing::Test { header_.public_header.connection_id_length = PACKET_8BYTE_CONNECTION_ID; header_.public_header.version_flag = true; header_.public_header.versions = - SupportedVersions(QuicSupportedVersions().front()); + SupportedVersions(AllSupportedVersions().front()); header_.public_header.reset_flag = false; header_.public_header.packet_number_length = PACKET_6BYTE_PACKET_NUMBER; header_.packet_number = 1; @@ -88,7 +88,7 @@ TEST_F(ChloExtractorTest, FindsValidChlo) { string client_hello_str( client_hello.GetSerialized().AsStringPiece().as_string()); // Construct a CHLO with each supported version - for (QuicVersion version : QuicSupportedVersions()) { + for (QuicVersion version : AllSupportedVersions()) { QuicVersionVector versions(SupportedVersions(version)); header_.public_header.versions = versions; MakePacket( @@ -111,7 +111,7 @@ TEST_F(ChloExtractorTest, DoesNotFindValidChloOnWrongStream) { MakePacket( new QuicStreamFrame(kCryptoStreamId + 1, false, 0, client_hello_str)); EXPECT_FALSE( - ChloExtractor::Extract(*packet_, QuicSupportedVersions(), &delegate_)); + ChloExtractor::Extract(*packet_, AllSupportedVersions(), &delegate_)); } TEST_F(ChloExtractorTest, DoesNotFindValidChloOnWrongOffset) { @@ -122,13 +122,13 @@ TEST_F(ChloExtractorTest, DoesNotFindValidChloOnWrongOffset) { client_hello.GetSerialized().AsStringPiece().as_string()); MakePacket(new QuicStreamFrame(kCryptoStreamId, false, 1, client_hello_str)); EXPECT_FALSE( - ChloExtractor::Extract(*packet_, QuicSupportedVersions(), &delegate_)); + ChloExtractor::Extract(*packet_, AllSupportedVersions(), &delegate_)); } TEST_F(ChloExtractorTest, DoesNotFindInvalidChlo) { MakePacket(new QuicStreamFrame(kCryptoStreamId, false, 0, "foo")); EXPECT_FALSE( - ChloExtractor::Extract(*packet_, QuicSupportedVersions(), &delegate_)); + ChloExtractor::Extract(*packet_, AllSupportedVersions(), &delegate_)); } } // namespace diff --git a/chromium/net/tools/quic/crypto_message_printer_bin.cc b/chromium/net/tools/quic/crypto_message_printer_bin.cc index e6813c4a3c6..11eb8f83410 100644 --- a/chromium/net/tools/quic/crypto_message_printer_bin.cc +++ b/chromium/net/tools/quic/crypto_message_printer_bin.cc @@ -10,8 +10,8 @@ #include <iostream> #include "base/command_line.h" -#include "net/quic/crypto/crypto_framer.h" -#include "net/quic/quic_utils.h" +#include "net/quic/core/crypto/crypto_framer.h" +#include "net/quic/core/quic_utils.h" using std::cerr; using std::cout; diff --git a/chromium/net/tools/quic/end_to_end_test.cc b/chromium/net/tools/quic/end_to_end_test.cc index 7f4451c94cc..aca08397a96 100644 --- a/chromium/net/tools/quic/end_to_end_test.cc +++ b/chromium/net/tools/quic/end_to_end_test.cc @@ -18,22 +18,24 @@ #include "base/time/time.h" #include "net/base/ip_address.h" #include "net/base/ip_endpoint.h" -#include "net/quic/crypto/aes_128_gcm_12_encrypter.h" -#include "net/quic/crypto/null_encrypter.h" -#include "net/quic/quic_client_session_base.h" -#include "net/quic/quic_flags.h" -#include "net/quic/quic_framer.h" -#include "net/quic/quic_packet_creator.h" -#include "net/quic/quic_protocol.h" -#include "net/quic/quic_server_id.h" -#include "net/quic/quic_session.h" -#include "net/quic/quic_utils.h" +#include "net/quic/core/crypto/aes_128_gcm_12_encrypter.h" +#include "net/quic/core/crypto/null_encrypter.h" +#include "net/quic/core/quic_client_session_base.h" +#include "net/quic/core/quic_flags.h" +#include "net/quic/core/quic_framer.h" +#include "net/quic/core/quic_packet_creator.h" +#include "net/quic/core/quic_protocol.h" +#include "net/quic/core/quic_server_id.h" +#include "net/quic/core/quic_session.h" +#include "net/quic/core/quic_utils.h" #include "net/quic/test_tools/crypto_test_utils.h" +#include "net/quic/test_tools/quic_config_peer.h" #include "net/quic/test_tools/quic_connection_peer.h" #include "net/quic/test_tools/quic_flow_controller_peer.h" #include "net/quic/test_tools/quic_sent_packet_manager_peer.h" #include "net/quic/test_tools/quic_session_peer.h" #include "net/quic/test_tools/quic_spdy_session_peer.h" +#include "net/quic/test_tools/quic_stream_sequencer_peer.h" #include "net/quic/test_tools/quic_test_utils.h" #include "net/quic/test_tools/reliable_quic_stream_peer.h" #include "net/test/gtest_util.h" @@ -47,6 +49,7 @@ #include "net/tools/quic/quic_spdy_client_stream.h" #include "net/tools/quic/test_tools/http_message.h" #include "net/tools/quic/test_tools/packet_dropping_test_writer.h" +#include "net/tools/quic/test_tools/packet_reordering_writer.h" #include "net/tools/quic/test_tools/quic_client_peer.h" #include "net/tools/quic/test_tools/quic_dispatcher_peer.h" #include "net/tools/quic/test_tools/quic_in_memory_cache_peer.h" @@ -71,7 +74,6 @@ using net::test::QuicSentPacketManagerPeer; using net::test::QuicSessionPeer; using net::test::QuicSpdySessionPeer; using net::test::ReliableQuicStreamPeer; -using net::test::ValueRestore; using net::test::kClientDataStreamId1; using net::test::kInitialSessionFlowControlWindowForTest; using net::test::kInitialStreamFlowControlWindowForTest; @@ -98,8 +100,11 @@ struct TestParams { bool client_supports_stateless_rejects, bool server_uses_stateless_rejects_if_peer_supported, QuicTag congestion_control_tag, - bool auto_tune_flow_control_window, - bool disable_hpack_dynamic_table) + bool disable_hpack_dynamic_table, + bool force_hol_blocking, + bool use_cheap_stateless_reject, + bool buffer_packet_till_chlo, + bool small_client_mtu) : client_supported_versions(client_supported_versions), server_supported_versions(server_supported_versions), negotiated_version(negotiated_version), @@ -107,8 +112,11 @@ struct TestParams { server_uses_stateless_rejects_if_peer_supported( server_uses_stateless_rejects_if_peer_supported), congestion_control_tag(congestion_control_tag), - auto_tune_flow_control_window(auto_tune_flow_control_window), - disable_hpack_dynamic_table(disable_hpack_dynamic_table) {} + disable_hpack_dynamic_table(disable_hpack_dynamic_table), + force_hol_blocking(force_hol_blocking), + use_cheap_stateless_reject(use_cheap_stateless_reject), + buffer_packet_till_chlo(buffer_packet_till_chlo), + small_client_mtu(small_client_mtu) {} friend ostream& operator<<(ostream& os, const TestParams& p) { os << "{ server_supported_versions: " @@ -122,9 +130,11 @@ struct TestParams { << p.server_uses_stateless_rejects_if_peer_supported; os << " congestion_control_tag: " << QuicUtils::TagToString(p.congestion_control_tag); - os << " auto_tune_flow_control_window: " << p.auto_tune_flow_control_window; - os << " disable_hpack_dynamic_table: " << p.disable_hpack_dynamic_table - << " }"; + os << " disable_hpack_dynamic_table: " << p.disable_hpack_dynamic_table; + os << " force_hol_blocking: " << p.force_hol_blocking; + os << " use_cheap_stateless_reject: " << p.use_cheap_stateless_reject; + os << " buffer_packet_till_chlo: " << p.buffer_packet_till_chlo; + os << " small_client_mtu: " << p.small_client_mtu << " }"; return os; } @@ -134,8 +144,11 @@ struct TestParams { bool client_supports_stateless_rejects; bool server_uses_stateless_rejects_if_peer_supported; QuicTag congestion_control_tag; - bool auto_tune_flow_control_window; bool disable_hpack_dynamic_table; + bool force_hol_blocking; + bool use_cheap_stateless_reject; + bool buffer_packet_till_chlo; + bool small_client_mtu; }; // Constructs various test permutations. @@ -148,116 +161,157 @@ vector<TestParams> GetTestParams() { // these tests need to ensure that clients are never attempting // to do 0-RTT across incompatible versions. Chromium only supports // a single version at a time anyway. :) - QuicVersionVector all_supported_versions = QuicSupportedVersions(); + QuicVersionVector all_supported_versions = AllSupportedVersions(); QuicVersionVector version_buckets[4]; for (const QuicVersion version : all_supported_versions) { - if (version <= QUIC_VERSION_25) { - // SPDY/4 + if (version <= QUIC_VERSION_30) { + // Versions: 30 + // v26 adds a hash of the expected leaf cert in the XLCT tag. version_buckets[0].push_back(version); } else if (version <= QUIC_VERSION_32) { - // QUIC_VERSION_26 changes the kdf in a way that is incompatible with - // version negotiation across the version 26 boundary. + // Versions: 31-32 + // v31 adds a hash of the CHLO into the proof signature. version_buckets[1].push_back(version); } else if (version <= QUIC_VERSION_33) { - // QUIC_VERSION_33 changes the kdf in a way that is incompatible with - // version negotiation across the version 33 boundary, by using the - // diversification nonce. + // Versions: 33 + // v33 adds a diversification nonce into the hkdf. version_buckets[2].push_back(version); } else { + // Versions: 34+ // QUIC_VERSION_34 deprecates entropy and uses new ack and stop waiting - // wire formats, so it is incompatible with version negotiation across the - // version 34 boundary. + // wire formats. version_buckets[3].push_back(version); } } // This must be kept in sync with the number of nested for-loops below as it // is used to prune the number of tests that are run. - const int kMaxEnabledOptions = 5; + const int kMaxEnabledOptions = 7; int max_enabled_options = 0; vector<TestParams> params; for (bool server_uses_stateless_rejects_if_peer_supported : {true, false}) { for (bool client_supports_stateless_rejects : {true, false}) { for (const QuicTag congestion_control_tag : {kRENO, kQBIC}) { - for (bool auto_tune_flow_control_window : {true, false}) { - for (bool disable_hpack_dynamic_table : {true, false}) { - int enabled_options = 0; - if (congestion_control_tag != kQBIC) { - ++enabled_options; - } - if (auto_tune_flow_control_window) { - ++enabled_options; - } - if (disable_hpack_dynamic_table) { - ++enabled_options; - } - if (client_supports_stateless_rejects) { - ++enabled_options; - } - if (server_uses_stateless_rejects_if_peer_supported) { - ++enabled_options; - } - CHECK_GE(kMaxEnabledOptions, enabled_options); - if (enabled_options > max_enabled_options) { - max_enabled_options = enabled_options; - } - - // Run tests with no options, a single option, or all the options - // enabled to avoid a combinatorial explosion. - if (enabled_options > 1 && enabled_options < kMaxEnabledOptions) { - continue; - } - - for (const QuicVersionVector& client_versions : version_buckets) { - if (client_versions.front() < QUIC_VERSION_30 && - FLAGS_quic_disable_pre_30) { - continue; - } - CHECK(!client_versions.empty()); - // Add an entry for server and client supporting all versions. - params.push_back(TestParams( - client_versions, all_supported_versions, - client_versions.front(), client_supports_stateless_rejects, - server_uses_stateless_rejects_if_peer_supported, - congestion_control_tag, auto_tune_flow_control_window, - disable_hpack_dynamic_table)); - - // Run version negotiation tests tests with no options, or all - // the options enabled to avoid a combinatorial explosion. - if (enabled_options > 0 && enabled_options < kMaxEnabledOptions) { - continue; - } - - // Test client supporting all versions and server supporting 1 - // version. Simulate an old server and exercise version downgrade - // in the client. Protocol negotiation should occur. Skip the i = - // 0 case because it is essentially the same as the default case. - for (size_t i = 1; i < client_versions.size(); ++i) { - if (client_versions[i] < QUIC_VERSION_30 && - FLAGS_quic_disable_pre_30) { - continue; - } - QuicVersionVector server_supported_versions; - server_supported_versions.push_back(client_versions[i]); - params.push_back(TestParams( - client_versions, server_supported_versions, - server_supported_versions.front(), - client_supports_stateless_rejects, - server_uses_stateless_rejects_if_peer_supported, - congestion_control_tag, auto_tune_flow_control_window, - disable_hpack_dynamic_table)); - } - } - } - } - } - } + for (bool disable_hpack_dynamic_table : {false}) { + for (bool force_hol_blocking : {true, false}) { + for (bool use_cheap_stateless_reject : {true, false}) { + for (bool buffer_packet_till_chlo : {true, false}) { + for (bool small_client_mtu : {true, false}) { + if (!buffer_packet_till_chlo && use_cheap_stateless_reject) { + // Doing stateless reject while not buffering packet + // before CHLO is not allowed. + break; + } + int enabled_options = 0; + if (force_hol_blocking) { + ++enabled_options; + } + if (congestion_control_tag != kQBIC) { + ++enabled_options; + } + if (disable_hpack_dynamic_table) { + ++enabled_options; + } + if (client_supports_stateless_rejects) { + ++enabled_options; + } + if (server_uses_stateless_rejects_if_peer_supported) { + ++enabled_options; + } + if (buffer_packet_till_chlo) { + ++enabled_options; + } + if (use_cheap_stateless_reject) { + ++enabled_options; + } + if (small_client_mtu) { + ++enabled_options; + } + CHECK_GE(kMaxEnabledOptions, enabled_options); + if (enabled_options > max_enabled_options) { + max_enabled_options = enabled_options; + } + + // Run tests with no options, a single option, or all the + // options enabled to avoid a combinatorial explosion. + if (enabled_options > 1 && + enabled_options < kMaxEnabledOptions) { + continue; + } + + for (const QuicVersionVector& client_versions : + version_buckets) { + CHECK(!client_versions.empty()); + if (FilterSupportedVersions(client_versions).empty()) { + continue; + } + // Add an entry for server and client supporting all + // versions. + params.push_back(TestParams( + client_versions, all_supported_versions, + client_versions.front(), + client_supports_stateless_rejects, + server_uses_stateless_rejects_if_peer_supported, + congestion_control_tag, disable_hpack_dynamic_table, + force_hol_blocking, use_cheap_stateless_reject, + buffer_packet_till_chlo, small_client_mtu)); + + // Run version negotiation tests tests with no options, or + // all the options enabled to avoid a combinatorial + // explosion. + if (enabled_options > 1 && + enabled_options < kMaxEnabledOptions) { + continue; + } + + // Test client supporting all versions and server supporting + // 1 version. Simulate an old server and exercise version + // downgrade in the client. Protocol negotiation should + // occur. Skip the i = 0 case because it is essentially the + // same as the default case. + for (size_t i = 1; i < client_versions.size(); ++i) { + QuicVersionVector server_supported_versions; + server_supported_versions.push_back(client_versions[i]); + if (FilterSupportedVersions(server_supported_versions) + .empty()) { + continue; + } + params.push_back(TestParams( + client_versions, server_supported_versions, + server_supported_versions.front(), + client_supports_stateless_rejects, + server_uses_stateless_rejects_if_peer_supported, + congestion_control_tag, disable_hpack_dynamic_table, + force_hol_blocking, use_cheap_stateless_reject, + buffer_packet_till_chlo, small_client_mtu)); + } // End of version for loop. + } // End of 2nd version for loop. + } // End of small_client_mtu loop. + } // End of buffer_packet_till_chlo loop. + } // End of use_cheap_stateless_reject for loop. + } // End of force_hol_blocking loop. + } // End of disable_hpack_dynamic_table for loop. + } // End of congestion_control_tag for loop. + } // End of client_supports_stateless_rejects for loop. CHECK_EQ(kMaxEnabledOptions, max_enabled_options); - } + } // End of server_uses_stateless_rejects_if_peer_supported for loop. return params; } +class SmallMtuPacketReader : public QuicPacketReader { + public: + bool ReadAndDispatchPackets(int fd, + int port, + bool potentially_small_mtu, + const QuicClock& clock, + ProcessPacketInterface* processor, + QuicPacketCount* packets_dropped) override { + return QuicPacketReader::ReadAndDispatchPackets(fd, port, true, clock, + processor, packets_dropped); + } +}; + class ServerDelegate : public PacketDroppingTestWriter::Delegate { public: explicit ServerDelegate(QuicDispatcher* dispatcher) @@ -287,7 +341,9 @@ class EndToEndTest : public ::testing::TestWithParam<TestParams> { EndToEndTest() : initialized_(false), server_address_(IPEndPoint(Loopback4(), 0)), - server_hostname_("example.com"), + server_hostname_("test.example.com"), + client_writer_(nullptr), + server_writer_(nullptr), server_started_(false), strike_register_no_startup_period_(false), chlo_multiplier_(0), @@ -328,10 +384,14 @@ class EndToEndTest : public ::testing::TestWithParam<TestParams> { QuicInMemoryCachePeer::ResetForTests(); } + virtual void CreateClientWithWriter() { + client_.reset(CreateQuicClient(client_writer_)); + } + QuicTestClient* CreateQuicClient(QuicPacketWriterWrapper* writer) { - QuicTestClient* client = - new QuicTestClient(server_address_, server_hostname_, client_config_, - client_supported_versions_); + QuicTestClient* client = new QuicTestClient( + server_address_, server_hostname_, client_config_, + client_supported_versions_, CryptoTestUtils::ProofVerifierForTesting()); client->UseWriter(writer); client->Connect(); return client; @@ -395,29 +455,29 @@ class EndToEndTest : public ::testing::TestWithParam<TestParams> { if (GetParam().client_supports_stateless_rejects) { copt.push_back(kSREJ); } - if (GetParam().auto_tune_flow_control_window) { - copt.push_back(kAFCW); - copt.push_back(kIFW5); - } if (GetParam().disable_hpack_dynamic_table) { copt.push_back(kDHDT); } + if (GetParam().force_hol_blocking) { + client_config_.SetForceHolBlocking(); + } client_config_.SetConnectionOptionsToSend(copt); // Start the server first, because CreateQuicClient() attempts // to connect to the server. StartServer(); - client_.reset(CreateQuicClient(client_writer_)); + CreateClientWithWriter(); static EpollEvent event(EPOLLOUT, false); - client_writer_->Initialize( - reinterpret_cast<QuicEpollConnectionHelper*>( - QuicConnectionPeer::GetHelper( - client_->client()->session()->connection())), - QuicConnectionPeer::GetAlarmFactory( - client_->client()->session()->connection()), - new ClientDelegate(client_->client())); - + if (client_writer_ != nullptr) { + client_writer_->Initialize( + reinterpret_cast<QuicEpollConnectionHelper*>( + QuicConnectionPeer::GetHelper( + client_->client()->session()->connection())), + QuicConnectionPeer::GetAlarmFactory( + client_->client()->session()->connection()), + new ClientDelegate(client_->client())); + } initialized_ = true; return client_->client()->connected(); } @@ -436,10 +496,22 @@ class EndToEndTest : public ::testing::TestWithParam<TestParams> { } void StartServer() { - server_thread_.reset(new ServerThread( + FLAGS_quic_buffer_packet_till_chlo = GetParam().buffer_packet_till_chlo; + FLAGS_quic_use_cheap_stateless_rejects = + GetParam().use_cheap_stateless_reject; + if (!FLAGS_quic_buffer_packet_till_chlo) { + FLAGS_quic_limit_num_new_sessions_per_epoll_loop = false; + } + auto test_server = new QuicTestServer(CryptoTestUtils::ProofSourceForTesting(), - server_config_, server_supported_versions_), - server_address_, strike_register_no_startup_period_)); + server_config_, server_supported_versions_); + if (GetParam().small_client_mtu) { + FLAGS_quic_enforce_mtu_limit = true; + QuicServerPeer::SetReader(test_server, new SmallMtuPacketReader); + server_writer_->set_max_allowed_packet_size(kMinimumSupportedPacketSize); + } + server_thread_.reset(new ServerThread(test_server, server_address_, + strike_register_no_startup_period_)); if (chlo_multiplier_ != 0) { server_thread_->server()->SetChloMultiplier(chlo_multiplier_); } @@ -475,7 +547,7 @@ class EndToEndTest : public ::testing::TestWithParam<TestParams> { } void AddToCache(StringPiece path, int response_code, StringPiece body) { - QuicInMemoryCache::GetInstance()->AddSimpleResponse("www.google.com", path, + QuicInMemoryCache::GetInstance()->AddSimpleResponse(server_hostname_, path, response_code, body); } @@ -509,7 +581,7 @@ class EndToEndTest : public ::testing::TestWithParam<TestParams> { void VerifyCleanConnection(bool had_packet_loss) { QuicConnectionStats client_stats = client_->client()->session()->connection()->GetStats(); - if (FLAGS_quic_reply_to_rej && !had_packet_loss) { + if (!had_packet_loss) { EXPECT_EQ(0u, client_stats.packets_lost); } EXPECT_EQ(0u, client_stats.packets_discarded); @@ -534,7 +606,7 @@ class EndToEndTest : public ::testing::TestWithParam<TestParams> { ASSERT_EQ(1u, dispatcher->session_map().size()); QuicSession* session = dispatcher->session_map().begin()->second; QuicConnectionStats server_stats = session->connection()->GetStats(); - if (FLAGS_quic_reply_to_rej && !had_packet_loss) { + if (!had_packet_loss) { EXPECT_EQ(0u, server_stats.packets_lost); } EXPECT_EQ(0u, server_stats.packets_discarded); @@ -562,6 +634,7 @@ class EndToEndTest : public ::testing::TestWithParam<TestParams> { stream_factory_ = factory; } + QuicFlagSaver flags_; // Save/restore all QUIC flag values. bool initialized_; IPEndPoint server_address_; string server_hostname_; @@ -579,6 +652,7 @@ class EndToEndTest : public ::testing::TestWithParam<TestParams> { size_t chlo_multiplier_; QuicTestServer::StreamFactory* stream_factory_; bool support_server_push_; + bool force_hol_blocking_; }; // Run all end to end tests with all supported versions. @@ -586,6 +660,27 @@ INSTANTIATE_TEST_CASE_P(EndToEndTests, EndToEndTest, ::testing::ValuesIn(GetTestParams())); +TEST_P(EndToEndTest, HandshakeSuccessful) { + ASSERT_TRUE(Initialize()); + client_->client()->WaitForCryptoHandshakeConfirmed(); + QuicCryptoStream* crypto_stream = + QuicSessionPeer::GetCryptoStream(client_->client()->session()); + QuicStreamSequencer* sequencer = + ReliableQuicStreamPeer::sequencer(crypto_stream); + EXPECT_NE(FLAGS_quic_release_crypto_stream_buffer && + FLAGS_quic_reduce_sequencer_buffer_memory_life_time, + QuicStreamSequencerPeer::IsUnderlyingBufferAllocated(sequencer)); + server_thread_->Pause(); + QuicDispatcher* dispatcher = + QuicServerPeer::GetDispatcher(server_thread_->server()); + QuicSession* server_session = dispatcher->session_map().begin()->second; + crypto_stream = QuicSessionPeer::GetCryptoStream(server_session); + sequencer = ReliableQuicStreamPeer::sequencer(crypto_stream); + EXPECT_NE(FLAGS_quic_release_crypto_stream_buffer && + FLAGS_quic_reduce_sequencer_buffer_memory_life_time, + QuicStreamSequencerPeer::IsUnderlyingBufferAllocated(sequencer)); +} + TEST_P(EndToEndTest, SimpleRequestResponse) { ASSERT_TRUE(Initialize()); @@ -1161,7 +1256,7 @@ TEST_P(EndToEndTest, DISABLED_MultipleTermination) { ReliableQuicStreamPeer::SetWriteSideClosed(false, client_->GetOrCreateStream()); - EXPECT_DFATAL(client_->SendData("eep", true), "Fin already buffered"); + EXPECT_QUIC_BUG(client_->SendData("eep", true), "Fin already buffered"); } TEST_P(EndToEndTest, Timeout) { @@ -1203,15 +1298,9 @@ TEST_P(EndToEndTest, NegotiateMaxOpenStreams) { } client_->WaitForResponse(); - if (negotiated_version_ <= QUIC_VERSION_27) { - EXPECT_FALSE(client_->connected()); - EXPECT_EQ(QUIC_STREAM_CONNECTION_ERROR, client_->stream_error()); - EXPECT_EQ(QUIC_TOO_MANY_OPEN_STREAMS, client_->connection_error()); - } else { - EXPECT_TRUE(client_->connected()); - EXPECT_EQ(QUIC_REFUSED_STREAM, client_->stream_error()); - EXPECT_EQ(QUIC_NO_ERROR, client_->connection_error()); - } + EXPECT_TRUE(client_->connected()); + EXPECT_EQ(QUIC_REFUSED_STREAM, client_->stream_error()); + EXPECT_EQ(QUIC_NO_ERROR, client_->connection_error()); } TEST_P(EndToEndTest, MaxIncomingDynamicStreamsLimitRespected) { @@ -1279,7 +1368,7 @@ TEST_P(EndToEndTest, SetIndependentMaxIncomingDynamicStreamsLimits) { } TEST_P(EndToEndTest, NegotiateCongestionControl) { - ValueRestore<bool> old_flag(&FLAGS_quic_allow_bbr, true); + FLAGS_quic_allow_bbr = true; // Disable this flag because if connection uses multipath sent packet manager, // static_cast here does not work. FLAGS_quic_enable_multipath = false; @@ -1292,7 +1381,9 @@ TEST_P(EndToEndTest, NegotiateCongestionControl) { expected_congestion_control_type = kReno; break; case kTBBR: - expected_congestion_control_type = kBBR; + // TODO(vasilvv): switch this back to kBBR when new BBR implementation is + // in. + expected_congestion_control_type = kCubic; break; case kQBIC: expected_congestion_control_type = kCubic; @@ -1301,11 +1392,13 @@ TEST_P(EndToEndTest, NegotiateCongestionControl) { DLOG(FATAL) << "Unexpected congestion control tag"; } + server_thread_->Pause(); EXPECT_EQ(expected_congestion_control_type, QuicSentPacketManagerPeer::GetSendAlgorithm( *static_cast<const QuicSentPacketManager*>( GetSentPacketManagerFromFirstServerSession())) ->GetCongestionControlType()); + server_thread_->Resume(); } TEST_P(EndToEndTest, LimitMaxOpenStreams) { @@ -1619,10 +1712,8 @@ TEST_P(EndToEndTest, DifferentFlowControlWindows) { set_client_initial_stream_flow_control_receive_window(kClientStreamIFCW); set_client_initial_session_flow_control_receive_window(kClientSessionIFCW); - uint32_t kServerStreamIFCW = - GetParam().auto_tune_flow_control_window ? 32 * 1024 : 654321; - uint32_t kServerSessionIFCW = - GetParam().auto_tune_flow_control_window ? 48 * 1024 : 765432; + uint32_t kServerStreamIFCW = 32 * 1024; + uint32_t kServerSessionIFCW = 48 * 1024; set_server_initial_stream_flow_control_receive_window(kServerStreamIFCW); set_server_initial_session_flow_control_receive_window(kServerSessionIFCW); @@ -1670,10 +1761,8 @@ TEST_P(EndToEndTest, DifferentFlowControlWindows) { TEST_P(EndToEndTest, HeadersAndCryptoStreamsNoConnectionFlowControl) { // The special headers and crypto streams should be subject to per-stream flow // control limits, but should not be subject to connection level flow control - const uint32_t kStreamIFCW = - GetParam().auto_tune_flow_control_window ? 32 * 1024 : 123456; - const uint32_t kSessionIFCW = - GetParam().auto_tune_flow_control_window ? 48 * 1024 : 234567; + const uint32_t kStreamIFCW = 32 * 1024; + const uint32_t kSessionIFCW = 48 * 1024; set_client_initial_stream_flow_control_receive_window(kStreamIFCW); set_client_initial_session_flow_control_receive_window(kSessionIFCW); set_server_initial_stream_flow_control_receive_window(kStreamIFCW); @@ -1801,10 +1890,9 @@ class TestAckListener : public QuicAckListenerInterface { class TestResponseListener : public QuicClient::ResponseListener { public: void OnCompleteResponse(QuicStreamId id, - const BalsaHeaders& response_headers, + const SpdyHeaderBlock& response_headers, const string& response_body) override { - string debug_string; - response_headers.DumpHeadersToString(&debug_string); + string debug_string = response_headers.DebugString(); DVLOG(1) << "response for stream " << id << " " << debug_string << "\n" << response_body; } @@ -1894,6 +1982,8 @@ TEST_P(EndToEndTest, ServerSendPublicReset) { TEST_P(EndToEndTest, ServerSendPublicResetWithDifferentConnectionId) { ASSERT_TRUE(Initialize()); + client_->client()->WaitForCryptoHandshakeConfirmed(); + // Send the public reset. QuicConnectionId incorrect_connection_id = client_->client()->session()->connection()->connection_id() + 1; @@ -1957,6 +2047,8 @@ TEST_P(EndToEndTest, ClientSendPublicResetWithDifferentConnectionId) { TEST_P(EndToEndTest, ServerSendVersionNegotiationWithDifferentConnectionId) { ASSERT_TRUE(Initialize()); + client_->client()->WaitForCryptoHandshakeConfirmed(); + // Send the version negotiation packet. QuicConnectionId incorrect_connection_id = client_->client()->session()->connection()->connection_id() + 1; @@ -2098,7 +2190,8 @@ class ServerStreamWithErrorResponseBody : public QuicSimpleServerStream { ServerStreamWithErrorResponseBody(QuicStreamId id, QuicSpdySession* session, string response_body) - : QuicSimpleServerStream(id, session), response_body_(response_body) {} + : QuicSimpleServerStream(id, session), + response_body_(std::move(response_body)) {} ~ServerStreamWithErrorResponseBody() override {} @@ -2120,7 +2213,7 @@ class ServerStreamWithErrorResponseBody : public QuicSimpleServerStream { class StreamWithErrorFactory : public QuicTestServer::StreamFactory { public: explicit StreamWithErrorFactory(string response_body) - : response_body_(response_body) {} + : response_body_(std::move(response_body)) {} ~StreamWithErrorFactory() override {} @@ -2390,7 +2483,6 @@ TEST_P(EndToEndTest, LargePostEarlyResponse) { // POST to a URL that gets an early error response, after the headers are // received and before the body is received. HTTPMessage request(HttpConstants::HTTP_1_1, HttpConstants::POST, "/garbage"); - const uint32_t kBodySize = 2 * kWindowSize; // Invalid content-length so the request will receive an early 500 response. request.AddHeader("content-length", "-1"); request.set_skip_message_validation(true); @@ -2404,28 +2496,11 @@ TEST_P(EndToEndTest, LargePostEarlyResponse) { client_->WaitForInitialResponse(); EXPECT_EQ(500u, client_->response_headers()->parsed_response_code()); - if (negotiated_version_ > QUIC_VERSION_28) { - // Receive the reset stream from server on early response. - client_->WaitForResponseForMs(100); - ReliableQuicStream* stream = - client_->client()->session()->GetOrCreateStream(kClientDataStreamId1); - // The stream is reset by server's reset stream. - EXPECT_EQ(stream, nullptr); - return; - } - - // Send a body larger than the stream flow control window. - string body; - GenerateBody(&body, kBodySize); - client_->SendData(body, true); - - // Run the client to let any buffered data be sent. - // (This is OK despite already waiting for a response.) - client_->WaitForResponse(); - // There should be no buffered data to write in the client's stream. + // Receive the reset stream from server on early response. ReliableQuicStream* stream = client_->client()->session()->GetOrCreateStream(kClientDataStreamId1); - EXPECT_FALSE(stream != nullptr && stream->HasBufferedData()); + // The stream is reset by server's reset stream. + EXPECT_EQ(stream, nullptr); } TEST_P(EndToEndTest, Trailers) { @@ -2449,7 +2524,7 @@ TEST_P(EndToEndTest, Trailers) { trailers["some-trailing-header"] = "trailing-header-value"; QuicInMemoryCache::GetInstance()->AddResponse( - "www.google.com", "/trailer_url", std::move(headers), kBody, + server_hostname_, "/trailer_url", std::move(headers), kBody, trailers.Clone()); EXPECT_EQ(kBody, client_->SendSynchronousRequest("/trailer_url")); @@ -2462,7 +2537,6 @@ class EndToEndTestServerPush : public EndToEndTest { const size_t kNumMaxStreams = 10; EndToEndTestServerPush() : EndToEndTest() { - FLAGS_quic_supports_push_promise = true; client_config_.SetMaxStreamsPerConnection(kNumMaxStreams, kNumMaxStreams); client_config_.SetMaxIncomingDynamicStreamsToSend(kNumMaxStreams); server_config_.SetMaxStreamsPerConnection(kNumMaxStreams, kNumMaxStreams); @@ -2531,7 +2605,9 @@ TEST_P(EndToEndTestServerPush, ServerPush) { AddRequestAndResponseWithServerPush("example.com", "/push_example", kBody, push_urls, kNumResources, 0); - client_->client()->set_response_listener(new TestResponseListener); + client_->client()->set_response_listener( + std::unique_ptr<QuicClientBase::ResponseListener>( + new TestResponseListener)); DVLOG(1) << "send request for /push_example"; EXPECT_EQ(kBody, client_->SendSynchronousRequest( @@ -2567,7 +2643,9 @@ TEST_P(EndToEndTestServerPush, ServerPushUnderLimit) { }; AddRequestAndResponseWithServerPush("example.com", "/push_example", kBody, push_urls, kNumResources, 0); - client_->client()->set_response_listener(new TestResponseListener); + client_->client()->set_response_listener( + std::unique_ptr<QuicClientBase::ResponseListener>( + new TestResponseListener)); // Send the first request: this will trigger the server to send all the push // resources associated with this request, and these will be cached by the @@ -2575,7 +2653,7 @@ TEST_P(EndToEndTestServerPush, ServerPushUnderLimit) { EXPECT_EQ(kBody, client_->SendSynchronousRequest( "https://example.com/push_example")); - for (string url : push_urls) { + for (const string& url : push_urls) { // Sending subsequent requesets will not actually send anything on the wire, // as the responses are already in the client's cache. DVLOG(1) << "send request for pushed stream on url " << url; @@ -2613,7 +2691,9 @@ TEST_P(EndToEndTestServerPush, ServerPushOverLimitNonBlocking) { } AddRequestAndResponseWithServerPush("example.com", "/push_example", kBody, push_urls, kNumResources, 0); - client_->client()->set_response_listener(new TestResponseListener); + client_->client()->set_response_listener( + std::unique_ptr<QuicClientBase::ResponseListener>( + new TestResponseListener)); // Send the first request: this will trigger the server to send all the push // resources associated with this request, and these will be cached by the @@ -2669,7 +2749,9 @@ TEST_P(EndToEndTestServerPush, ServerPushOverLimitWithBlocking) { AddRequestAndResponseWithServerPush("example.com", "/push_example", kBody, push_urls, kNumResources, kBodySize); - client_->client()->set_response_listener(new TestResponseListener); + client_->client()->set_response_listener( + std::unique_ptr<QuicClientBase::ResponseListener>( + new TestResponseListener)); client_->SendRequest("https://example.com/push_example"); @@ -2715,7 +2797,10 @@ TEST_P(EndToEndTestServerPush, ServerPushOverLimitWithBlocking) { EXPECT_EQ(12u, client_->num_responses()); } +// TODO(ckrasic) - remove this when deprecating +// FLAGS_quic_enable_server_push_by_default. TEST_P(EndToEndTestServerPush, DisabledWithoutConnectionOption) { + FLAGS_quic_enable_server_push_by_default = false; // Tests that server push won't be triggered when kSPSH is not set by client. support_server_push_ = false; ASSERT_TRUE(Initialize()); @@ -2730,7 +2815,9 @@ TEST_P(EndToEndTestServerPush, DisabledWithoutConnectionOption) { }; AddRequestAndResponseWithServerPush("example.com", "/push_example", kBody, push_urls, kNumResources, 0); - client_->client()->set_response_listener(new TestResponseListener); + client_->client()->set_response_listener( + std::unique_ptr<QuicClientBase::ResponseListener>( + new TestResponseListener)); EXPECT_EQ(kBody, client_->SendSynchronousRequest( "https://example.com/push_example")); @@ -2821,6 +2908,61 @@ TEST_P(EndToEndTest, DISABLED_TestHugeResponseWithPacketLoss) { } } +class EndToEndBufferedPacketsTest : public EndToEndTest { + public: + EndToEndBufferedPacketsTest() : EndToEndTest() { + FLAGS_quic_buffer_packet_till_chlo = true; + } + + void CreateClientWithWriter() override { + LOG(ERROR) << "create client with reorder_writer_ "; + reorder_writer_ = new PacketReorderingWriter(); + client_.reset(EndToEndTest::CreateQuicClient(reorder_writer_)); + } + + void SetUp() override { + // Don't initialize client writer in base class. + server_writer_ = new PacketDroppingTestWriter(); + } + + protected: + PacketReorderingWriter* reorder_writer_; +}; + +INSTANTIATE_TEST_CASE_P(EndToEndBufferedPacketsTests, + EndToEndBufferedPacketsTest, + testing::ValuesIn(GetTestParams())); + +TEST_P(EndToEndBufferedPacketsTest, Buffer0RttRequest) { + ASSERT_TRUE(Initialize()); + if (negotiated_version_ <= QUIC_VERSION_32) { + // Since no 0-rtt for v32 and under, and this test relies on 0-rtt, skip + // this test if QUIC doesn't do 0-rtt. + return; + } + // Finish one request to make sure handshake established. + client_->SendSynchronousRequest("/foo"); + // Disconnect for next 0-rtt request. + client_->Disconnect(); + + // Client get valid STK now. Do a 0-rtt request. + // Buffer a CHLO till another packets sent out. + reorder_writer_->SetDelay(1); + // Only send out a CHLO. + client_->client()->Initialize(); + client_->client()->StartConnect(); + ASSERT_TRUE(client_->client()->connected()); + // Send a request before handshake finishes. + HTTPMessage request(HttpConstants::HTTP_1_1, HttpConstants::GET, "/bar"); + client_->SendMessage(request); + client_->WaitForResponse(); + EXPECT_EQ(kBarResponseBody, client_->response_body()); + QuicConnectionStats client_stats = + client_->client()->session()->connection()->GetStats(); + EXPECT_EQ(0u, client_stats.packets_lost); + EXPECT_EQ(1, client_->client()->GetNumSentClientHellos()); +} + } // namespace } // namespace test } // namespace net diff --git a/chromium/net/tools/quic/quic_client.cc b/chromium/net/tools/quic/quic_client.cc index 224859e0537..ebb701d8f67 100644 --- a/chromium/net/tools/quic/quic_client.cc +++ b/chromium/net/tools/quic/quic_client.cc @@ -13,14 +13,16 @@ #include "base/logging.h" #include "base/run_loop.h" +#include "base/strings/string_number_conversions.h" #include "net/base/sockaddr_storage.h" -#include "net/quic/crypto/quic_random.h" -#include "net/quic/quic_bug_tracker.h" -#include "net/quic/quic_connection.h" -#include "net/quic/quic_data_reader.h" -#include "net/quic/quic_flags.h" -#include "net/quic/quic_protocol.h" -#include "net/quic/quic_server_id.h" +#include "net/quic/core/crypto/quic_random.h" +#include "net/quic/core/quic_bug_tracker.h" +#include "net/quic/core/quic_connection.h" +#include "net/quic/core/quic_data_reader.h" +#include "net/quic/core/quic_flags.h" +#include "net/quic/core/quic_protocol.h" +#include "net/quic/core/quic_server_id.h" +#include "net/quic/core/spdy_utils.h" #include "net/tools/quic/quic_epoll_alarm_factory.h" #include "net/tools/quic/quic_epoll_connection_helper.h" #include "net/tools/quic/quic_socket_utils.h" @@ -34,6 +36,7 @@ #define MMSG_MORE 0 using base::StringPiece; +using base::StringToInt; using std::string; using std::vector; @@ -41,46 +44,37 @@ namespace net { const int kEpollFlags = EPOLLIN | EPOLLOUT | EPOLLET; -void QuicClient::ClientQuicDataToResend::Resend() { - client_->SendRequest(*headers_, body_, fin_); - delete headers_; - headers_ = nullptr; -} - QuicClient::QuicClient(IPEndPoint server_address, const QuicServerId& server_id, const QuicVersionVector& supported_versions, EpollServer* epoll_server, - ProofVerifier* proof_verifier) + std::unique_ptr<ProofVerifier> proof_verifier) : QuicClient(server_address, server_id, supported_versions, QuicConfig(), epoll_server, - proof_verifier) {} + std::move(proof_verifier)) {} QuicClient::QuicClient(IPEndPoint server_address, const QuicServerId& server_id, const QuicVersionVector& supported_versions, const QuicConfig& config, EpollServer* epoll_server, - ProofVerifier* proof_verifier) + std::unique_ptr<ProofVerifier> proof_verifier) : QuicClientBase( server_id, supported_versions, config, new QuicEpollConnectionHelper(epoll_server, QuicAllocator::SIMPLE), new QuicEpollAlarmFactory(epoll_server), - proof_verifier), - server_address_(server_address), - local_port_(0), + std::move(proof_verifier)), epoll_server_(epoll_server), - initialized_(false), packets_dropped_(0), overflow_supported_(false), - store_response_(false), - latest_response_code_(-1), - packet_reader_(new QuicPacketReader()) {} + packet_reader_(new QuicPacketReader()) { + set_server_address(server_address); +} QuicClient::~QuicClient() { if (connected()) { @@ -89,70 +83,27 @@ QuicClient::~QuicClient() { ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); } - STLDeleteElements(&data_to_resend_on_connect_); - STLDeleteElements(&data_sent_before_handshake_); - CleanUpAllUDPSockets(); } -bool QuicClient::Initialize() { - QuicClientBase::Initialize(); - - set_num_sent_client_hellos(0); - set_num_stateless_rejects_received(0); - set_connection_error(QUIC_NO_ERROR); - - // If an initial flow control window has not explicitly been set, then use the - // same values that Chrome uses. - const uint32_t kSessionMaxRecvWindowSize = 15 * 1024 * 1024; // 15 MB - const uint32_t kStreamMaxRecvWindowSize = 6 * 1024 * 1024; // 6 MB - if (config()->GetInitialStreamFlowControlWindowToSend() == - kMinimumFlowControlSendWindow) { - config()->SetInitialStreamFlowControlWindowToSend(kStreamMaxRecvWindowSize); - } - if (config()->GetInitialSessionFlowControlWindowToSend() == - kMinimumFlowControlSendWindow) { - config()->SetInitialSessionFlowControlWindowToSend( - kSessionMaxRecvWindowSize); - } - +bool QuicClient::CreateUDPSocketAndBind(IPEndPoint server_address, + IPAddress bind_to_address, + int bind_to_port) { epoll_server_->set_timeout_in_us(50 * 1000); - if (!CreateUDPSocketAndBind()) { - return false; - } - - epoll_server_->RegisterFD(GetLatestFD(), this, kEpollFlags); - initialized_ = true; - return true; -} - -QuicClient::QuicDataToResend::QuicDataToResend(BalsaHeaders* headers, - StringPiece body, - bool fin) - : headers_(headers), body_(body), fin_(fin) {} - -QuicClient::QuicDataToResend::~QuicDataToResend() { - if (headers_) { - delete headers_; - } -} - -bool QuicClient::CreateUDPSocketAndBind() { int fd = - QuicSocketUtils::CreateUDPSocket(server_address_, &overflow_supported_); + QuicSocketUtils::CreateUDPSocket(server_address, &overflow_supported_); if (fd < 0) { return false; } IPEndPoint client_address; - if (bind_to_address_.size() != 0) { - client_address = IPEndPoint(bind_to_address_, local_port_); - } else if (server_address_.GetSockAddrFamily() == AF_INET) { - client_address = IPEndPoint(IPAddress::IPv4AllZeros(), local_port_); + if (bind_to_address.size() != 0) { + client_address = IPEndPoint(bind_to_address, bind_to_port); + } else if (server_address.GetSockAddrFamily() == AF_INET) { + client_address = IPEndPoint(IPAddress::IPv4AllZeros(), bind_to_port); } else { - IPAddress any6 = IPAddress::IPv6AllZeros(); - client_address = IPEndPoint(any6, local_port_); + client_address = IPEndPoint(IPAddress::IPv6AllZeros(), bind_to_port); } sockaddr_storage raw_addr; @@ -174,92 +125,10 @@ bool QuicClient::CreateUDPSocketAndBind() { fd_address_map_[fd] = client_address; + epoll_server_->RegisterFD(fd, this, kEpollFlags); return true; } -bool QuicClient::Connect() { - // Attempt multiple connects until the maximum number of client hellos have - // been sent. - while (!connected() && - GetNumSentClientHellos() <= QuicCryptoClientStream::kMaxClientHellos) { - StartConnect(); - while (EncryptionBeingEstablished()) { - WaitForEvents(); - } - if (FLAGS_enable_quic_stateless_reject_support && connected() && - !data_to_resend_on_connect_.empty()) { - // A connection has been established and there was previously queued data - // to resend. Resend it and empty the queue. - for (QuicDataToResend* data : data_to_resend_on_connect_) { - data->Resend(); - } - STLDeleteElements(&data_to_resend_on_connect_); - } - if (session() != nullptr && - session()->error() != QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT) { - // We've successfully created a session but we're not connected, and there - // is no stateless reject to recover from. Give up trying. - break; - } - } - if (!connected() && - GetNumSentClientHellos() > QuicCryptoClientStream::kMaxClientHellos && - session() != nullptr && - session()->error() == QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT) { - // The overall connection failed due too many stateless rejects. - set_connection_error(QUIC_CRYPTO_TOO_MANY_REJECTS); - } - return session()->connection()->connected(); -} - -void QuicClient::StartConnect() { - DCHECK(initialized_); - DCHECK(!connected()); - - QuicPacketWriter* writer = CreateQuicPacketWriter(); - - if (connected_or_attempting_connect()) { - // Before we destroy the last session and create a new one, gather its stats - // and update the stats for the overall connection. - UpdateStats(); - if (session()->error() == QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT) { - // If the last error was due to a stateless reject, queue up the data to - // be resent on the next successful connection. - // TODO(jokulik): I'm a little bit concerned about ordering here. Maybe - // we should just maintain one queue? - DCHECK(data_to_resend_on_connect_.empty()); - data_to_resend_on_connect_.swap(data_sent_before_handshake_); - } - } - - CreateQuicClientSession(new QuicConnection( - GetNextConnectionId(), server_address_, helper(), alarm_factory(), writer, - /* owns_writer= */ false, Perspective::IS_CLIENT, supported_versions())); - - // Reset |writer()| after |session()| so that the old writer outlives the old - // session. - set_writer(writer); - session()->Initialize(); - session()->CryptoConnect(); - set_connected_or_attempting_connect(true); -} - -void QuicClient::Disconnect() { - DCHECK(initialized_); - - if (connected()) { - session()->connection()->CloseConnection( - QUIC_PEER_GOING_AWAY, "Client disconnecting", - ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); - } - STLDeleteElements(&data_to_resend_on_connect_); - STLDeleteElements(&data_sent_before_handshake_); - - CleanUpAllUDPSockets(); - - initialized_ = false; -} - void QuicClient::CleanUpUDPSocket(int fd) { CleanUpUDPSocketImpl(fd); fd_address_map_.erase(fd); @@ -280,121 +149,9 @@ void QuicClient::CleanUpUDPSocketImpl(int fd) { } } -void QuicClient::SendRequest(const BalsaHeaders& headers, - StringPiece body, - bool fin) { - QuicClientPushPromiseIndex::TryHandle* handle; - QuicAsyncStatus rv = push_promise_index()->Try( - SpdyBalsaUtils::RequestHeadersToSpdyHeaders(headers), this, &handle); - if (rv == QUIC_SUCCESS) - return; - - if (rv == QUIC_PENDING) { - // May need to retry request if asynchronous rendezvous fails. - auto* new_headers = new BalsaHeaders; - new_headers->CopyFrom(headers); - push_promise_data_to_resend_.reset( - new ClientQuicDataToResend(new_headers, body, fin, this)); - return; - } - - QuicSpdyClientStream* stream = CreateReliableClientStream(); - if (stream == nullptr) { - QUIC_BUG << "stream creation failed!"; - return; - } - stream->SendRequest(SpdyBalsaUtils::RequestHeadersToSpdyHeaders(headers), - body, fin); - if (FLAGS_enable_quic_stateless_reject_support) { - // Record this in case we need to resend. - auto* new_headers = new BalsaHeaders; - new_headers->CopyFrom(headers); - auto* data_to_resend = - new ClientQuicDataToResend(new_headers, body, fin, this); - MaybeAddQuicDataToResend(data_to_resend); - } -} - -void QuicClient::MaybeAddQuicDataToResend(QuicDataToResend* data_to_resend) { - DCHECK(FLAGS_enable_quic_stateless_reject_support); - if (session()->IsCryptoHandshakeConfirmed()) { - // The handshake is confirmed. No need to continue saving requests to - // resend. - STLDeleteElements(&data_sent_before_handshake_); - delete data_to_resend; - return; - } - - // The handshake is not confirmed. Push the data onto the queue of data to - // resend if statelessly rejected. - data_sent_before_handshake_.push_back(data_to_resend); -} - -void QuicClient::SendRequestAndWaitForResponse(const BalsaHeaders& headers, - StringPiece body, - bool fin) { - SendRequest(headers, body, fin); - while (WaitForEvents()) { - } -} - -void QuicClient::SendRequestsAndWaitForResponse( - const vector<string>& url_list) { - for (size_t i = 0; i < url_list.size(); ++i) { - BalsaHeaders headers; - headers.SetRequestFirstlineFromStringPieces("GET", url_list[i], "HTTP/1.1"); - SendRequest(headers, "", true); - } - while (WaitForEvents()) { - } -} - -QuicSpdyClientStream* QuicClient::CreateReliableClientStream() { - QuicSpdyClientStream* stream = QuicClientBase::CreateReliableClientStream(); - if (stream) { - stream->set_visitor(this); - } - return stream; -} - -bool QuicClient::WaitForEvents() { - DCHECK(connected()); - - epoll_server_->WaitForEventsAndExecuteCallbacks(); +void QuicClient::RunEventLoop() { base::RunLoop().RunUntilIdle(); - - DCHECK(session() != nullptr); - if (!connected() && - session()->error() == QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT) { - DCHECK(FLAGS_enable_quic_stateless_reject_support); - DVLOG(1) << "Detected stateless reject while waiting for events. " - << "Attempting to reconnect."; - Connect(); - } - - return session()->num_active_requests() != 0; -} - -bool QuicClient::MigrateSocket(const IPAddress& new_host) { - if (!connected()) { - return false; - } - - CleanUpUDPSocket(GetLatestFD()); - - bind_to_address_ = new_host; - if (!CreateUDPSocketAndBind()) { - return false; - } - - epoll_server_->RegisterFD(GetLatestFD(), this, kEpollFlags); - session()->connection()->SetSelfAddress(GetLatestClientAddress()); - - QuicPacketWriter* writer = CreateQuicPacketWriter(); - set_writer(writer); - session()->connection()->SetQuicPacketWriter(writer, false); - - return true; + epoll_server_->WaitForEventsAndExecuteCallbacks(); } void QuicClient::OnEvent(int fd, EpollEvent* event) { @@ -405,7 +162,7 @@ void QuicClient::OnEvent(int fd, EpollEvent* event) { while (connected() && more_to_read) { more_to_read = packet_reader_->ReadAndDispatchPackets( GetLatestFD(), QuicClient::GetLatestClientAddress().port(), - *helper()->GetClock(), this, + false /* potentially_small_mtu */, *helper()->GetClock(), this, overflow_supported_ ? &packets_dropped_ : nullptr); } } @@ -418,71 +175,11 @@ void QuicClient::OnEvent(int fd, EpollEvent* event) { } } -void QuicClient::OnClose(QuicSpdyStream* stream) { - DCHECK(stream != nullptr); - QuicSpdyClientStream* client_stream = - static_cast<QuicSpdyClientStream*>(stream); - BalsaHeaders response_headers; - SpdyBalsaUtils::SpdyHeadersToResponseHeaders( - client_stream->response_headers(), &response_headers); - - if (response_listener_.get() != nullptr) { - response_listener_->OnCompleteResponse(stream->id(), response_headers, - client_stream->data()); - } - - // Store response headers and body. - if (store_response_) { - latest_response_code_ = response_headers.parsed_response_code(); - response_headers.DumpHeadersToString(&latest_response_headers_); - latest_response_body_ = client_stream->data(); - latest_response_trailers_ = - client_stream->received_trailers().DebugString(); - } -} - -bool QuicClient::CheckVary(const SpdyHeaderBlock& client_request, - const SpdyHeaderBlock& promise_request, - const SpdyHeaderBlock& promise_response) { - return true; -} - -void QuicClient::OnRendezvousResult(QuicSpdyStream* stream) { - std::unique_ptr<ClientQuicDataToResend> data_to_resend = - std::move(push_promise_data_to_resend_); - if (stream) { - stream->set_visitor(this); - stream->OnDataAvailable(); - } else if (data_to_resend.get()) { - data_to_resend->Resend(); - } -} - -size_t QuicClient::latest_response_code() const { - QUIC_BUG_IF(!store_response_) << "Response not stored!"; - return latest_response_code_; -} - -const string& QuicClient::latest_response_headers() const { - QUIC_BUG_IF(!store_response_) << "Response not stored!"; - return latest_response_headers_; -} - -const string& QuicClient::latest_response_body() const { - QUIC_BUG_IF(!store_response_) << "Response not stored!"; - return latest_response_body_; -} - -const string& QuicClient::latest_response_trailers() const { - QUIC_BUG_IF(!store_response_) << "Response not stored!"; - return latest_response_trailers_; -} - QuicPacketWriter* QuicClient::CreateQuicPacketWriter() { return new QuicDefaultPacketWriter(GetLatestFD()); } -const IPEndPoint QuicClient::GetLatestClientAddress() const { +IPEndPoint QuicClient::GetLatestClientAddress() const { if (fd_address_map_.empty()) { return IPEndPoint(); } @@ -501,7 +198,7 @@ int QuicClient::GetLatestFD() const { void QuicClient::ProcessPacket(const IPEndPoint& self_address, const IPEndPoint& peer_address, const QuicReceivedPacket& packet) { - session()->connection()->ProcessUdpPacket(self_address, peer_address, packet); + session()->ProcessUdpPacket(self_address, peer_address, packet); } } // namespace net diff --git a/chromium/net/tools/quic/quic_client.h b/chromium/net/tools/quic/quic_client.h index 99e529c4a2b..567dc6c837c 100644 --- a/chromium/net/tools/quic/quic_client.h +++ b/chromium/net/tools/quic/quic_client.h @@ -17,10 +17,9 @@ #include "base/strings/string_piece.h" #include "net/base/ip_address.h" #include "net/base/ip_endpoint.h" -#include "net/quic/quic_client_push_promise_index.h" -#include "net/quic/quic_config.h" -#include "net/quic/quic_spdy_stream.h" -#include "net/tools/balsa/balsa_headers.h" +#include "net/quic/core/quic_client_push_promise_index.h" +#include "net/quic/core/quic_config.h" +#include "net/quic/core/quic_spdy_stream.h" #include "net/tools/epoll_server/epoll_server.h" #include "net/tools/quic/quic_client_base.h" #include "net/tools/quic/quic_client_session.h" @@ -39,97 +38,24 @@ class QuicClientPeer; class QuicClient : public QuicClientBase, public EpollCallbackInterface, - public QuicSpdyStream::Visitor, - public ProcessPacketInterface, - public QuicClientPushPromiseIndex::Delegate { + public ProcessPacketInterface { public: - class ResponseListener { - public: - ResponseListener() {} - virtual ~ResponseListener() {} - virtual void OnCompleteResponse(QuicStreamId id, - const BalsaHeaders& response_headers, - const std::string& response_body) = 0; - }; - - // The client uses these objects to keep track of any data to resend upon - // receipt of a stateless reject. Recall that the client API allows callers - // to optimistically send data to the server prior to handshake-confirmation. - // If the client subsequently receives a stateless reject, it must tear down - // its existing session, create a new session, and resend all previously sent - // data. It uses these objects to keep track of all the sent data, and to - // resend the data upon a subsequent connection. - class QuicDataToResend { - public: - // Takes ownership of |headers|. |headers| may be null, since it's possible - // to send data without headers. - QuicDataToResend(BalsaHeaders* headers, base::StringPiece body, bool fin); - - virtual ~QuicDataToResend(); - - // Must be overridden by specific classes with the actual method for - // re-sending data. - virtual void Resend() = 0; - - protected: - BalsaHeaders* headers_; - base::StringPiece body_; - bool fin_; - - private: - DISALLOW_COPY_AND_ASSIGN(QuicDataToResend); - }; - // Create a quic client, which will have events managed by an externally owned // EpollServer. QuicClient(IPEndPoint server_address, const QuicServerId& server_id, const QuicVersionVector& supported_versions, EpollServer* epoll_server, - ProofVerifier* proof_verifier); + std::unique_ptr<ProofVerifier> proof_verifier); QuicClient(IPEndPoint server_address, const QuicServerId& server_id, const QuicVersionVector& supported_versions, const QuicConfig& config, EpollServer* epoll_server, - ProofVerifier* proof_verifier); + std::unique_ptr<ProofVerifier> proof_verifier); ~QuicClient() override; - // From QuicClientBase - bool Initialize() override; - bool WaitForEvents() override; - QuicSpdyClientStream* CreateReliableClientStream() override; - - // "Connect" to the QUIC server, including performing synchronous crypto - // handshake. - bool Connect(); - - // Start the crypto handshake. This can be done in place of the synchronous - // Connect(), but callers are responsible for making sure the crypto handshake - // completes. - void StartConnect(); - - // Disconnects from the QUIC server. - void Disconnect(); - - // Sends an HTTP request and does not wait for response before returning. - void SendRequest(const BalsaHeaders& headers, - base::StringPiece body, - bool fin); - - // Sends an HTTP request and waits for response before returning. - void SendRequestAndWaitForResponse(const BalsaHeaders& headers, - base::StringPiece body, - bool fin); - - // Sends a request simple GET for each URL in |url_list|, and then waits for - // each to complete. - void SendRequestsAndWaitForResponse(const std::vector<std::string>& url_list); - - // Migrate to a new socket during an active connection. - bool MigrateSocket(const IPAddress& new_host); - // From EpollCallbackInterface void OnRegistration(EpollServer* eps, int fd, int event_mask) override {} void OnModification(int fd, int event_mask) override {} @@ -140,65 +66,32 @@ class QuicClient : public QuicClientBase, void OnUnregistration(int fd, bool replaced) override {} void OnShutdown(EpollServer* eps, int fd) override {} - // QuicSpdyStream::Visitor - void OnClose(QuicSpdyStream* stream) override; - - bool CheckVary(const SpdyHeaderBlock& client_request, - const SpdyHeaderBlock& promise_request, - const SpdyHeaderBlock& promise_response) override; - void OnRendezvousResult(QuicSpdyStream*) override; - - // If the crypto handshake has not yet been confirmed, adds the data to the - // queue of data to resend if the client receives a stateless reject. - // Otherwise, deletes the data. Takes ownerership of |data_to_resend|. - void MaybeAddQuicDataToResend(QuicDataToResend* data_to_resend); - - // If the client has at least one UDP socket, return address of the latest - // created one. Otherwise, return an empty socket address. - const IPEndPoint GetLatestClientAddress() const; - // If the client has at least one UDP socket, return the latest created one. // Otherwise, return -1. int GetLatestFD() const; - void set_bind_to_address(const IPAddress& address) { - bind_to_address_ = address; - } - - const IPAddress& bind_to_address() const { return bind_to_address_; } - - void set_local_port(int local_port) { local_port_ = local_port; } - - const IPEndPoint& server_address() const { return server_address_; } - - // Takes ownership of the listener. - void set_response_listener(ResponseListener* listener) { - response_listener_.reset(listener); - } - - void set_store_response(bool val) { store_response_ = val; } - - size_t latest_response_code() const; - const std::string& latest_response_headers() const; - const std::string& latest_response_body() const; - const std::string& latest_response_trailers() const; + // From QuicClientBase + IPEndPoint GetLatestClientAddress() const override; - protected: // Implements ProcessPacketInterface. This will be called for each received // packet. void ProcessPacket(const IPEndPoint& self_address, const IPEndPoint& peer_address, const QuicReceivedPacket& packet) override; - virtual QuicPacketWriter* CreateQuicPacketWriter(); + protected: + // From QuicClientBase + QuicPacketWriter* CreateQuicPacketWriter() override; + void RunEventLoop() override; + bool CreateUDPSocketAndBind(IPEndPoint server_address, + IPAddress bind_to_address, + int bind_to_port) override; + void CleanUpAllUDPSockets() override; // If |fd| is an open UDP socket, unregister and close it. Otherwise, do // nothing. virtual void CleanUpUDPSocket(int fd); - // Unregister and close all open UDP sockets. - virtual void CleanUpAllUDPSockets(); - EpollServer* epoll_server() { return epoll_server_; } const linked_hash_map<int, IPEndPoint>& fd_address_map() const { @@ -208,52 +101,9 @@ class QuicClient : public QuicClientBase, private: friend class net::test::QuicClientPeer; - // Specific QuicClient class for storing data to resend. - class ClientQuicDataToResend : public QuicDataToResend { - public: - // Takes ownership of |headers|. - ClientQuicDataToResend(BalsaHeaders* headers, - base::StringPiece body, - bool fin, - QuicClient* client) - : QuicDataToResend(headers, body, fin), client_(client) { - DCHECK(headers); - DCHECK(client); - } - - ~ClientQuicDataToResend() override {} - - void Resend() override; - - private: - QuicClient* client_; - - DISALLOW_COPY_AND_ASSIGN(ClientQuicDataToResend); - }; - - // Used during initialization: creates the UDP socket FD, sets socket options, - // and binds the socket to our address. - bool CreateUDPSocketAndBind(); - // Actually clean up |fd|. void CleanUpUDPSocketImpl(int fd); - // If the request URL matches a push promise, bypass sending the - // request. - bool MaybeHandlePromised(const BalsaHeaders& headers, - const SpdyHeaderBlock& spdy_headers, - base::StringPiece body, - bool fin); - - // Address of the server. - const IPEndPoint server_address_; - - // If initialized, the address to bind to. - IPAddress bind_to_address_; - - // Local port to bind to. Initialize to 0. - int local_port_; - // Listens for events on the client socket. EpollServer* epoll_server_; @@ -261,12 +111,6 @@ class QuicClient : public QuicClientBase, // map, the order of socket creation can be recorded. linked_hash_map<int, IPEndPoint> fd_address_map_; - // Listens for full responses. - std::unique_ptr<ResponseListener> response_listener_; - - // Tracks if the client is initialized to connect. - bool initialized_; - // If overflow_supported_ is true, this will be the number of packets dropped // during the lifetime of the server. QuicPacketCount packets_dropped_; @@ -275,33 +119,10 @@ class QuicClient : public QuicClientBase, // because the socket would otherwise overflow. bool overflow_supported_; - // If true, store the latest response code, headers, and body. - bool store_response_; - // HTTP response code from most recent response. - size_t latest_response_code_; - // HTTP/2 headers from most recent response. - std::string latest_response_headers_; - // Body of most recent response. - std::string latest_response_body_; - // HTTP/2 trailers from most recent response. - std::string latest_response_trailers_; - - // Keeps track of any data sent before the handshake. - std::vector<QuicDataToResend*> data_sent_before_handshake_; - - // Once the client receives a stateless reject, keeps track of any data that - // must be resent upon a subsequent successful connection. - std::vector<QuicDataToResend*> data_to_resend_on_connect_; - // Point to a QuicPacketReader object on the heap. The reader allocates more // space than allowed on the stack. - // - // TODO(rtenneti): Chromium code doesn't use |packet_reader_|. Add support for - // QuicPacketReader std::unique_ptr<QuicPacketReader> packet_reader_; - std::unique_ptr<ClientQuicDataToResend> push_promise_data_to_resend_; - DISALLOW_COPY_AND_ASSIGN(QuicClient); }; diff --git a/chromium/net/tools/quic/quic_client_base.cc b/chromium/net/tools/quic/quic_client_base.cc index 211c54d8e98..14af28a5757 100644 --- a/chromium/net/tools/quic/quic_client_base.cc +++ b/chromium/net/tools/quic/quic_client_base.cc @@ -4,20 +4,42 @@ #include "net/tools/quic/quic_client_base.h" -#include "net/quic/crypto/quic_random.h" -#include "net/quic/quic_server_id.h" +#include "base/strings/string_number_conversions.h" +#include "net/quic/core/crypto/quic_random.h" +#include "net/quic/core/quic_server_id.h" +#include "net/quic/core/spdy_utils.h" + +using base::StringPiece; +using base::StringToInt; +using std::string; +using std::vector; namespace net { +void QuicClientBase::ClientQuicDataToResend::Resend() { + client_->SendRequest(*headers_, body_, fin_); + headers_ = nullptr; +} + +QuicClientBase::QuicDataToResend::QuicDataToResend( + std::unique_ptr<SpdyHeaderBlock> headers, + StringPiece body, + bool fin) + : headers_(std::move(headers)), body_(body), fin_(fin) {} + +QuicClientBase::QuicDataToResend::~QuicDataToResend() {} + QuicClientBase::QuicClientBase(const QuicServerId& server_id, const QuicVersionVector& supported_versions, const QuicConfig& config, QuicConnectionHelperInterface* helper, QuicAlarmFactory* alarm_factory, - ProofVerifier* proof_verifier) + std::unique_ptr<ProofVerifier> proof_verifier) : server_id_(server_id), + initialized_(false), + local_port_(0), config_(config), - crypto_config_(proof_verifier), + crypto_config_(std::move(proof_verifier)), helper_(helper), alarm_factory_(alarm_factory), supported_versions_(supported_versions), @@ -25,18 +47,142 @@ QuicClientBase::QuicClientBase(const QuicServerId& server_id, num_stateless_rejects_received_(0), num_sent_client_hellos_(0), connection_error_(QUIC_NO_ERROR), - connected_or_attempting_connect_(false) {} + connected_or_attempting_connect_(false), + store_response_(false), + latest_response_code_(-1) {} QuicClientBase::~QuicClientBase() {} +void QuicClientBase::OnClose(QuicSpdyStream* stream) { + DCHECK(stream != nullptr); + QuicSpdyClientStream* client_stream = + static_cast<QuicSpdyClientStream*>(stream); + + const SpdyHeaderBlock& response_headers = client_stream->response_headers(); + if (response_listener_ != nullptr) { + response_listener_->OnCompleteResponse(stream->id(), response_headers, + client_stream->data()); + } + + // Store response headers and body. + if (store_response_) { + auto status = response_headers.find(":status"); + if (status == response_headers.end() || + !StringToInt(status->second, &latest_response_code_)) { + LOG(ERROR) << "Invalid response headers"; + } + latest_response_headers_ = response_headers.DebugString(); + latest_response_header_block_ = response_headers.Clone(); + latest_response_body_ = client_stream->data(); + latest_response_trailers_ = + client_stream->received_trailers().DebugString(); + } +} + bool QuicClientBase::Initialize() { num_sent_client_hellos_ = 0; num_stateless_rejects_received_ = 0; connection_error_ = QUIC_NO_ERROR; connected_or_attempting_connect_ = false; + + // If an initial flow control window has not explicitly been set, then use the + // same values that Chrome uses. + const uint32_t kSessionMaxRecvWindowSize = 15 * 1024 * 1024; // 15 MB + const uint32_t kStreamMaxRecvWindowSize = 6 * 1024 * 1024; // 6 MB + if (config()->GetInitialStreamFlowControlWindowToSend() == + kMinimumFlowControlSendWindow) { + config()->SetInitialStreamFlowControlWindowToSend(kStreamMaxRecvWindowSize); + } + if (config()->GetInitialSessionFlowControlWindowToSend() == + kMinimumFlowControlSendWindow) { + config()->SetInitialSessionFlowControlWindowToSend( + kSessionMaxRecvWindowSize); + } + + if (!CreateUDPSocketAndBind(server_address_, bind_to_address_, local_port_)) { + return false; + } + + initialized_ = true; return true; } +bool QuicClientBase::Connect() { + // Attempt multiple connects until the maximum number of client hellos have + // been sent. + while (!connected() && + GetNumSentClientHellos() <= QuicCryptoClientStream::kMaxClientHellos) { + StartConnect(); + while (EncryptionBeingEstablished()) { + WaitForEvents(); + } + if (FLAGS_enable_quic_stateless_reject_support && connected()) { + // Resend any previously queued data. + ResendSavedData(); + } + if (session() != nullptr && + session()->error() != QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT) { + // We've successfully created a session but we're not connected, and there + // is no stateless reject to recover from. Give up trying. + break; + } + } + if (!connected() && + GetNumSentClientHellos() > QuicCryptoClientStream::kMaxClientHellos && + session() != nullptr && + session()->error() == QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT) { + // The overall connection failed due too many stateless rejects. + set_connection_error(QUIC_CRYPTO_TOO_MANY_REJECTS); + } + return session()->connection()->connected(); +} + +void QuicClientBase::StartConnect() { + DCHECK(initialized_); + DCHECK(!connected()); + + QuicPacketWriter* writer = CreateQuicPacketWriter(); + + if (connected_or_attempting_connect()) { + // If the last error was not a stateless reject, then the queued up data + // does not need to be resent. + if (session()->error() != QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT) { + ClearDataToResend(); + } + // Before we destroy the last session and create a new one, gather its stats + // and update the stats for the overall connection. + UpdateStats(); + } + + CreateQuicClientSession(new QuicConnection( + GetNextConnectionId(), server_address(), helper(), alarm_factory(), + writer, + /* owns_writer= */ false, Perspective::IS_CLIENT, supported_versions())); + + // Reset |writer()| after |session()| so that the old writer outlives the old + // session. + set_writer(writer); + session()->Initialize(); + session()->CryptoConnect(); + set_connected_or_attempting_connect(true); +} + +void QuicClientBase::Disconnect() { + DCHECK(initialized_); + + if (connected()) { + session()->connection()->CloseConnection( + QUIC_PEER_GOING_AWAY, "Client disconnecting", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + } + + ClearDataToResend(); + + CleanUpAllUDPSockets(); + + initialized_ = false; +} + ProofVerifier* QuicClientBase::proof_verifier() const { return crypto_config_.proof_verifier(); } @@ -56,12 +202,102 @@ bool QuicClientBase::EncryptionBeingEstablished() { session_->connection()->connected(); } +void QuicClientBase::SendRequest(const SpdyHeaderBlock& headers, + StringPiece body, + bool fin) { + QuicClientPushPromiseIndex::TryHandle* handle; + QuicAsyncStatus rv = push_promise_index()->Try(headers, this, &handle); + if (rv == QUIC_SUCCESS) + return; + + if (rv == QUIC_PENDING) { + // May need to retry request if asynchronous rendezvous fails. + AddPromiseDataToResend(headers, body, fin); + return; + } + + QuicSpdyClientStream* stream = CreateReliableClientStream(); + if (stream == nullptr) { + QUIC_BUG << "stream creation failed!"; + return; + } + stream->SendRequest(headers.Clone(), body, fin); + // Record this in case we need to resend. + MaybeAddDataToResend(headers, body, fin); +} + +void QuicClientBase::SendRequestAndWaitForResponse( + const SpdyHeaderBlock& headers, + StringPiece body, + bool fin) { + SendRequest(headers, body, fin); + while (WaitForEvents()) { + } +} + +void QuicClientBase::SendRequestsAndWaitForResponse( + const vector<string>& url_list) { + for (size_t i = 0; i < url_list.size(); ++i) { + SpdyHeaderBlock headers; + if (!SpdyUtils::PopulateHeaderBlockFromUrl(url_list[i], &headers)) { + QUIC_BUG << "Unable to create request"; + continue; + } + SendRequest(headers, "", true); + } + while (WaitForEvents()) { + } +} + QuicSpdyClientStream* QuicClientBase::CreateReliableClientStream() { if (!connected()) { return nullptr; } - return session_->CreateOutgoingDynamicStream(kDefaultPriority); + QuicSpdyClientStream* stream = + session_->CreateOutgoingDynamicStream(kDefaultPriority); + if (stream) { + stream->set_visitor(this); + } + return stream; +} + +bool QuicClientBase::WaitForEvents() { + DCHECK(connected()); + + RunEventLoop(); + + DCHECK(session() != nullptr); + if (!connected() && + session()->error() == QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT) { + DCHECK(FLAGS_enable_quic_stateless_reject_support); + DVLOG(1) << "Detected stateless reject while waiting for events. " + << "Attempting to reconnect."; + Connect(); + } + + return session()->num_active_requests() != 0; +} + +bool QuicClientBase::MigrateSocket(const IPAddress& new_host) { + if (!connected()) { + return false; + } + + CleanUpAllUDPSockets(); + + set_bind_to_address(new_host); + if (!CreateUDPSocketAndBind(server_address_, bind_to_address_, local_port_)) { + return false; + } + + session()->connection()->SetSelfAddress(GetLatestClientAddress()); + + QuicPacketWriter* writer = CreateQuicPacketWriter(); + set_writer(writer); + session()->connection()->SetQuicPacketWriter(writer, false); + + return true; } void QuicClientBase::WaitForStreamToClose(QuicStreamId id) { @@ -122,7 +358,7 @@ QuicErrorCode QuicClientBase::connection_error() const { if (connection_error_ != QUIC_NO_ERROR) { return connection_error_; } - if (session_.get() == nullptr) { + if (session_ == nullptr) { return QUIC_NO_ERROR; } return session_->error(); @@ -150,4 +386,97 @@ QuicConnectionId QuicClientBase::GenerateNewConnectionId() { return QuicRandom::GetInstance()->RandUint64(); } +void QuicClientBase::MaybeAddDataToResend(const SpdyHeaderBlock& headers, + StringPiece body, + bool fin) { + if (!FLAGS_enable_quic_stateless_reject_support) { + return; + } + + if (session()->IsCryptoHandshakeConfirmed()) { + // The handshake is confirmed. No need to continue saving requests to + // resend. + data_to_resend_on_connect_.clear(); + return; + } + + // The handshake is not confirmed. Push the data onto the queue of data to + // resend if statelessly rejected. + std::unique_ptr<SpdyHeaderBlock> new_headers( + new SpdyHeaderBlock(headers.Clone())); + std::unique_ptr<QuicDataToResend> data_to_resend( + new ClientQuicDataToResend(std::move(new_headers), body, fin, this)); + MaybeAddQuicDataToResend(std::move(data_to_resend)); +} + +void QuicClientBase::MaybeAddQuicDataToResend( + std::unique_ptr<QuicDataToResend> data_to_resend) { + data_to_resend_on_connect_.push_back(std::move(data_to_resend)); +} + +void QuicClientBase::ClearDataToResend() { + data_to_resend_on_connect_.clear(); +} + +void QuicClientBase::ResendSavedData() { + // Calling Resend will re-enqueue the data, so swap out + // data_to_resend_on_connect_ before iterating. + vector<std::unique_ptr<QuicDataToResend>> old_data; + old_data.swap(data_to_resend_on_connect_); + for (const auto& data : old_data) { + data->Resend(); + } +} + +void QuicClientBase::AddPromiseDataToResend(const SpdyHeaderBlock& headers, + StringPiece body, + bool fin) { + std::unique_ptr<SpdyHeaderBlock> new_headers( + new SpdyHeaderBlock(headers.Clone())); + push_promise_data_to_resend_.reset( + new ClientQuicDataToResend(std::move(new_headers), body, fin, this)); +} + +bool QuicClientBase::CheckVary(const SpdyHeaderBlock& client_request, + const SpdyHeaderBlock& promise_request, + const SpdyHeaderBlock& promise_response) { + return true; +} + +void QuicClientBase::OnRendezvousResult(QuicSpdyStream* stream) { + std::unique_ptr<ClientQuicDataToResend> data_to_resend = + std::move(push_promise_data_to_resend_); + if (stream) { + stream->set_visitor(this); + stream->OnDataAvailable(); + } else if (data_to_resend.get()) { + data_to_resend->Resend(); + } +} + +size_t QuicClientBase::latest_response_code() const { + QUIC_BUG_IF(!store_response_) << "Response not stored!"; + return latest_response_code_; +} + +const string& QuicClientBase::latest_response_headers() const { + QUIC_BUG_IF(!store_response_) << "Response not stored!"; + return latest_response_headers_; +} + +const SpdyHeaderBlock& QuicClientBase::latest_response_header_block() const { + QUIC_BUG_IF(!store_response_) << "Response not stored!"; + return latest_response_header_block_; +} + +const string& QuicClientBase::latest_response_body() const { + QUIC_BUG_IF(!store_response_) << "Response not stored!"; + return latest_response_body_; +} + +const string& QuicClientBase::latest_response_trailers() const { + QUIC_BUG_IF(!store_response_) << "Response not stored!"; + return latest_response_trailers_; +} + } // namespace net diff --git a/chromium/net/tools/quic/quic_client_base.h b/chromium/net/tools/quic/quic_client_base.h index 3f9e514fb89..b32cd0a4c3c 100644 --- a/chromium/net/tools/quic/quic_client_base.h +++ b/chromium/net/tools/quic/quic_client_base.h @@ -13,16 +13,15 @@ #include "base/macros.h" #include "net/base/ip_endpoint.h" -#include "net/log/net_log.h" -#include "net/quic/crypto/crypto_handshake.h" -#include "net/quic/crypto/quic_crypto_client_config.h" -#include "net/quic/quic_alarm_factory.h" -#include "net/quic/quic_bandwidth.h" -#include "net/quic/quic_client_push_promise_index.h" -#include "net/quic/quic_config.h" -#include "net/quic/quic_connection.h" -#include "net/quic/quic_packet_writer.h" -#include "net/quic/quic_protocol.h" +#include "net/quic/core/crypto/crypto_handshake.h" +#include "net/quic/core/crypto/quic_crypto_client_config.h" +#include "net/quic/core/quic_alarm_factory.h" +#include "net/quic/core/quic_bandwidth.h" +#include "net/quic/core/quic_client_push_promise_index.h" +#include "net/quic/core/quic_config.h" +#include "net/quic/core/quic_connection.h" +#include "net/quic/core/quic_packet_writer.h" +#include "net/quic/core/quic_protocol.h" #include "net/tools/quic/quic_client_session.h" #include "net/tools/quic/quic_spdy_client_stream.h" @@ -31,27 +30,96 @@ namespace net { class ProofVerifier; class QuicServerId; -class QuicClientBase { +class QuicClientBase : public QuicClientPushPromiseIndex::Delegate, + public QuicSpdyStream::Visitor { public: + // A ResponseListener is notified when a complete response is received. + class ResponseListener { + public: + ResponseListener() {} + virtual ~ResponseListener() {} + virtual void OnCompleteResponse(QuicStreamId id, + const SpdyHeaderBlock& response_headers, + const std::string& response_body) = 0; + }; + + // The client uses these objects to keep track of any data to resend upon + // receipt of a stateless reject. Recall that the client API allows callers + // to optimistically send data to the server prior to handshake-confirmation. + // If the client subsequently receives a stateless reject, it must tear down + // its existing session, create a new session, and resend all previously sent + // data. It uses these objects to keep track of all the sent data, and to + // resend the data upon a subsequent connection. + class QuicDataToResend { + public: + // |headers| may be null, since it's possible to send data without headers. + QuicDataToResend(std::unique_ptr<SpdyHeaderBlock> headers, + base::StringPiece body, + bool fin); + + virtual ~QuicDataToResend(); + + // Must be overridden by specific classes with the actual method for + // re-sending data. + virtual void Resend() = 0; + + protected: + std::unique_ptr<SpdyHeaderBlock> headers_; + base::StringPiece body_; + bool fin_; + + private: + DISALLOW_COPY_AND_ASSIGN(QuicDataToResend); + }; + QuicClientBase(const QuicServerId& server_id, const QuicVersionVector& supported_versions, const QuicConfig& config, QuicConnectionHelperInterface* helper, QuicAlarmFactory* alarm_factory, - ProofVerifier* proof_verifier); + std::unique_ptr<ProofVerifier> proof_verifier); + + ~QuicClientBase() override; - ~QuicClientBase(); + // QuicSpdyStream::Visitor + void OnClose(QuicSpdyStream* stream) override; // Initializes the client to create a connection. Should be called exactly // once before calling StartConnect or Connect. Returns true if the // initialization succeeds, false otherwise. virtual bool Initialize(); + // "Connect" to the QUIC server, including performing synchronous crypto + // handshake. + bool Connect(); + + // Start the crypto handshake. This can be done in place of the synchronous + // Connect(), but callers are responsible for making sure the crypto handshake + // completes. + void StartConnect(); + + // Disconnects from the QUIC server. + void Disconnect(); + // Returns true if the crypto handshake has yet to establish encryption. // Returns false if encryption is active (even if the server hasn't confirmed // the handshake) or if the connection has been closed. bool EncryptionBeingEstablished(); + // Sends an HTTP request and does not wait for response before returning. + void SendRequest(const SpdyHeaderBlock& headers, + base::StringPiece body, + bool fin); + + // Sends an HTTP request and waits for response before returning. + void SendRequestAndWaitForResponse(const SpdyHeaderBlock& headers, + base::StringPiece body, + bool fin); + + // Sends a request simple GET for each URL in |url_list|, and then waits for + // each to complete. + void SendRequestsAndWaitForResponse(const std::vector<std::string>& url_list); + // Returns a newly created QuicSpdyClientStream, owned by the // QuicSimpleClient. virtual QuicSpdyClientStream* CreateReliableClientStream(); @@ -64,7 +132,10 @@ class QuicClientBase { // Wait up to 50ms, and handle any events which occur. // Returns true if there are any outstanding requests. - virtual bool WaitForEvents() = 0; + bool WaitForEvents(); + + // Migrate to a new socket during an active connection. + bool MigrateSocket(const IPAddress& new_host); QuicClientSession* session() { return session_.get(); } @@ -88,6 +159,17 @@ class QuicClientBase { crypto_config_.SetChannelIDSource(source); } + // UseTokenBinding enables token binding negotiation in the client. This + // should only be called before the initial Connect(). The client will still + // need to check that token binding is negotiated with the server, and add + // token binding headers to requests if so. server, and add token binding + // headers to requests if so. The negotiated token binding parameters can be + // found on the QuicCryptoNegotiatedParameters object in + // token_binding_key_param. + void UseTokenBinding() { + crypto_config_.tb_key_params = QuicTagVector{kTB10}; + } + const QuicVersionVector& supported_versions() const { return supported_versions_; } @@ -164,10 +246,67 @@ class QuicClientBase { return &push_promise_index_; } + bool CheckVary(const SpdyHeaderBlock& client_request, + const SpdyHeaderBlock& promise_request, + const SpdyHeaderBlock& promise_response) override; + void OnRendezvousResult(QuicSpdyStream*) override; + + // If the crypto handshake has not yet been confirmed, adds the data to the + // queue of data to resend if the client receives a stateless reject. + // Otherwise, deletes the data. + void MaybeAddQuicDataToResend( + std::unique_ptr<QuicDataToResend> data_to_resend); + + void set_store_response(bool val) { store_response_ = val; } + + size_t latest_response_code() const; + const std::string& latest_response_headers() const; + const SpdyHeaderBlock& latest_response_header_block() const; + const std::string& latest_response_body() const; + const std::string& latest_response_trailers() const; + + void set_response_listener(std::unique_ptr<ResponseListener> listener) { + response_listener_ = std::move(listener); + } + + void set_bind_to_address(IPAddress address) { bind_to_address_ = address; } + + IPAddress bind_to_address() const { return bind_to_address_; } + + void set_local_port(int local_port) { local_port_ = local_port; } + + int local_port() const { return local_port_; } + + const IPEndPoint& server_address() const { return server_address_; } + + void set_server_address(const IPEndPoint& server_address) { + server_address_ = server_address; + } + protected: + // Creates a packet writer to be used for the next connection. + virtual QuicPacketWriter* CreateQuicPacketWriter() = 0; + + // Takes ownership of |connection|. virtual QuicClientSession* CreateQuicClientSession( QuicConnection* connection); + // Runs one iteration of the event loop. + virtual void RunEventLoop() = 0; + + // Used during initialization: creates the UDP socket FD, sets socket options, + // and binds the socket to our address. + virtual bool CreateUDPSocketAndBind(IPEndPoint server_address, + IPAddress bind_to_address, + int bind_to_port) = 0; + + // Unregister and close all open UDP sockets. + virtual void CleanUpAllUDPSockets() = 0; + + // If the client has at least one UDP socket, return address of the latest + // created one. Otherwise, return an empty socket address. + virtual IPEndPoint GetLatestClientAddress() const = 0; + // Generates the next ConnectionId for |server_id_|. By default, if the // cached server config contains a server-designated ID, that ID will be // returned. Otherwise, the next random ID will be returned. @@ -181,6 +320,21 @@ class QuicClientBase { // connection ID). virtual QuicConnectionId GenerateNewConnectionId(); + // If the crypto handshake has not yet been confirmed, adds the data to the + // queue of data to resend if the client receives a stateless reject. + // Otherwise, deletes the data. + void MaybeAddDataToResend(const SpdyHeaderBlock& headers, + base::StringPiece body, + bool fin); + + void ClearDataToResend(); + + void ResendSavedData(); + + void AddPromiseDataToResend(const SpdyHeaderBlock& headers, + base::StringPiece body, + bool fin); + QuicConnectionHelperInterface* helper() { return helper_.get(); } QuicAlarmFactory* alarm_factory() { return alarm_factory_.get(); } @@ -194,24 +348,60 @@ class QuicClientBase { } private: + // Specific QuicClient class for storing data to resend. + class ClientQuicDataToResend : public QuicDataToResend { + public: + ClientQuicDataToResend(std::unique_ptr<SpdyHeaderBlock> headers, + base::StringPiece body, + bool fin, + QuicClientBase* client) + : QuicDataToResend(std::move(headers), body, fin), client_(client) { + DCHECK(headers_); + DCHECK(client); + } + + ~ClientQuicDataToResend() override {} + + void Resend() override; + + private: + QuicClientBase* client_; + + DISALLOW_COPY_AND_ASSIGN(ClientQuicDataToResend); + }; + // |server_id_| is a tuple (hostname, port, is_https) of the server. QuicServerId server_id_; + // Tracks if the client is initialized to connect. + bool initialized_; + + // Address of the server. + IPEndPoint server_address_; + + // If initialized, the address to bind to. + IPAddress bind_to_address_; + + // Local port to bind to. Initialize to 0. + int local_port_; + // config_ and crypto_config_ contain configuration and cached state about // servers. QuicConfig config_; QuicCryptoClientConfig crypto_config_; - // Helper to be used by created connections. Needs to outlive |session_|. + // Helper to be used by created connections. Must outlive |session_|. std::unique_ptr<QuicConnectionHelperInterface> helper_; - // Helper to be used by created connections. Needs to outlive |session_|. + // Alarm factory to be used by created connections. Must outlive |session_|. std::unique_ptr<QuicAlarmFactory> alarm_factory_; - // Writer used to actually send packets to the wire. Needs to outlive - // |session_|. + // Writer used to actually send packets to the wire. Must outlive |session_|. std::unique_ptr<QuicPacketWriter> writer_; + // Index of pending promised streams. Must outlive |session_|. + QuicClientPushPromiseIndex push_promise_index_; + // Session which manages streams. std::unique_ptr<QuicClientSession> session_; @@ -247,7 +437,27 @@ class QuicClientBase { // to the previous client-level connection. bool connected_or_attempting_connect_; - QuicClientPushPromiseIndex push_promise_index_; + // If true, store the latest response code, headers, and body. + bool store_response_; + // HTTP response code from most recent response. + int latest_response_code_; + // HTTP/2 headers from most recent response. + std::string latest_response_headers_; + // HTTP/2 headers from most recent response. + SpdyHeaderBlock latest_response_header_block_; + // Body of most recent response. + std::string latest_response_body_; + // HTTP/2 trailers from most recent response. + std::string latest_response_trailers_; + + // Listens for full responses. + std::unique_ptr<ResponseListener> response_listener_; + + // Keeps track of any data that must be resent upon a subsequent successful + // connection, in case the client receives a stateless reject. + std::vector<std::unique_ptr<QuicDataToResend>> data_to_resend_on_connect_; + + std::unique_ptr<ClientQuicDataToResend> push_promise_data_to_resend_; DISALLOW_COPY_AND_ASSIGN(QuicClientBase); }; diff --git a/chromium/net/tools/quic/quic_client_bin.cc b/chromium/net/tools/quic/quic_client_bin.cc index 26820af6e5d..07ff2fc5c98 100644 --- a/chromium/net/tools/quic/quic_client_bin.cc +++ b/chromium/net/tools/quic/quic_client_bin.cc @@ -55,12 +55,11 @@ #include "net/cert/cert_verifier.h" #include "net/cert/multi_log_ct_verifier.h" #include "net/http/transport_security_state.h" -#include "net/log/net_log.h" -#include "net/quic/crypto/proof_verifier_chromium.h" -#include "net/quic/quic_flags.h" -#include "net/quic/quic_protocol.h" -#include "net/quic/quic_server_id.h" -#include "net/quic/quic_utils.h" +#include "net/quic/chromium/crypto/proof_verifier_chromium.h" +#include "net/quic/core/quic_flags.h" +#include "net/quic/core/quic_protocol.h" +#include "net/quic/core/quic_server_id.h" +#include "net/quic/core/quic_utils.h" #include "net/spdy/spdy_header_block.h" #include "net/tools/epoll_server/epoll_server.h" #include "net/tools/quic/quic_client.h" @@ -113,7 +112,7 @@ class FakeCertVerifier : public net::CertVerifier { net::CertVerifyResult* verify_result, const net::CompletionCallback& callback, std::unique_ptr<Request>* out_req, - const net::BoundNetLog& net_log) override { + const net::NetLogWithSource& net_log) override { return net::OK; } @@ -239,7 +238,7 @@ int main(int argc, char* argv[]) { net::EpollServer epoll_server; net::QuicServerId server_id(url.host(), url.EffectiveIntPort(), net::PRIVACY_MODE_DISABLED); - net::QuicVersionVector versions = net::QuicSupportedVersions(); + net::QuicVersionVector versions = net::AllSupportedVersions(); if (FLAGS_quic_version != -1) { versions.clear(); versions.push_back(static_cast<net::QuicVersion>(FLAGS_quic_version)); @@ -254,11 +253,12 @@ int main(int argc, char* argv[]) { transport_security_state.reset(new TransportSecurityState); std::unique_ptr<CTVerifier> ct_verifier(new MultiLogCTVerifier()); std::unique_ptr<CTPolicyEnforcer> ct_policy_enforcer(new CTPolicyEnforcer()); - ProofVerifierChromium* proof_verifier = new ProofVerifierChromium( - cert_verifier.get(), ct_policy_enforcer.get(), - transport_security_state.get(), ct_verifier.get()); - net::QuicClient client(net::IPEndPoint(ip_addr, FLAGS_port), server_id, - versions, &epoll_server, proof_verifier); + std::unique_ptr<net::ProofVerifierChromium> proof_verifier( + new ProofVerifierChromium(cert_verifier.get(), ct_policy_enforcer.get(), + transport_security_state.get(), + ct_verifier.get())); + net::QuicClient client(net::IPEndPoint(ip_addr, port), server_id, versions, + &epoll_server, std::move(proof_verifier)); client.set_initial_max_packet_length( FLAGS_initial_mtu != 0 ? FLAGS_initial_mtu : net::kDefaultMaxPacketSize); if (!client.Initialize()) { @@ -312,11 +312,10 @@ int main(int argc, char* argv[]) { // Make sure to store the response, for later output. client.set_store_response(true); - // Send the request. net::SpdyHeaderBlock header_block = net::SpdyBalsaUtils::RequestHeadersToSpdyHeaders(headers); - client.SendRequestAndWaitForResponse(headers, body, /*fin=*/true); + client.SendRequestAndWaitForResponse(header_block, body, /*fin=*/true); // Print request and response details. if (!FLAGS_quiet) { @@ -324,9 +323,8 @@ int main(int argc, char* argv[]) { cout << "headers:" << header_block.DebugString(); if (!FLAGS_body_hex.empty()) { // Print the user provided hex, rather than binary body. - cout << "body hex: " << FLAGS_body_hex << endl; - cout << "body ascii: " << net::QuicUtils::BinaryToAscii( - net::QuicUtils::HexDecode(FLAGS_body_hex)) + cout << "body:\n" + << net::QuicUtils::HexDump(net::QuicUtils::HexDecode(FLAGS_body_hex)) << endl; } else { cout << "body: " << body << endl; @@ -337,10 +335,7 @@ int main(int argc, char* argv[]) { string response_body = client.latest_response_body(); if (!FLAGS_body_hex.empty()) { // Assume response is binary data. - cout << "body hex: " << net::QuicUtils::HexEncode(response_body) - << endl; - cout << "body ascii: " << net::QuicUtils::BinaryToAscii(response_body) - << endl; + cout << "body:\n" << net::QuicUtils::HexDump(response_body) << endl; } else { cout << "body: " << response_body << endl; } diff --git a/chromium/net/tools/quic/quic_client_session.cc b/chromium/net/tools/quic/quic_client_session.cc index a0e11b1cfb4..48ed1d0950c 100644 --- a/chromium/net/tools/quic/quic_client_session.cc +++ b/chromium/net/tools/quic/quic_client_session.cc @@ -5,9 +5,11 @@ #include "net/tools/quic/quic_client_session.h" #include "base/logging.h" -#include "net/quic/crypto/crypto_protocol.h" -#include "net/quic/crypto/proof_verifier_chromium.h" -#include "net/quic/quic_server_id.h" +#include "net/log/net_log_with_source.h" +#include "net/quic/chromium/crypto/proof_verifier_chromium.h" +#include "net/quic/core/crypto/crypto_protocol.h" +#include "net/quic/core/quic_bug_tracker.h" +#include "net/quic/core/quic_server_id.h" #include "net/tools/quic/quic_spdy_client_stream.h" using std::string; @@ -90,7 +92,7 @@ int QuicClientSession::GetNumReceivedServerConfigUpdates() const { bool QuicClientSession::ShouldCreateIncomingDynamicStream(QuicStreamId id) { if (!connection()->connected()) { - LOG(DFATAL) << "ShouldCreateIncomingDynamicStream called when disconnected"; + QUIC_BUG << "ShouldCreateIncomingDynamicStream called when disconnected"; return false; } if (goaway_received() && respect_goaway_) { @@ -121,7 +123,7 @@ QuicSpdyStream* QuicClientSession::CreateIncomingDynamicStream( QuicCryptoClientStreamBase* QuicClientSession::CreateQuicCryptoStream() { return new QuicCryptoClientStream( - server_id_, this, new ProofVerifyContextChromium(0, BoundNetLog()), + server_id_, this, new ProofVerifyContextChromium(0, NetLogWithSource()), crypto_config_, this); } diff --git a/chromium/net/tools/quic/quic_client_session.h b/chromium/net/tools/quic/quic_client_session.h index fc5abbf12f6..af9ee0952f6 100644 --- a/chromium/net/tools/quic/quic_client_session.h +++ b/chromium/net/tools/quic/quic_client_session.h @@ -11,9 +11,9 @@ #include <string> #include "base/macros.h" -#include "net/quic/quic_client_session_base.h" -#include "net/quic/quic_crypto_client_stream.h" -#include "net/quic/quic_protocol.h" +#include "net/quic/core/quic_client_session_base.h" +#include "net/quic/core/quic_crypto_client_stream.h" +#include "net/quic/core/quic_protocol.h" #include "net/tools/quic/quic_spdy_client_stream.h" namespace net { @@ -24,7 +24,8 @@ class ReliableQuicStream; class QuicClientSession : public QuicClientSessionBase { public: - // Caller retains ownership of |promised_by_url|. + // Takes ownership of |connection|. Caller retains ownership of + // |promised_by_url|. QuicClientSession(const QuicConfig& config, QuicConnection* connection, const QuicServerId& server_id, diff --git a/chromium/net/tools/quic/quic_client_session_test.cc b/chromium/net/tools/quic/quic_client_session_test.cc index a38214c8b28..75d02cb8eea 100644 --- a/chromium/net/tools/quic/quic_client_session_test.cc +++ b/chromium/net/tools/quic/quic_client_session_test.cc @@ -8,9 +8,9 @@ #include "base/strings/stringprintf.h" #include "net/base/ip_endpoint.h" -#include "net/quic/crypto/aes_128_gcm_12_encrypter.h" -#include "net/quic/quic_flags.h" -#include "net/quic/spdy_utils.h" +#include "net/quic/core/crypto/aes_128_gcm_12_encrypter.h" +#include "net/quic/core/quic_flags.h" +#include "net/quic/core/spdy_utils.h" #include "net/quic/test_tools/crypto_test_utils.h" #include "net/quic/test_tools/mock_quic_spdy_client_stream.h" #include "net/quic/test_tools/quic_config_peer.h" @@ -36,7 +36,6 @@ using net::test::QuicPacketCreatorPeer; using net::test::QuicSpdySessionPeer; using net::test::SupportedVersions; using net::test::TestPeerIPAddress; -using net::test::ValueRestore; using net::test::kClientDataStreamId1; using net::test::kServerDataStreamId1; using net::test::kTestPort; @@ -141,7 +140,7 @@ class QuicClientSessionTest : public ::testing::TestWithParam<QuicVersion> { INSTANTIATE_TEST_CASE_P(Tests, QuicClientSessionTest, - ::testing::ValuesIn(QuicSupportedVersions())); + ::testing::ValuesIn(AllSupportedVersions())); TEST_P(QuicClientSessionTest, CryptoConnect) { CompleteCryptoHandshake(); diff --git a/chromium/net/tools/quic/quic_client_test.cc b/chromium/net/tools/quic/quic_client_test.cc index dca06bddae2..dd360de33a7 100644 --- a/chromium/net/tools/quic/quic_client_test.cc +++ b/chromium/net/tools/quic/quic_client_test.cc @@ -43,7 +43,7 @@ QuicClient* CreateAndInitializeQuicClient(EpollServer* eps, uint16_t port) { IPEndPoint server_address(IPEndPoint(net::test::Loopback4(), port)); QuicServerId server_id("hostname", server_address.port(), PRIVACY_MODE_DISABLED); - QuicVersionVector versions = QuicSupportedVersions(); + QuicVersionVector versions = AllSupportedVersions(); QuicClient* client = new QuicClient(server_address, server_id, versions, eps, CryptoTestUtils::ProofVerifierForTesting()); @@ -54,7 +54,7 @@ QuicClient* CreateAndInitializeQuicClient(EpollServer* eps, uint16_t port) { TEST(QuicClientTest, DoNotLeakFDs) { // Create a ProofVerifier before counting the number of open FDs to work // around some ASAN weirdness. - delete CryptoTestUtils::ProofVerifierForTesting(); + CryptoTestUtils::ProofVerifierForTesting().reset(); // Make sure that the QuicClient doesn't leak FDs. Doing so could cause port // exhaustion in long running processes which repeatedly create clients. @@ -81,7 +81,7 @@ TEST(QuicClientTest, DoNotLeakFDs) { TEST(QuicClientTest, CreateAndCleanUpUDPSockets) { // Create a ProofVerifier before counting the number of open FDs to work // around some ASAN weirdness. - delete CryptoTestUtils::ProofVerifierForTesting(); + CryptoTestUtils::ProofVerifierForTesting().reset(); EpollServer eps; int number_of_open_fds = NumOpenFDs(); diff --git a/chromium/net/tools/quic/quic_default_packet_writer.h b/chromium/net/tools/quic/quic_default_packet_writer.h index 0a8f1093273..e695637b470 100644 --- a/chromium/net/tools/quic/quic_default_packet_writer.h +++ b/chromium/net/tools/quic/quic_default_packet_writer.h @@ -9,7 +9,8 @@ #include "base/macros.h" #include "net/base/ip_endpoint.h" -#include "net/quic/quic_packet_writer.h" +#include "net/base/net_export.h" +#include "net/quic/core/quic_packet_writer.h" namespace net { @@ -17,7 +18,7 @@ struct WriteResult; // Default packet writer which wraps QuicSocketUtils WritePacket. -class QuicDefaultPacketWriter : public QuicPacketWriter { +class NET_EXPORT_PRIVATE QuicDefaultPacketWriter : public QuicPacketWriter { public: explicit QuicDefaultPacketWriter(int fd); ~QuicDefaultPacketWriter() override; diff --git a/chromium/net/tools/quic/quic_dispatcher.cc b/chromium/net/tools/quic/quic_dispatcher.cc index 70f2af14411..b43b8e1664f 100644 --- a/chromium/net/tools/quic/quic_dispatcher.cc +++ b/chromium/net/tools/quic/quic_dispatcher.cc @@ -10,21 +10,28 @@ #include "base/logging.h" #include "base/macros.h" #include "base/stl_util.h" -#include "net/quic/crypto/quic_random.h" -#include "net/quic/quic_bug_tracker.h" -#include "net/quic/quic_flags.h" -#include "net/quic/quic_utils.h" +#include "net/quic/core/crypto/quic_random.h" +#include "net/quic/core/quic_bug_tracker.h" +#include "net/quic/core/quic_flags.h" +#include "net/quic/core/quic_utils.h" + #include "net/tools/quic/chlo_extractor.h" #include "net/tools/quic/quic_per_connection_packet_writer.h" #include "net/tools/quic/quic_simple_server_session.h" +#include "net/tools/quic/quic_simple_server_session.h" #include "net/tools/quic/quic_time_wait_list_manager.h" #include "net/tools/quic/stateless_rejector.h" using base::StringPiece; +using std::list; using std::string; namespace net { +typedef QuicBufferedPacketStore::BufferedPacket BufferedPacket; +typedef QuicBufferedPacketStore::BufferedPacketList BufferedPacketList; +typedef QuicBufferedPacketStore::EnqueuePacketResult EnqueuePacketResult; + namespace { // An alarm that informs the QuicDispatcher to delete old sessions. @@ -150,7 +157,7 @@ class StatelessConnectionTerminator { // to give the QuicDispatcher a chance to apply policy checks to the CHLO. class ChloValidator : public ChloExtractor::Delegate { public: - ChloValidator(QuicServerSessionBase::Helper* helper, + ChloValidator(QuicCryptoServerStream::Helper* helper, IPEndPoint self_address, StatelessRejector* rejector) : helper_(helper), @@ -175,7 +182,7 @@ class ChloValidator : public ChloExtractor::Delegate { const string& error_details() const { return error_details_; } private: - QuicServerSessionBase::Helper* helper_; // Unowned. + QuicCryptoServerStream::Helper* helper_; // Unowned. IPEndPoint self_address_; StatelessRejector* rejector_; // Unowned. bool can_accept_; @@ -187,9 +194,9 @@ class ChloValidator : public ChloExtractor::Delegate { QuicDispatcher::QuicDispatcher( const QuicConfig& config, const QuicCryptoServerConfig* crypto_config, - const QuicVersionVector& supported_versions, + QuicVersionManager* version_manager, std::unique_ptr<QuicConnectionHelperInterface> helper, - std::unique_ptr<QuicServerSessionBase::Helper> session_helper, + std::unique_ptr<QuicCryptoServerStream::Helper> session_helper, std::unique_ptr<QuicAlarmFactory> alarm_factory) : config_(config), crypto_config_(crypto_config), @@ -200,20 +207,20 @@ QuicDispatcher::QuicDispatcher( alarm_factory_(std::move(alarm_factory)), delete_sessions_alarm_( alarm_factory_->CreateAlarm(new DeleteSessionsAlarm(this))), - supported_versions_(supported_versions), - disable_quic_pre_30_(FLAGS_quic_disable_pre_30), - allowed_supported_versions_(supported_versions), + buffered_packets_(this, helper_->GetClock(), alarm_factory_.get()), current_packet_(nullptr), - framer_(supported_versions, + version_manager_(version_manager), + framer_(GetSupportedVersions(), /*unused*/ QuicTime::Zero(), Perspective::IS_SERVER), - last_error_(QUIC_NO_ERROR) { + last_error_(QUIC_NO_ERROR), + new_sessions_allowed_per_event_loop_(0u) { framer_.set_visitor(this); } QuicDispatcher::~QuicDispatcher() { - STLDeleteValues(&session_map_); - STLDeleteElements(&closed_session_list_); + base::STLDeleteValues(&session_map_); + base::STLDeleteElements(&closed_session_list_); } void QuicDispatcher::InitializeWithWriter(QuicPacketWriter* writer) { @@ -260,11 +267,18 @@ bool QuicDispatcher::OnUnauthenticatedPublicHeader( QuicConnectionId connection_id = header.connection_id; SessionMap::iterator it = session_map_.find(connection_id); if (it != session_map_.end()) { + DCHECK(!buffered_packets_.HasBufferedPackets(connection_id)); it->second->ProcessUdpPacket(current_server_address_, current_client_address_, *current_packet_); return false; } + if (FLAGS_quic_buffer_packets_after_chlo && + buffered_packets_.HasChloForConnection(connection_id)) { + BufferEarlyPacket(connection_id); + return false; + } + if (!OnUnauthenticatedUnknownPublicHeader(header)) { return false; } @@ -319,29 +333,35 @@ bool QuicDispatcher::OnUnauthenticatedHeader(const QuicPacketHeader& header) { return false; } - // Packet's connection ID is unknown. - // Apply the validity checks. + // Packet's connection ID is unknown. Apply the validity checks. QuicPacketFate fate = ValidityChecks(header); if (fate == kFateProcess) { - fate = MaybeRejectStatelessly(connection_id, header); + // Execute stateless rejection logic to determine the packet fate, then + // invoke ProcessUnauthenticatedHeaderFate. + MaybeRejectStatelessly(connection_id, header); + } else { + // If the fate is already known, process it without executing stateless + // rejection logic. + ProcessUnauthenticatedHeaderFate(fate, connection_id, header.packet_number); } + + return false; +} + +void QuicDispatcher::ProcessUnauthenticatedHeaderFate( + QuicPacketFate fate, + QuicConnectionId connection_id, + QuicPacketNumber packet_number) { switch (fate) { case kFateProcess: { - // Create a session and process the packet. - QuicServerSessionBase* session = - CreateQuicSession(connection_id, current_client_address_); - DVLOG(1) << "Created new session for " << connection_id; - session_map_.insert(std::make_pair(connection_id, session)); - session->ProcessUdpPacket(current_server_address_, - current_client_address_, *current_packet_); + ProcessChlo(); break; } case kFateTimeWait: // MaybeRejectStatelessly might have already added the connection to // time wait, in which case it should not be added again. if (!FLAGS_quic_use_cheap_stateless_rejects || - !time_wait_list_manager_->IsConnectionIdInTimeWait( - header.public_header.connection_id)) { + !time_wait_list_manager_->IsConnectionIdInTimeWait(connection_id)) { // Add this connection_id to the time-wait state, to safely reject // future packets. DVLOG(1) << "Adding connection ID " << connection_id @@ -350,19 +370,20 @@ bool QuicDispatcher::OnUnauthenticatedHeader(const QuicPacketHeader& header) { connection_id, framer_.version(), /*connection_rejected_statelessly=*/false, nullptr); } - DCHECK(time_wait_list_manager_->IsConnectionIdInTimeWait( - header.public_header.connection_id)); + DCHECK(time_wait_list_manager_->IsConnectionIdInTimeWait(connection_id)); time_wait_list_manager_->ProcessPacket( - current_server_address_, current_client_address_, - header.public_header.connection_id, header.packet_number, - *current_packet_); + current_server_address_, current_client_address_, connection_id, + packet_number, *current_packet_); + break; + case kFateBuffer: + // This packet is a non-CHLO packet which has arrived out of order. + // Buffer it. + BufferEarlyPacket(connection_id); break; case kFateDrop: // Do nothing with the packet. break; } - - return false; } QuicDispatcher::QuicPacketFate QuicDispatcher::ValidityChecks( @@ -412,7 +433,7 @@ void QuicDispatcher::CleanUpSession(SessionMap::iterator it, } void QuicDispatcher::DeleteSessions() { - STLDeleteElements(&closed_session_list_); + base::STLDeleteElements(&closed_session_list_); } void QuicDispatcher::OnCanWrite() { @@ -463,8 +484,8 @@ void QuicDispatcher::OnConnectionClosed(QuicConnectionId connection_id, << ", with details: " << error_details; if (closed_session_list_.empty()) { - delete_sessions_alarm_->Cancel(); - delete_sessions_alarm_->Set(helper()->GetClock()->ApproximateNow()); + delete_sessions_alarm_->Update(helper()->GetClock()->ApproximateNow(), + QuicTime::Delta::Zero()); } closed_session_list_.push_back(it->second); const bool should_close_statelessly = @@ -594,20 +615,49 @@ void QuicDispatcher::OnPacketComplete() { DCHECK(false); } -QuicServerSessionBase* QuicDispatcher::CreateQuicSession( +void QuicDispatcher::OnExpiredPackets( QuicConnectionId connection_id, - const IPEndPoint& client_address) { - // The QuicServerSessionBase takes ownership of |connection| below. - QuicConnection* connection = new QuicConnection( - connection_id, client_address, helper_.get(), alarm_factory_.get(), - CreatePerConnectionWriter(), - /* owns_writer= */ true, Perspective::IS_SERVER, GetSupportedVersions()); + BufferedPacketList early_arrived_packets) { + time_wait_list_manager_->AddConnectionIdToTimeWait( + connection_id, framer_.version(), false, nullptr); +} + +void QuicDispatcher::ProcessBufferedChlos(size_t max_connections_to_create) { + // Reset the counter before starting creating connections. + new_sessions_allowed_per_event_loop_ = max_connections_to_create; + for (; new_sessions_allowed_per_event_loop_ > 0; + --new_sessions_allowed_per_event_loop_) { + QuicConnectionId connection_id; + list<BufferedPacket> packets = + buffered_packets_.DeliverPacketsForNextConnection(&connection_id); + if (packets.empty()) { + return; + } + QuicServerSessionBase* session = + CreateQuicSession(connection_id, packets.front().client_address); + DVLOG(1) << "Created new session for " << connection_id; + session_map_.insert(std::make_pair(connection_id, session)); + DeliverPacketsToSession(packets, session); + } +} - QuicServerSessionBase* session = new QuicSimpleServerSession( - config_, connection, this, session_helper_.get(), crypto_config_, - &compressed_certs_cache_); - session->Initialize(); - return session; +bool QuicDispatcher::HasChlosBuffered() const { + return buffered_packets_.HasChlosBuffered(); +} + +void QuicDispatcher::OnNewConnectionAdded(QuicConnectionId connection_id) { + VLOG(1) << "Received packet from new connection " << connection_id; +} + +// Return true if there is any packet buffered in the store. +bool QuicDispatcher::HasBufferedPackets(QuicConnectionId connection_id) { + return buffered_packets_.HasBufferedPackets(connection_id); +} + +void QuicDispatcher::OnBufferPacketFailure(EnqueuePacketResult result, + QuicConnectionId connection_id) { + DVLOG(1) << "Fail to buffer packet on connection " << connection_id + << " because of " << result; } void QuicDispatcher::OnConnectionRejectedStatelessly() {} @@ -623,6 +673,79 @@ QuicTimeWaitListManager* QuicDispatcher::CreateQuicTimeWaitListManager() { alarm_factory_.get()); } +void QuicDispatcher::BufferEarlyPacket(QuicConnectionId connection_id) { + bool is_new_connection = !buffered_packets_.HasBufferedPackets(connection_id); + EnqueuePacketResult rs = buffered_packets_.EnqueuePacket( + connection_id, *current_packet_, current_server_address_, + current_client_address_, /*is_chlo=*/false); + if (rs != EnqueuePacketResult::SUCCESS) { + OnBufferPacketFailure(rs, connection_id); + } else if (is_new_connection) { + OnNewConnectionAdded(connection_id); + } +} + +void QuicDispatcher::ProcessChlo() { + QUIC_BUG_IF(!FLAGS_quic_buffer_packet_till_chlo && + FLAGS_quic_limit_num_new_sessions_per_epoll_loop) + << "Try to limit connection creation per epoll event while not " + "supporting packet buffer. " + "--quic_limit_num_new_sessions_per_epoll_loop = true " + "--quic_buffer_packet_till_chlo = false"; + + if (FLAGS_quic_limit_num_new_sessions_per_epoll_loop && + FLAGS_quic_buffer_packet_till_chlo && + new_sessions_allowed_per_event_loop_ <= 0) { + // Can't create new session any more. Wait till next event loop. + if (!buffered_packets_.HasChloForConnection(current_connection_id_)) { + // Only buffer one CHLO per connection. Remove this condition check when + // --gfe2_reloadable_flag_quic_buffer_packets_after_chlo + // is deprecated because after that retransmitted CHLO should be buffered + // earlier in OnUnauthenticatedPublicHeader(). + bool is_new_connection = + !buffered_packets_.HasBufferedPackets(current_connection_id_); + EnqueuePacketResult rs = buffered_packets_.EnqueuePacket( + current_connection_id_, *current_packet_, current_server_address_, + current_client_address_, /*is_chlo=*/true); + if (rs != EnqueuePacketResult::SUCCESS) { + OnBufferPacketFailure(rs, current_connection_id_); + } else if (is_new_connection) { + OnNewConnectionAdded(current_connection_id_); + } + } + return; + } + + // Creates a new session and process all buffered packets for this connection. + QuicServerSessionBase* session = + CreateQuicSession(current_connection_id_, current_client_address_); + if (FLAGS_quic_enforce_mtu_limit && + current_packet().potentially_small_mtu()) { + session->connection()->set_largest_packet_size_supported( + kMinimumSupportedPacketSize); + } + DVLOG(1) << "Created new session for " << current_connection_id_; + session_map_.insert(std::make_pair(current_connection_id_, session)); + std::list<BufferedPacket> packets = + buffered_packets_.DeliverPackets(current_connection_id_); + // Check if CHLO is the first packet arrived on this connection. + if (FLAGS_quic_buffer_packet_till_chlo && packets.empty()) { + OnNewConnectionAdded(current_connection_id_); + } + // Process CHLO at first. + session->ProcessUdpPacket(current_server_address_, current_client_address_, + *current_packet_); + + // Deliver queued-up packets in the same order as they arrived. + // Do this even when flag is off because there might be still some packets + // buffered in the store before flag is turned off. + DeliverPacketsToSession(packets, session); + if (FLAGS_quic_limit_num_new_sessions_per_epoll_loop && + FLAGS_quic_buffer_packet_till_chlo) { + --new_sessions_allowed_per_event_loop_; + } +} + bool QuicDispatcher::HandlePacketForTimeWait( const QuicPacketPublicHeader& header) { if (header.reset_flag) { @@ -653,30 +776,70 @@ bool QuicDispatcher::OnUnauthenticatedUnknownPublicHeader( return true; } -QuicDispatcher::QuicPacketFate QuicDispatcher::MaybeRejectStatelessly( - QuicConnectionId connection_id, - const QuicPacketHeader& header) { +class StatelessRejectorProcessDoneCallback + : public StatelessRejector::ProcessDoneCallback { + public: + StatelessRejectorProcessDoneCallback(QuicDispatcher* dispatcher, + QuicPacketNumber packet_number, + QuicVersion first_version) + : dispatcher_(dispatcher), + packet_number_(packet_number), + first_version_(first_version) {} + + void Run(std::unique_ptr<StatelessRejector> rejector) override { + dispatcher_->OnStatelessRejectorProcessDone(std::move(rejector), + packet_number_, first_version_); + } + + private: + QuicDispatcher* dispatcher_; + QuicPacketNumber packet_number_; + QuicVersion first_version_; +}; + +void QuicDispatcher::MaybeRejectStatelessly(QuicConnectionId connection_id, + const QuicPacketHeader& header) { // TODO(rch): This logic should probably live completely inside the rejector. if (!FLAGS_quic_use_cheap_stateless_rejects || !FLAGS_enable_quic_stateless_reject_support || header.public_header.versions.front() <= QUIC_VERSION_32 || !ShouldAttemptCheapStatelessRejection()) { - return kFateProcess; + // Not use cheap stateless reject. + if (FLAGS_quic_buffer_packet_till_chlo && + !ChloExtractor::Extract(*current_packet_, GetSupportedVersions(), + nullptr)) { + // Buffer non-CHLO packets. + ProcessUnauthenticatedHeaderFate(kFateBuffer, connection_id, + header.packet_number); + return; + } + ProcessUnauthenticatedHeaderFate(kFateProcess, connection_id, + header.packet_number); + return; } - StatelessRejector rejector(header.public_header.versions.front(), - supported_versions_, crypto_config_, - &compressed_certs_cache_, helper()->GetClock(), - helper()->GetRandomGenerator(), - current_client_address_, current_server_address_); + std::unique_ptr<StatelessRejector> rejector(new StatelessRejector( + header.public_header.versions.front(), GetSupportedVersions(), + crypto_config_, &compressed_certs_cache_, helper()->GetClock(), + helper()->GetRandomGenerator(), current_packet_->length(), + current_client_address_, current_server_address_)); ChloValidator validator(session_helper_.get(), current_server_address_, - &rejector); - if (!ChloExtractor::Extract(*current_packet_, supported_versions_, + rejector.get()); + if (!ChloExtractor::Extract(*current_packet_, GetSupportedVersions(), &validator)) { - // TODO(rch): Since there was no CHLO in this packet, buffer it until one - // arrives. - DLOG(ERROR) << "Dropping undecryptable packet."; - return kFateDrop; + if (!FLAGS_quic_buffer_packet_till_chlo) { + QUIC_BUG + << "Have to drop packet because buffering non-chlo packet is " + "not supported while trying to do stateless reject. " + "--gfe2_reloadable_flag_quic_buffer_packet_till_chlo false" + " --gfe2_reloadable_flag_quic_use_cheap_stateless_rejects true"; + ProcessUnauthenticatedHeaderFate(kFateDrop, connection_id, + header.packet_number); + return; + } + ProcessUnauthenticatedHeaderFate(kFateBuffer, connection_id, + header.packet_number); + return; } if (!validator.can_accept()) { @@ -686,51 +849,76 @@ QuicDispatcher::QuicPacketFate QuicDispatcher::MaybeRejectStatelessly( terminator.CloseConnection(QUIC_HANDSHAKE_FAILED, validator.error_details()); OnConnectionClosedStatelessly(QUIC_HANDSHAKE_FAILED); - return kFateTimeWait; + ProcessUnauthenticatedHeaderFate(kFateTimeWait, connection_id, + header.packet_number); + return; } - // This packet included a CHLO. See if it can be rejected statelessly. - switch (rejector.state()) { + // Continue stateless rejector processing + std::unique_ptr<StatelessRejectorProcessDoneCallback> cb( + new StatelessRejectorProcessDoneCallback( + this, header.packet_number, header.public_header.versions.front())); + StatelessRejector::Process(std::move(rejector), std::move(cb)); +} + +void QuicDispatcher::OnStatelessRejectorProcessDone( + std::unique_ptr<StatelessRejector> rejector, + QuicPacketNumber packet_number, + QuicVersion first_version) { + QuicPacketFate fate; + switch (rejector->state()) { case StatelessRejector::FAILED: { // There was an error processing the client hello. - StatelessConnectionTerminator terminator( - connection_id, &framer_, helper(), time_wait_list_manager_.get()); - terminator.CloseConnection(rejector.error(), rejector.error_details()); - return kFateTimeWait; + StatelessConnectionTerminator terminator(rejector->connection_id(), + &framer_, helper(), + time_wait_list_manager_.get()); + terminator.CloseConnection(rejector->error(), rejector->error_details()); + fate = kFateTimeWait; + break; } case StatelessRejector::UNSUPPORTED: // Cheap stateless rejects are not supported so process the packet. - return kFateProcess; + fate = kFateProcess; + break; case StatelessRejector::ACCEPTED: // Contains a valid CHLO, so process the packet and create a connection. - return kFateProcess; + fate = kFateProcess; + break; case StatelessRejector::REJECTED: { - DCHECK_EQ(framer_.version(), header.public_header.versions.front()); - StatelessConnectionTerminator terminator( - connection_id, &framer_, helper(), time_wait_list_manager_.get()); + DCHECK_EQ(framer_.version(), first_version); + StatelessConnectionTerminator terminator(rejector->connection_id(), + &framer_, helper(), + time_wait_list_manager_.get()); terminator.RejectConnection( - rejector.reply().GetSerialized().AsStringPiece()); + rejector->reply().GetSerialized().AsStringPiece()); OnConnectionRejectedStatelessly(); - return kFateTimeWait; + fate = kFateTimeWait; + break; } - } - QUIC_BUG << "Rejector has unknown invalid state."; - return kFateDrop; + default: + QUIC_BUG << "Rejector has invalid state " << rejector->state(); + fate = kFateDrop; + break; + } + ProcessUnauthenticatedHeaderFate(fate, rejector->connection_id(), + packet_number); } const QuicVersionVector& QuicDispatcher::GetSupportedVersions() { - // Filter (or un-filter) the list of supported versions based on the flag. - if (disable_quic_pre_30_ != FLAGS_quic_disable_pre_30) { - DCHECK_EQ(supported_versions_.capacity(), - allowed_supported_versions_.capacity()); - disable_quic_pre_30_ = FLAGS_quic_disable_pre_30; - supported_versions_ = FilterSupportedVersions(allowed_supported_versions_); - } - return supported_versions_; + return version_manager_->GetSupportedVersions(); +} + +void QuicDispatcher::DeliverPacketsToSession( + const std::list<BufferedPacket>& packets, + QuicServerSessionBase* session) { + for (const BufferedPacket& packet : packets) { + session->ProcessUdpPacket(packet.server_address, packet.client_address, + *(packet.packet)); + } } } // namespace net diff --git a/chromium/net/tools/quic/quic_dispatcher.h b/chromium/net/tools/quic/quic_dispatcher.h index db972530435..8d9197a4806 100644 --- a/chromium/net/tools/quic/quic_dispatcher.h +++ b/chromium/net/tools/quic/quic_dispatcher.h @@ -15,20 +15,23 @@ #include "base/macros.h" #include "net/base/ip_endpoint.h" #include "net/base/linked_hash_map.h" -#include "net/quic/crypto/quic_compressed_certs_cache.h" -#include "net/quic/crypto/quic_random.h" -#include "net/quic/quic_blocked_writer_interface.h" -#include "net/quic/quic_connection.h" -#include "net/quic/quic_protocol.h" -#include "net/quic/quic_server_session_base.h" +#include "net/quic/core/crypto/quic_compressed_certs_cache.h" +#include "net/quic/core/crypto/quic_random.h" +#include "net/quic/core/quic_blocked_writer_interface.h" +#include "net/quic/core/quic_buffered_packet_store.h" +#include "net/quic/core/quic_connection.h" +#include "net/quic/core/quic_crypto_server_stream.h" +#include "net/quic/core/quic_protocol.h" +#include "net/quic/core/quic_server_session_base.h" + #include "net/tools/quic/quic_process_packet_interface.h" #include "net/tools/quic/quic_time_wait_list_manager.h" +#include "net/tools/quic/stateless_rejector.h" namespace net { class QuicConfig; class QuicCryptoServerConfig; -class QuicServerSessionBase; namespace test { class QuicDispatcherPeer; @@ -37,7 +40,8 @@ class QuicDispatcherPeer; class QuicDispatcher : public QuicServerSessionBase::Visitor, public ProcessPacketInterface, public QuicBlockedWriterInterface, - public QuicFramerVisitorInterface { + public QuicFramerVisitorInterface, + public QuicBufferedPacketStore::VisitorInterface { public: // Ideally we'd have a linked_hash_set: the boolean is unused. typedef linked_hash_map<QuicBlockedWriterInterface*, @@ -47,9 +51,9 @@ class QuicDispatcher : public QuicServerSessionBase::Visitor, QuicDispatcher(const QuicConfig& config, const QuicCryptoServerConfig* crypto_config, - const QuicVersionVector& supported_versions, + QuicVersionManager* version_manager, std::unique_ptr<QuicConnectionHelperInterface> helper, - std::unique_ptr<QuicServerSessionBase::Helper> session_helper, + std::unique_ptr<QuicCryptoServerStream::Helper> session_helper, std::unique_ptr<QuicAlarmFactory> alarm_factory); ~QuicDispatcher() override; @@ -85,6 +89,9 @@ class QuicDispatcher : public QuicServerSessionBase::Visitor, // time-wait list. void OnConnectionAddedToTimeWaitList(QuicConnectionId connection_id) override; + void OnPacketBeingDispatchedToSession( + QuicServerSessionBase* session) override {} + typedef std::unordered_map<QuicConnectionId, QuicServerSessionBase*> SessionMap; @@ -139,10 +146,21 @@ class QuicDispatcher : public QuicServerSessionBase::Visitor, bool OnPathCloseFrame(const QuicPathCloseFrame& frame) override; void OnPacketComplete() override; + // QuicBufferedPacketStore::VisitorInterface implementation. + void OnExpiredPackets(QuicConnectionId connection_id, + QuicBufferedPacketStore::BufferedPacketList + early_arrived_packets) override; + + // Create connections for previously buffered CHLOs as many as allowed. + virtual void ProcessBufferedChlos(size_t max_connections_to_create); + + // Return true if there is CHLO buffered. + virtual bool HasChlosBuffered() const; + protected: virtual QuicServerSessionBase* CreateQuicSession( QuicConnectionId connection_id, - const IPEndPoint& client_address); + const IPEndPoint& client_address) = 0; // Called when a connection is rejected statelessly. virtual void OnConnectionRejectedStatelessly(); @@ -163,6 +181,8 @@ class QuicDispatcher : public QuicServerSessionBase::Visitor, kFateProcess, // Put the connection ID into time-wait state and send a public reset. kFateTimeWait, + // Buffer the packet. + kFateBuffer, // Drop the packet (ignore and give no response). kFateDrop, }; @@ -176,12 +196,22 @@ class QuicDispatcher : public QuicServerSessionBase::Visitor, // will be owned by the dispatcher as time_wait_list_manager_ virtual QuicTimeWaitListManager* CreateQuicTimeWaitListManager(); + // Called when |current_packet_| is a data packet that has arrived before + // the CHLO or it is any kind of packet while a CHLO on same connection has + // already been in the buffer. + void BufferEarlyPacket(QuicConnectionId connection_id); + + // Called when |current_packet_| is a CHLO packet. Creates a new connection + // and delivers any buffered packets for that connection id. + void ProcessChlo(); + QuicTimeWaitListManager* time_wait_list_manager() { return time_wait_list_manager_.get(); } const QuicVersionVector& GetSupportedVersions(); + QuicConnectionId current_connection_id() { return current_connection_id_; } const IPEndPoint& current_server_address() { return current_server_address_; } const IPEndPoint& current_client_address() { return current_client_address_; } const QuicReceivedPacket& current_packet() { return *current_packet_; } @@ -198,7 +228,7 @@ class QuicDispatcher : public QuicServerSessionBase::Visitor, QuicConnectionHelperInterface* helper() { return helper_.get(); } - QuicServerSessionBase::Helper* session_helper() { + QuicCryptoServerStream::Helper* session_helper() { return session_helper_.get(); } @@ -225,8 +255,21 @@ class QuicDispatcher : public QuicServerSessionBase::Visitor, virtual bool OnUnauthenticatedUnknownPublicHeader( const QuicPacketPublicHeader& header); + // Called when a new connection starts to be handled by this dispatcher. + // Either this connection is created or its packets is buffered while waiting + // for CHLO. + virtual void OnNewConnectionAdded(QuicConnectionId connection_id); + + bool HasBufferedPackets(QuicConnectionId connection_id); + + // Called when BufferEarlyPacket() fail to buffer the packet. + virtual void OnBufferPacketFailure( + QuicBufferedPacketStore::EnqueuePacketResult result, + QuicConnectionId connection_id); + private: friend class net::test::QuicDispatcherPeer; + friend class StatelessRejectorProcessDoneCallback; // Removes the session from the session map and write blocked list, and adds // the ConnectionId to the time-wait list. If |session_closed_statelessly| is @@ -236,11 +279,33 @@ class QuicDispatcher : public QuicServerSessionBase::Visitor, bool HandlePacketForTimeWait(const QuicPacketPublicHeader& header); // Attempts to reject the connection statelessly, if stateless rejects are - // possible and if the current packet contains a CHLO message. - // Returns a fate which describes what subsequent processing should be - // performed on the packets, like ValidityChecks. - QuicPacketFate MaybeRejectStatelessly(QuicConnectionId connection_id, - const QuicPacketHeader& header); + // possible and if the current packet contains a CHLO message. Determines a + // fate which describes what subsequent processing should be performed on the + // packets, like ValidityChecks, and invokes ProcessUnauthenticatedHeaderFate. + void MaybeRejectStatelessly(QuicConnectionId connection_id, + const QuicPacketHeader& header); + + // Deliver |packets| to |session| for further processing. + void DeliverPacketsToSession( + const std::list<QuicBufferedPacketStore::BufferedPacket>& packets, + QuicServerSessionBase* session); + + // Perform the appropriate actions on the current packet based on |fate| - + // either process, buffer, or drop it. + void ProcessUnauthenticatedHeaderFate(QuicPacketFate fate, + QuicConnectionId connection_id, + QuicPacketNumber packet_number); + + // Invoked when StatelessRejector::Process completes. + void OnStatelessRejectorProcessDone( + std::unique_ptr<StatelessRejector> rejector, + QuicPacketNumber packet_number, + QuicVersion first_version); + + void set_new_sessions_allowed_per_event_loop( + int16_t new_sessions_allowed_per_event_loop) { + new_sessions_allowed_per_event_loop_ = new_sessions_allowed_per_event_loop; + } const QuicConfig& config_; @@ -264,7 +329,7 @@ class QuicDispatcher : public QuicServerSessionBase::Visitor, std::unique_ptr<QuicConnectionHelperInterface> helper_; // The helper used for all sessions. - std::unique_ptr<QuicServerSessionBase::Helper> session_helper_; + std::unique_ptr<QuicCryptoServerStream::Helper> session_helper_; // Creates alarms. std::unique_ptr<QuicAlarmFactory> alarm_factory_; @@ -275,17 +340,9 @@ class QuicDispatcher : public QuicServerSessionBase::Visitor, // The writer to write to the socket with. std::unique_ptr<QuicPacketWriter> writer_; - // This vector contains QUIC versions which we currently support. - // This should be ordered such that the highest supported version is the first - // element, with subsequent elements in descending order (versions can be - // skipped as necessary). - QuicVersionVector supported_versions_; - - // FLAGS_quic_disable_pre_30 - bool disable_quic_pre_30_; - // The list of versions that may be supported by this dispatcher. - // |supported_versions| is derived from this list and |disable_quic_pre_30_|. - const QuicVersionVector allowed_supported_versions_; + // Packets which are buffered until a connection can be created to handle + // them. + QuicBufferedPacketStore buffered_packets_; // Information about the packet currently being handled. IPEndPoint current_client_address_; @@ -293,12 +350,19 @@ class QuicDispatcher : public QuicServerSessionBase::Visitor, const QuicReceivedPacket* current_packet_; QuicConnectionId current_connection_id_; + // Used to get the supported versions based on flag. Does not own. + QuicVersionManager* version_manager_; + QuicFramer framer_; // The last error set by SetLastError(), which is called by // framer_visitor_->OnError(). QuicErrorCode last_error_; + // A backward counter of how many new sessions can be create within current + // event loop. When reaches 0, it means can't create sessions for now. + int16_t new_sessions_allowed_per_event_loop_; + DISALLOW_COPY_AND_ASSIGN(QuicDispatcher); }; diff --git a/chromium/net/tools/quic/quic_dispatcher_test.cc b/chromium/net/tools/quic/quic_dispatcher_test.cc index a063a2daf66..23ca8797bae 100644 --- a/chromium/net/tools/quic/quic_dispatcher_test.cc +++ b/chromium/net/tools/quic/quic_dispatcher_test.cc @@ -9,42 +9,53 @@ #include <string> #include "base/macros.h" -#include "base/strings/string_piece.h" -#include "net/quic/crypto/crypto_handshake.h" -#include "net/quic/crypto/quic_crypto_server_config.h" -#include "net/quic/crypto/quic_random.h" -#include "net/quic/quic_chromium_connection_helper.h" -#include "net/quic/quic_crypto_stream.h" -#include "net/quic/quic_flags.h" -#include "net/quic/quic_utils.h" +#include "base/strings/string_number_conversions.h" +#include "net/quic/core/crypto/crypto_handshake.h" +#include "net/quic/core/crypto/quic_crypto_server_config.h" +#include "net/quic/core/crypto/quic_random.h" +#include "net/quic/core/quic_crypto_stream.h" +#include "net/quic/core/quic_flags.h" +#include "net/quic/core/quic_utils.h" #include "net/quic/test_tools/crypto_test_utils.h" +#include "net/quic/test_tools/quic_buffered_packet_store_peer.h" #include "net/quic/test_tools/quic_test_utils.h" +#include "net/test/gtest_util.h" #include "net/tools/epoll_server/epoll_server.h" +#include "net/tools/quic/chlo_extractor.h" #include "net/tools/quic/quic_epoll_alarm_factory.h" #include "net/tools/quic/quic_epoll_connection_helper.h" #include "net/tools/quic/quic_packet_writer_wrapper.h" -#include "net/tools/quic/quic_simple_server_session_helper.h" +#include "net/tools/quic/quic_simple_crypto_server_stream_helper.h" #include "net/tools/quic/quic_time_wait_list_manager.h" +#include "net/tools/quic/stateless_rejector.h" #include "net/tools/quic/test_tools/mock_quic_time_wait_list_manager.h" #include "net/tools/quic/test_tools/quic_dispatcher_peer.h" #include "testing/gmock/include/gmock/gmock.h" +#include "testing/gmock_mutant.h" #include "testing/gtest/include/gtest/gtest.h" +using base::IntToString; using base::StringPiece; using net::EpollServer; using net::test::ConstructEncryptedPacket; using net::test::CryptoTestUtils; using net::test::MockQuicConnection; using net::test::MockQuicConnectionHelper; -using net::test::ValueRestore; +using std::ostream; using std::string; using std::vector; +using testing::CreateFunctor; using testing::DoAll; using testing::InSequence; using testing::Invoke; using testing::WithoutArgs; using testing::_; +static const size_t kDefaultMaxConnectionsInStore = 100; +static const size_t kMaxConnectionsWithoutCHLO = + kDefaultMaxConnectionsInStore / 2; +static const int16_t kMaxNumSessionsToCreate = 16; + namespace net { namespace test { namespace { @@ -62,7 +73,8 @@ class TestQuicSpdyServerSession : public QuicServerSessionBase { crypto_config, compressed_certs_cache), crypto_stream_(QuicServerSessionBase::GetCryptoStream()) {} - ~TestQuicSpdyServerSession() override{}; + + ~TestQuicSpdyServerSession() override { delete connection(); }; MOCK_METHOD3(OnConnectionClosed, void(QuicErrorCode error, @@ -77,7 +89,7 @@ class TestQuicSpdyServerSession : public QuicServerSessionBase { QuicCompressedCertsCache* compressed_certs_cache) override { return new QuicCryptoServerStream( crypto_config, compressed_certs_cache, - FLAGS_enable_quic_stateless_reject_support, this); + FLAGS_enable_quic_stateless_reject_support, this, stream_helper()); } void SetCryptoStream(QuicCryptoServerStream* crypto_stream) { @@ -88,6 +100,10 @@ class TestQuicSpdyServerSession : public QuicServerSessionBase { return crypto_stream_; } + QuicCryptoServerStream::Helper* stream_helper() { + return QuicServerSessionBase::stream_helper(); + } + private: QuicCryptoServerStreamBase* crypto_stream_; @@ -98,15 +114,17 @@ class TestDispatcher : public QuicDispatcher { public: TestDispatcher(const QuicConfig& config, const QuicCryptoServerConfig* crypto_config, + QuicVersionManager* version_manager, EpollServer* eps) : QuicDispatcher( config, crypto_config, - QuicSupportedVersions(), + version_manager, std::unique_ptr<QuicEpollConnectionHelper>( new QuicEpollConnectionHelper(eps, QuicAllocator::BUFFER_POOL)), - std::unique_ptr<QuicServerSessionBase::Helper>( - new QuicSimpleServerSessionHelper(QuicRandom::GetInstance())), + std::unique_ptr<QuicCryptoServerStream::Helper>( + new QuicSimpleCryptoServerStreamHelper( + QuicRandom::GetInstance())), std::unique_ptr<QuicEpollAlarmFactory>( new QuicEpollAlarmFactory(eps))) {} @@ -114,6 +132,8 @@ class TestDispatcher : public QuicDispatcher { QuicServerSessionBase*(QuicConnectionId connection_id, const IPEndPoint& client_address)); + MOCK_METHOD1(OnNewConnectionAdded, void(QuicConnectionId connection_id)); + using QuicDispatcher::current_server_address; using QuicDispatcher::current_client_address; }; @@ -144,43 +164,29 @@ class MockServerConnection : public MockQuicConnection { QuicDispatcher* dispatcher_; }; -QuicServerSessionBase* CreateSession( - QuicDispatcher* dispatcher, - const QuicConfig& config, - QuicConnectionId connection_id, - const IPEndPoint& client_address, - MockQuicConnectionHelper* helper, - MockAlarmFactory* alarm_factory, - const QuicCryptoServerConfig* crypto_config, - QuicCompressedCertsCache* compressed_certs_cache, - TestQuicSpdyServerSession** session) { - MockServerConnection* connection = new MockServerConnection( - connection_id, helper, alarm_factory, dispatcher); - *session = new TestQuicSpdyServerSession(config, connection, crypto_config, - compressed_certs_cache); - connection->set_visitor(*session); - ON_CALL(*connection, CloseConnection(_, _, _)) - .WillByDefault(WithoutArgs(Invoke( - connection, &MockServerConnection::UnregisterOnConnectionClosed))); - EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>((*session)->connection()), - ProcessUdpPacket(_, client_address, _)); - - return *session; -} - class QuicDispatcherTest : public ::testing::Test { public: QuicDispatcherTest() : helper_(&eps_, QuicAllocator::BUFFER_POOL), alarm_factory_(&eps_), + version_manager_(AllSupportedVersions()), crypto_config_(QuicCryptoServerConfig::TESTING, QuicRandom::GetInstance(), CryptoTestUtils::ProofSourceForTesting()), - dispatcher_(config_, &crypto_config_, &eps_), + dispatcher_(new TestDispatcher(config_, + &crypto_config_, + &version_manager_, + &eps_)), time_wait_list_manager_(nullptr), session1_(nullptr), - session2_(nullptr) { - dispatcher_.InitializeWithWriter(new QuicDefaultPacketWriter(1)); + session2_(nullptr), + store_(nullptr) {} + + void SetUp() override { + dispatcher_->InitializeWithWriter(new QuicDefaultPacketWriter(1)); + // Set the counter to some value to start with. + QuicDispatcherPeer::set_new_sessions_allowed_per_event_loop( + dispatcher_.get(), kMaxNumSessionsToCreate); } ~QuicDispatcherTest() override {} @@ -231,8 +237,8 @@ class QuicDispatcherTest : public ::testing::Test { QuicPathId path_id, QuicPacketNumber packet_number) { ProcessPacket(client_address, connection_id, has_version_flag, - QuicSupportedVersions().front(), data, connection_id_length, - packet_number_length, packet_number); + CurrentSupportedVersions().front(), data, + connection_id_length, packet_number_length, packet_number); } // Processes a packet. @@ -251,22 +257,55 @@ class QuicDispatcherTest : public ::testing::Test { std::unique_ptr<QuicReceivedPacket> received_packet( ConstructReceivedPacket(*packet, helper_.GetClock()->Now())); - data_ = string(packet->data(), packet->length()); - dispatcher_.ProcessPacket(server_address_, client_address, - *received_packet); + if (ChloExtractor::Extract(*packet, versions, nullptr)) { + // Add CHLO packet to the beginning to be verified first, because it is + // also processed first by new session. + data_connection_map_[connection_id].push_front( + string(packet->data(), packet->length())); + } else { + // For non-CHLO, always append to last. + data_connection_map_[connection_id].push_back( + string(packet->data(), packet->length())); + } + dispatcher_->ProcessPacket(server_address_, client_address, + *received_packet); } - void ValidatePacket(const QuicEncryptedPacket& packet) { - EXPECT_EQ(data_.length(), packet.AsStringPiece().length()); - EXPECT_EQ(data_, packet.AsStringPiece()); + void ValidatePacket(QuicConnectionId conn_id, + const QuicEncryptedPacket& packet) { + EXPECT_EQ(data_connection_map_[conn_id].front().length(), + packet.AsStringPiece().length()); + EXPECT_EQ(data_connection_map_[conn_id].front(), packet.AsStringPiece()); + data_connection_map_[conn_id].pop_front(); + } + + QuicServerSessionBase* CreateSession( + QuicDispatcher* dispatcher, + const QuicConfig& config, + QuicConnectionId connection_id, + const IPEndPoint& client_address, + MockQuicConnectionHelper* helper, + MockAlarmFactory* alarm_factory, + const QuicCryptoServerConfig* crypto_config, + QuicCompressedCertsCache* compressed_certs_cache, + TestQuicSpdyServerSession** session) { + MockServerConnection* connection = new MockServerConnection( + connection_id, helper, alarm_factory, dispatcher); + *session = new TestQuicSpdyServerSession(config, connection, crypto_config, + compressed_certs_cache); + connection->set_visitor(*session); + ON_CALL(*connection, CloseConnection(_, _, _)) + .WillByDefault(WithoutArgs(Invoke( + connection, &MockServerConnection::UnregisterOnConnectionClosed))); + return *session; } void CreateTimeWaitListManager() { - time_wait_list_manager_ = - new MockTimeWaitListManager(QuicDispatcherPeer::GetWriter(&dispatcher_), - &dispatcher_, &helper_, &alarm_factory_); + time_wait_list_manager_ = new MockTimeWaitListManager( + QuicDispatcherPeer::GetWriter(dispatcher_.get()), dispatcher_.get(), + &helper_, &alarm_factory_); // dispatcher_ takes the ownership of time_wait_list_manager_. - QuicDispatcherPeer::SetTimeWaitListManager(&dispatcher_, + QuicDispatcherPeer::SetTimeWaitListManager(dispatcher_.get(), time_wait_list_manager_); } @@ -276,46 +315,57 @@ class QuicDispatcherTest : public ::testing::Test { return client_hello.GetSerialized().AsStringPiece().as_string(); } + QuicFlagSaver flags_; // Save/restore all QUIC flag values. EpollServer eps_; QuicEpollConnectionHelper helper_; MockQuicConnectionHelper mock_helper_; QuicEpollAlarmFactory alarm_factory_; MockAlarmFactory mock_alarm_factory_; QuicConfig config_; + QuicVersionManager version_manager_; QuicCryptoServerConfig crypto_config_; IPEndPoint server_address_; - TestDispatcher dispatcher_; + std::unique_ptr<TestDispatcher> dispatcher_; MockTimeWaitListManager* time_wait_list_manager_; TestQuicSpdyServerSession* session1_; TestQuicSpdyServerSession* session2_; - string data_; + std::map<QuicConnectionId, std::list<string>> data_connection_map_; + QuicBufferedPacketStore* store_; }; TEST_F(QuicDispatcherTest, ProcessPackets) { IPEndPoint client_address(net::test::Loopback4(), 1); server_address_ = IPEndPoint(net::test::Any4(), 5); - EXPECT_CALL(dispatcher_, CreateQuicSession(1, client_address)) + EXPECT_CALL(*dispatcher_, CreateQuicSession(1, client_address)) .WillOnce(testing::Return(CreateSession( - &dispatcher_, config_, 1, client_address, &mock_helper_, + dispatcher_.get(), config_, 1, client_address, &mock_helper_, &mock_alarm_factory_, &crypto_config_, - QuicDispatcherPeer::GetCache(&dispatcher_), &session1_))); + QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_))); + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()), + ProcessUdpPacket(_, _, _)) + .WillOnce(testing::WithArgs<2>(Invoke(CreateFunctor( + &QuicDispatcherTest::ValidatePacket, base::Unretained(this), 1)))); ProcessPacket(client_address, 1, true, false, SerializeCHLO()); - EXPECT_EQ(client_address, dispatcher_.current_client_address()); - EXPECT_EQ(server_address_, dispatcher_.current_server_address()); + EXPECT_EQ(client_address, dispatcher_->current_client_address()); + EXPECT_EQ(server_address_, dispatcher_->current_server_address()); - EXPECT_CALL(dispatcher_, CreateQuicSession(2, client_address)) + EXPECT_CALL(*dispatcher_, CreateQuicSession(2, client_address)) .WillOnce(testing::Return(CreateSession( - &dispatcher_, config_, 2, client_address, &mock_helper_, + dispatcher_.get(), config_, 2, client_address, &mock_helper_, &mock_alarm_factory_, &crypto_config_, - QuicDispatcherPeer::GetCache(&dispatcher_), &session2_))); + QuicDispatcherPeer::GetCache(dispatcher_.get()), &session2_))); + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session2_->connection()), + ProcessUdpPacket(_, _, _)) + .WillOnce(testing::WithArgs<2>(Invoke(CreateFunctor( + &QuicDispatcherTest::ValidatePacket, base::Unretained(this), 2)))); ProcessPacket(client_address, 2, true, false, SerializeCHLO()); EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()), ProcessUdpPacket(_, _, _)) .Times(1) - .WillOnce(testing::WithArgs<2>( - Invoke(this, &QuicDispatcherTest::ValidatePacket))); + .WillOnce(testing::WithArgs<2>(Invoke(CreateFunctor( + &QuicDispatcherTest::ValidatePacket, base::Unretained(this), 1)))); ProcessPacket(client_address, 1, false, false, "data"); } @@ -323,7 +373,7 @@ TEST_F(QuicDispatcherTest, StatelessVersionNegotiation) { IPEndPoint client_address(net::test::Loopback4(), 1); server_address_ = IPEndPoint(net::test::Any4(), 5); - EXPECT_CALL(dispatcher_, CreateQuicSession(1, client_address)).Times(0); + EXPECT_CALL(*dispatcher_, CreateQuicSession(1, client_address)).Times(0); QuicVersion version = static_cast<QuicVersion>(QuicVersionMin() - 1); ProcessPacket(client_address, 1, true, version, SerializeCHLO(), PACKET_8BYTE_CONNECTION_ID, PACKET_6BYTE_PACKET_NUMBER, 1); @@ -332,18 +382,22 @@ TEST_F(QuicDispatcherTest, StatelessVersionNegotiation) { TEST_F(QuicDispatcherTest, Shutdown) { IPEndPoint client_address(net::test::Loopback4(), 1); - EXPECT_CALL(dispatcher_, CreateQuicSession(_, client_address)) + EXPECT_CALL(*dispatcher_, CreateQuicSession(_, client_address)) .WillOnce(testing::Return(CreateSession( - &dispatcher_, config_, 1, client_address, &mock_helper_, + dispatcher_.get(), config_, 1, client_address, &mock_helper_, &mock_alarm_factory_, &crypto_config_, - QuicDispatcherPeer::GetCache(&dispatcher_), &session1_))); + QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_))); + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()), + ProcessUdpPacket(_, _, _)) + .WillOnce(testing::WithArgs<2>(Invoke(CreateFunctor( + &QuicDispatcherTest::ValidatePacket, base::Unretained(this), 1)))); ProcessPacket(client_address, 1, true, false, SerializeCHLO()); EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()), CloseConnection(QUIC_PEER_GOING_AWAY, _, _)); - dispatcher_.Shutdown(); + dispatcher_->Shutdown(); } TEST_F(QuicDispatcherTest, TimeWaitListManager) { @@ -352,11 +406,16 @@ TEST_F(QuicDispatcherTest, TimeWaitListManager) { // Create a new session. IPEndPoint client_address(net::test::Loopback4(), 1); QuicConnectionId connection_id = 1; - EXPECT_CALL(dispatcher_, CreateQuicSession(connection_id, client_address)) + EXPECT_CALL(*dispatcher_, CreateQuicSession(connection_id, client_address)) .WillOnce(testing::Return(CreateSession( - &dispatcher_, config_, connection_id, client_address, &mock_helper_, - &mock_alarm_factory_, &crypto_config_, - QuicDispatcherPeer::GetCache(&dispatcher_), &session1_))); + dispatcher_.get(), config_, connection_id, client_address, + &mock_helper_, &mock_alarm_factory_, &crypto_config_, + QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_))); + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()), + ProcessUdpPacket(_, _, _)) + .WillOnce(testing::WithArgs<2>(Invoke(CreateFunctor( + &QuicDispatcherTest::ValidatePacket, base::Unretained(this), 1)))); + ProcessPacket(client_address, connection_id, true, false, SerializeCHLO()); // Close the connection by sending public reset packet. @@ -368,8 +427,8 @@ TEST_F(QuicDispatcherTest, TimeWaitListManager) { packet.nonce_proof = 132232; std::unique_ptr<QuicEncryptedPacket> encrypted( QuicFramer::BuildPublicResetPacket(packet)); - std::unique_ptr<QuicReceivedPacket> received( - ConstructReceivedPacket(*encrypted, helper_.GetClock()->Now())); + std::unique_ptr<QuicReceivedPacket> received(ConstructReceivedPacket( + *encrypted, session1_->connection()->clock()->Now())); EXPECT_CALL(*session1_, OnConnectionClosed(QUIC_PUBLIC_RESET, _, ConnectionCloseSource::FROM_PEER)) .Times(1) @@ -381,7 +440,7 @@ TEST_F(QuicDispatcherTest, TimeWaitListManager) { .WillOnce( Invoke(reinterpret_cast<MockQuicConnection*>(session1_->connection()), &MockQuicConnection::ReallyProcessUdpPacket)); - dispatcher_.ProcessPacket(IPEndPoint(), client_address, *received); + dispatcher_->ProcessPacket(IPEndPoint(), client_address, *received); EXPECT_TRUE(time_wait_list_manager_->IsConnectionIdInTimeWait(connection_id)); // Dispatcher forwards subsequent packets for this connection_id to the time @@ -401,7 +460,7 @@ TEST_F(QuicDispatcherTest, NoVersionPacketToTimeWaitListManager) { QuicConnectionId connection_id = 1; // Dispatcher forwards all packets for this connection_id to the time wait // list manager. - EXPECT_CALL(dispatcher_, CreateQuicSession(_, _)).Times(0); + EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _)).Times(0); EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, connection_id, _, _)) .Times(1); @@ -417,7 +476,7 @@ TEST_F(QuicDispatcherTest, ProcessPacketWithZeroPort) { server_address_ = IPEndPoint(net::test::Any4(), 5); // dispatcher_ should drop this packet. - EXPECT_CALL(dispatcher_, CreateQuicSession(1, client_address)).Times(0); + EXPECT_CALL(*dispatcher_, CreateQuicSession(1, client_address)).Times(0); EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, _, _, _)).Times(0); EXPECT_CALL(*time_wait_list_manager_, AddConnectionIdToTimeWait(_, _, _, _)) .Times(0); @@ -429,19 +488,23 @@ TEST_F(QuicDispatcherTest, OKSeqNoPacketProcessed) { QuicConnectionId connection_id = 1; server_address_ = IPEndPoint(net::test::Any4(), 5); - EXPECT_CALL(dispatcher_, CreateQuicSession(1, client_address)) + EXPECT_CALL(*dispatcher_, CreateQuicSession(1, client_address)) .WillOnce(testing::Return(CreateSession( - &dispatcher_, config_, 1, client_address, &mock_helper_, + dispatcher_.get(), config_, 1, client_address, &mock_helper_, &mock_alarm_factory_, &crypto_config_, - QuicDispatcherPeer::GetCache(&dispatcher_), &session1_))); + QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_))); + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()), + ProcessUdpPacket(_, _, _)) + .WillOnce(testing::WithArgs<2>(Invoke(CreateFunctor( + &QuicDispatcherTest::ValidatePacket, base::Unretained(this), 1)))); // A packet whose packet number is the largest that is allowed to start a // connection. ProcessPacket(client_address, connection_id, true, false, SerializeCHLO(), PACKET_8BYTE_CONNECTION_ID, PACKET_6BYTE_PACKET_NUMBER, kDefaultPathId, QuicDispatcher::kMaxReasonableInitialPacketNumber); - EXPECT_EQ(client_address, dispatcher_.current_client_address()); - EXPECT_EQ(server_address_, dispatcher_.current_server_address()); + EXPECT_EQ(client_address, dispatcher_->current_client_address()); + EXPECT_EQ(server_address_, dispatcher_->current_server_address()); } TEST_F(QuicDispatcherTest, TooBigSeqNoPacketToTimeWaitListManager) { @@ -451,7 +514,7 @@ TEST_F(QuicDispatcherTest, TooBigSeqNoPacketToTimeWaitListManager) { QuicConnectionId connection_id = 1; // Dispatcher forwards this packet for this connection_id to the time wait // list manager. - EXPECT_CALL(dispatcher_, CreateQuicSession(_, _)).Times(0); + EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _)).Times(0); EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, connection_id, _, _)) .Times(1); @@ -470,11 +533,13 @@ class MockQuicCryptoServerStream : public QuicCryptoServerStream { public: MockQuicCryptoServerStream(const QuicCryptoServerConfig& crypto_config, QuicCompressedCertsCache* compressed_certs_cache, - QuicServerSessionBase* session) + QuicServerSessionBase* session, + QuicCryptoServerStream::Helper* helper) : QuicCryptoServerStream(&crypto_config, compressed_certs_cache, FLAGS_enable_quic_stateless_reject_support, - session) {} + session, + helper) {} void set_handshake_confirmed_for_testing(bool handshake_confirmed) { handshake_confirmed_ = handshake_confirmed; } @@ -530,7 +595,8 @@ class QuicDispatcherStatelessRejectTest : public QuicDispatcherTest, public ::testing::WithParamInterface<StatelessRejectTestParams> { public: - QuicDispatcherStatelessRejectTest() : crypto_stream1_(nullptr) {} + QuicDispatcherStatelessRejectTest() + : QuicDispatcherTest(), crypto_stream1_(nullptr) {} ~QuicDispatcherStatelessRejectTest() override { if (crypto_stream1_) { @@ -541,6 +607,7 @@ class QuicDispatcherStatelessRejectTest // This test setup assumes that all testing will be done using // crypto_stream1_. void SetUp() override { + QuicDispatcherTest::SetUp(); FLAGS_enable_quic_stateless_reject_support = GetParam().enable_stateless_rejects_via_flag; } @@ -553,17 +620,18 @@ class QuicDispatcherStatelessRejectTest GetParam().client_supports_statelesss_rejects; } - // Sets up dispatcher_, sesession1_, and crypto_stream1_ based on + // Sets up dispatcher_, session1_, and crypto_stream1_ based on // the test parameters. QuicServerSessionBase* CreateSessionBasedOnTestParams( QuicConnectionId connection_id, const IPEndPoint& client_address) { - CreateSession(&dispatcher_, config_, connection_id, client_address, + CreateSession(dispatcher_.get(), config_, connection_id, client_address, &mock_helper_, &mock_alarm_factory_, &crypto_config_, - QuicDispatcherPeer::GetCache(&dispatcher_), &session1_); + QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_); crypto_stream1_ = new MockQuicCryptoServerStream( - crypto_config_, QuicDispatcherPeer::GetCache(&dispatcher_), session1_); + crypto_config_, QuicDispatcherPeer::GetCache(dispatcher_.get()), + session1_, session1_->stream_helper()); session1_->SetCryptoStream(crypto_stream1_); crypto_stream1_->set_handshake_confirmed_for_testing( GetParam().crypto_handshake_successful); @@ -587,10 +655,14 @@ TEST_P(QuicDispatcherStatelessRejectTest, ParameterizedBasicTest) { IPEndPoint client_address(net::test::Loopback4(), 1); QuicConnectionId connection_id = 1; - EXPECT_CALL(dispatcher_, CreateQuicSession(connection_id, client_address)) + EXPECT_CALL(*dispatcher_, CreateQuicSession(connection_id, client_address)) .WillOnce(testing::Return( CreateSessionBasedOnTestParams(connection_id, client_address))); - + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()), + ProcessUdpPacket(_, _, _)) + .WillOnce(testing::WithArgs<2>( + Invoke(CreateFunctor(&QuicDispatcherTest::ValidatePacket, + base::Unretained(this), connection_id)))); // Process the first packet for the connection. ProcessPacket(client_address, connection_id, true, false, SerializeCHLO()); if (ExpectStatelessReject()) { @@ -616,24 +688,30 @@ TEST_P(QuicDispatcherStatelessRejectTest, ParameterizedBasicTest) { ProcessUdpPacket(_, _, _)) .Times(1) .WillOnce(testing::WithArgs<2>( - Invoke(this, &QuicDispatcherTest::ValidatePacket))); + Invoke(CreateFunctor(&QuicDispatcherTest::ValidatePacket, + base::Unretained(this), connection_id)))); } ProcessPacket(client_address, connection_id, true, false, "data"); } TEST_P(QuicDispatcherStatelessRejectTest, CheapRejects) { FLAGS_quic_use_cheap_stateless_rejects = true; + FLAGS_quic_buffer_packet_till_chlo = true; CreateTimeWaitListManager(); IPEndPoint client_address(net::test::Loopback4(), 1); QuicConnectionId connection_id = 1; if (GetParam().enable_stateless_rejects_via_flag) { - EXPECT_CALL(dispatcher_, CreateQuicSession(connection_id, client_address)) + EXPECT_CALL(*dispatcher_, CreateQuicSession(connection_id, client_address)) .Times(0); } else { - EXPECT_CALL(dispatcher_, CreateQuicSession(connection_id, client_address)) + EXPECT_CALL(*dispatcher_, CreateQuicSession(connection_id, client_address)) .WillOnce(testing::Return( CreateSessionBasedOnTestParams(connection_id, client_address))); + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()), + ProcessUdpPacket(_, _, _)) + .WillOnce(testing::WithArgs<2>(Invoke(CreateFunctor( + &QuicDispatcherTest::ValidatePacket, base::Unretained(this), 1)))); } VLOG(1) << "ExpectStatelessReject: " << ExpectStatelessReject(); @@ -660,6 +738,88 @@ TEST_P(QuicDispatcherStatelessRejectTest, CheapRejects) { } } +TEST_P(QuicDispatcherStatelessRejectTest, BufferNonChlo) { + FLAGS_quic_use_cheap_stateless_rejects = true; + CreateTimeWaitListManager(); + + const IPEndPoint client_address(net::test::Loopback4(), 1); + const QuicConnectionId connection_id = 1; + + if (!GetParam().enable_stateless_rejects_via_flag && + !FLAGS_quic_buffer_packet_till_chlo) { + // If stateless rejects are not being used and early arrived packets are not + // buffered, then a connection will be created immediately. + EXPECT_CALL(*dispatcher_, CreateQuicSession(connection_id, client_address)) + .WillOnce(testing::Return( + CreateSessionBasedOnTestParams(connection_id, client_address))); + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()), + ProcessUdpPacket(_, client_address, _)) + .WillOnce(testing::WithArg<2>( + Invoke(CreateFunctor(&QuicDispatcherTest::ValidatePacket, + base::Unretained(this), connection_id)))); + } + bool first_packet_dropped = GetParam().enable_stateless_rejects_via_flag && + !FLAGS_quic_buffer_packet_till_chlo; + if (first_packet_dropped) { + // Never do stateless reject while + // FLAGS_quic_buffer_packet_till_chlo is off. + EXPECT_QUIC_BUG( + ProcessPacket(client_address, connection_id, true, false, + "NOT DATA FOR A CHLO"), + "Have to drop packet because buffering non-chlo packet is " + "not supported while trying to do stateless reject. " + "--gfe2_reloadable_flag_quic_buffer_packet_till_chlo false " + "--gfe2_reloadable_flag_quic_use_cheap_stateless_rejects true"); + } else { + ProcessPacket(client_address, connection_id, true, false, + "NOT DATA FOR A CHLO"); + } + + // Process the first packet for the connection. + // clang-format off + CryptoHandshakeMessage client_hello = CryptoTestUtils::Message( + "CHLO", + "AEAD", "AESG", + "KEXS", "C255", + "NONC", "1234567890123456789012", + "VER\0", "Q025", + "$padding", static_cast<int>(kClientHelloMinimumSize), + nullptr); + // clang-format on + + if (GetParam().enable_stateless_rejects_via_flag || + FLAGS_quic_buffer_packet_till_chlo) { + // If stateless rejects are enabled then a connection will be created now + // and the buffered packet will be processed + EXPECT_CALL(*dispatcher_, CreateQuicSession(connection_id, client_address)) + .WillOnce(testing::Return( + CreateSessionBasedOnTestParams(connection_id, client_address))); + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()), + ProcessUdpPacket(_, client_address, _)) + .WillOnce(testing::WithArg<2>( + Invoke(CreateFunctor(&QuicDispatcherTest::ValidatePacket, + base::Unretained(this), connection_id)))); + } + if (!first_packet_dropped) { + // Expect both packets to be passed to ProcessUdpPacket(). And one of them + // is already expected in CreateSessionBasedOnTestParams(). + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()), + ProcessUdpPacket(_, client_address, _)) + .WillOnce(testing::WithArg<2>( + Invoke(CreateFunctor(&QuicDispatcherTest::ValidatePacket, + base::Unretained(this), connection_id)))) + .RetiresOnSaturation(); + } else { + // Since first packet is dropped, remove it from map to skip + // ValidatePacket() on it. + data_connection_map_[connection_id].pop_front(); + } + ProcessPacket(client_address, connection_id, true, false, + client_hello.GetSerialized().AsStringPiece().as_string()); + EXPECT_FALSE( + time_wait_list_manager_->IsConnectionIdInTimeWait(connection_id)); +} + // Verify the stopgap test: Packets with truncated connection IDs should be // dropped. class QuicDispatcherTestStrayPacketConnectionId : public QuicDispatcherTest {}; @@ -672,7 +832,7 @@ TEST_F(QuicDispatcherTestStrayPacketConnectionId, IPEndPoint client_address(net::test::Loopback4(), 1); QuicConnectionId connection_id = 1; // Dispatcher drops this packet. - EXPECT_CALL(dispatcher_, CreateQuicSession(_, _)).Times(0); + EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _)).Times(0); EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, connection_id, _, _)) .Times(0); @@ -707,39 +867,48 @@ class BlockingWriter : public QuicPacketWriterWrapper { class QuicDispatcherWriteBlockedListTest : public QuicDispatcherTest { public: void SetUp() override { + QuicDispatcherTest::SetUp(); writer_ = new BlockingWriter; - QuicDispatcherPeer::UseWriter(&dispatcher_, writer_); + QuicDispatcherPeer::UseWriter(dispatcher_.get(), writer_); IPEndPoint client_address(net::test::Loopback4(), 1); - EXPECT_CALL(dispatcher_, CreateQuicSession(_, client_address)) + EXPECT_CALL(*dispatcher_, CreateQuicSession(_, client_address)) .WillOnce(testing::Return(CreateSession( - &dispatcher_, config_, 1, client_address, &helper_, &alarm_factory_, - &crypto_config_, QuicDispatcherPeer::GetCache(&dispatcher_), - &session1_))); + dispatcher_.get(), config_, 1, client_address, &helper_, + &alarm_factory_, &crypto_config_, + QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_))); + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()), + ProcessUdpPacket(_, _, _)) + .WillOnce(testing::WithArgs<2>(Invoke(CreateFunctor( + &QuicDispatcherTest::ValidatePacket, base::Unretained(this), 1)))); ProcessPacket(client_address, 1, true, false, SerializeCHLO()); - EXPECT_CALL(dispatcher_, CreateQuicSession(_, client_address)) + EXPECT_CALL(*dispatcher_, CreateQuicSession(_, client_address)) .WillOnce(testing::Return(CreateSession( - &dispatcher_, config_, 2, client_address, &helper_, &alarm_factory_, - &crypto_config_, QuicDispatcherPeer::GetCache(&dispatcher_), - &session2_))); + dispatcher_.get(), config_, 2, client_address, &helper_, + &alarm_factory_, &crypto_config_, + QuicDispatcherPeer::GetCache(dispatcher_.get()), &session2_))); + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session2_->connection()), + ProcessUdpPacket(_, _, _)) + .WillOnce(testing::WithArgs<2>(Invoke(CreateFunctor( + &QuicDispatcherTest::ValidatePacket, base::Unretained(this), 2)))); ProcessPacket(client_address, 2, true, false, SerializeCHLO()); - blocked_list_ = QuicDispatcherPeer::GetWriteBlockedList(&dispatcher_); + blocked_list_ = QuicDispatcherPeer::GetWriteBlockedList(dispatcher_.get()); } void TearDown() override { EXPECT_CALL(*connection1(), CloseConnection(QUIC_PEER_GOING_AWAY, _, _)); EXPECT_CALL(*connection2(), CloseConnection(QUIC_PEER_GOING_AWAY, _, _)); - dispatcher_.Shutdown(); + dispatcher_->Shutdown(); } void SetBlocked() { writer_->write_blocked_ = true; } void BlockConnection2() { writer_->write_blocked_ = true; - dispatcher_.OnWriteBlocked(connection2()); + dispatcher_->OnWriteBlocked(connection2()); } protected: @@ -751,95 +920,95 @@ class QuicDispatcherWriteBlockedListTest : public QuicDispatcherTest { TEST_F(QuicDispatcherWriteBlockedListTest, BasicOnCanWrite) { // No OnCanWrite calls because no connections are blocked. - dispatcher_.OnCanWrite(); + dispatcher_->OnCanWrite(); // Register connection 1 for events, and make sure it's notified. SetBlocked(); - dispatcher_.OnWriteBlocked(connection1()); + dispatcher_->OnWriteBlocked(connection1()); EXPECT_CALL(*connection1(), OnCanWrite()); - dispatcher_.OnCanWrite(); + dispatcher_->OnCanWrite(); // It should get only one notification. EXPECT_CALL(*connection1(), OnCanWrite()).Times(0); - dispatcher_.OnCanWrite(); - EXPECT_FALSE(dispatcher_.HasPendingWrites()); + dispatcher_->OnCanWrite(); + EXPECT_FALSE(dispatcher_->HasPendingWrites()); } TEST_F(QuicDispatcherWriteBlockedListTest, OnCanWriteOrder) { // Make sure we handle events in order. InSequence s; SetBlocked(); - dispatcher_.OnWriteBlocked(connection1()); - dispatcher_.OnWriteBlocked(connection2()); + dispatcher_->OnWriteBlocked(connection1()); + dispatcher_->OnWriteBlocked(connection2()); EXPECT_CALL(*connection1(), OnCanWrite()); EXPECT_CALL(*connection2(), OnCanWrite()); - dispatcher_.OnCanWrite(); + dispatcher_->OnCanWrite(); // Check the other ordering. SetBlocked(); - dispatcher_.OnWriteBlocked(connection2()); - dispatcher_.OnWriteBlocked(connection1()); + dispatcher_->OnWriteBlocked(connection2()); + dispatcher_->OnWriteBlocked(connection1()); EXPECT_CALL(*connection2(), OnCanWrite()); EXPECT_CALL(*connection1(), OnCanWrite()); - dispatcher_.OnCanWrite(); + dispatcher_->OnCanWrite(); } TEST_F(QuicDispatcherWriteBlockedListTest, OnCanWriteRemove) { // Add and remove one connction. SetBlocked(); - dispatcher_.OnWriteBlocked(connection1()); + dispatcher_->OnWriteBlocked(connection1()); blocked_list_->erase(connection1()); EXPECT_CALL(*connection1(), OnCanWrite()).Times(0); - dispatcher_.OnCanWrite(); + dispatcher_->OnCanWrite(); // Add and remove one connction and make sure it doesn't affect others. SetBlocked(); - dispatcher_.OnWriteBlocked(connection1()); - dispatcher_.OnWriteBlocked(connection2()); + dispatcher_->OnWriteBlocked(connection1()); + dispatcher_->OnWriteBlocked(connection2()); blocked_list_->erase(connection1()); EXPECT_CALL(*connection2(), OnCanWrite()); - dispatcher_.OnCanWrite(); + dispatcher_->OnCanWrite(); // Add it, remove it, and add it back and make sure things are OK. SetBlocked(); - dispatcher_.OnWriteBlocked(connection1()); + dispatcher_->OnWriteBlocked(connection1()); blocked_list_->erase(connection1()); - dispatcher_.OnWriteBlocked(connection1()); + dispatcher_->OnWriteBlocked(connection1()); EXPECT_CALL(*connection1(), OnCanWrite()).Times(1); - dispatcher_.OnCanWrite(); + dispatcher_->OnCanWrite(); } TEST_F(QuicDispatcherWriteBlockedListTest, DoubleAdd) { // Make sure a double add does not necessitate a double remove. SetBlocked(); - dispatcher_.OnWriteBlocked(connection1()); - dispatcher_.OnWriteBlocked(connection1()); + dispatcher_->OnWriteBlocked(connection1()); + dispatcher_->OnWriteBlocked(connection1()); blocked_list_->erase(connection1()); EXPECT_CALL(*connection1(), OnCanWrite()).Times(0); - dispatcher_.OnCanWrite(); + dispatcher_->OnCanWrite(); // Make sure a double add does not result in two OnCanWrite calls. SetBlocked(); - dispatcher_.OnWriteBlocked(connection1()); - dispatcher_.OnWriteBlocked(connection1()); + dispatcher_->OnWriteBlocked(connection1()); + dispatcher_->OnWriteBlocked(connection1()); EXPECT_CALL(*connection1(), OnCanWrite()).Times(1); - dispatcher_.OnCanWrite(); + dispatcher_->OnCanWrite(); } TEST_F(QuicDispatcherWriteBlockedListTest, OnCanWriteHandleBlock) { // Finally make sure if we write block on a write call, we stop calling. InSequence s; SetBlocked(); - dispatcher_.OnWriteBlocked(connection1()); - dispatcher_.OnWriteBlocked(connection2()); + dispatcher_->OnWriteBlocked(connection1()); + dispatcher_->OnWriteBlocked(connection2()); EXPECT_CALL(*connection1(), OnCanWrite()) .WillOnce(Invoke(this, &QuicDispatcherWriteBlockedListTest::SetBlocked)); EXPECT_CALL(*connection2(), OnCanWrite()).Times(0); - dispatcher_.OnCanWrite(); + dispatcher_->OnCanWrite(); // And we'll resume where we left off when we get another call. EXPECT_CALL(*connection2(), OnCanWrite()); - dispatcher_.OnCanWrite(); + dispatcher_->OnCanWrite(); } TEST_F(QuicDispatcherWriteBlockedListTest, LimitedWrites) { @@ -847,37 +1016,495 @@ TEST_F(QuicDispatcherWriteBlockedListTest, LimitedWrites) { // but should not be immediately called due to limits. InSequence s; SetBlocked(); - dispatcher_.OnWriteBlocked(connection1()); - dispatcher_.OnWriteBlocked(connection2()); + dispatcher_->OnWriteBlocked(connection1()); + dispatcher_->OnWriteBlocked(connection2()); EXPECT_CALL(*connection1(), OnCanWrite()); EXPECT_CALL(*connection2(), OnCanWrite()) .WillOnce( Invoke(this, &QuicDispatcherWriteBlockedListTest::BlockConnection2)); - dispatcher_.OnCanWrite(); - EXPECT_TRUE(dispatcher_.HasPendingWrites()); + dispatcher_->OnCanWrite(); + EXPECT_TRUE(dispatcher_->HasPendingWrites()); // Now call OnCanWrite again, and connection1 should get its second chance EXPECT_CALL(*connection2(), OnCanWrite()); - dispatcher_.OnCanWrite(); - EXPECT_FALSE(dispatcher_.HasPendingWrites()); + dispatcher_->OnCanWrite(); + EXPECT_FALSE(dispatcher_->HasPendingWrites()); } TEST_F(QuicDispatcherWriteBlockedListTest, TestWriteLimits) { // Finally make sure if we write block on a write call, we stop calling. InSequence s; SetBlocked(); - dispatcher_.OnWriteBlocked(connection1()); - dispatcher_.OnWriteBlocked(connection2()); + dispatcher_->OnWriteBlocked(connection1()); + dispatcher_->OnWriteBlocked(connection2()); EXPECT_CALL(*connection1(), OnCanWrite()) .WillOnce(Invoke(this, &QuicDispatcherWriteBlockedListTest::SetBlocked)); EXPECT_CALL(*connection2(), OnCanWrite()).Times(0); - dispatcher_.OnCanWrite(); - EXPECT_TRUE(dispatcher_.HasPendingWrites()); + dispatcher_->OnCanWrite(); + EXPECT_TRUE(dispatcher_->HasPendingWrites()); // And we'll resume where we left off when we get another call. EXPECT_CALL(*connection2(), OnCanWrite()); - dispatcher_.OnCanWrite(); - EXPECT_FALSE(dispatcher_.HasPendingWrites()); + dispatcher_->OnCanWrite(); + EXPECT_FALSE(dispatcher_->HasPendingWrites()); +} + +// Tests that bufferring packets works in stateful reject, expensive stateless +// reject and cheap stateless reject. +struct BufferedPacketStoreTestParams { + BufferedPacketStoreTestParams(bool enable_stateless_rejects_via_flag, + bool support_cheap_stateless_reject) + : enable_stateless_rejects_via_flag(enable_stateless_rejects_via_flag), + support_cheap_stateless_reject(support_cheap_stateless_reject) {} + + friend ostream& operator<<(ostream& os, + const BufferedPacketStoreTestParams& p) { + os << "{ enable_stateless_rejects_via_flag: " + << p.enable_stateless_rejects_via_flag << std::endl; + os << " support_cheap_stateless_reject: " + << p.support_cheap_stateless_reject << " }"; + return os; + } + + // This only enables the stateless reject feature via the feature-flag. + // This should be a no-op if the peer does not support them. + bool enable_stateless_rejects_via_flag; + // Whether to do cheap stateless or not. + bool support_cheap_stateless_reject; +}; + +vector<BufferedPacketStoreTestParams> GetBufferedPacketStoreTestParams() { + vector<BufferedPacketStoreTestParams> params; + for (bool enable_stateless_rejects_via_flag : {true, false}) { + for (bool support_cheap_stateless_reject : {true, false}) { + params.push_back(BufferedPacketStoreTestParams( + enable_stateless_rejects_via_flag, support_cheap_stateless_reject)); + } + } + return params; +} + +// A dispatcher whose stateless rejector will always ACCEPTs CHLO. +class BufferedPacketStoreTest + : public QuicDispatcherTest, + public ::testing::WithParamInterface<BufferedPacketStoreTestParams> { + public: + BufferedPacketStoreTest() + : QuicDispatcherTest(), client_addr_(Loopback4(), 1234) { + FLAGS_quic_buffer_packet_till_chlo = true; + FLAGS_quic_use_cheap_stateless_rejects = + GetParam().support_cheap_stateless_reject; + FLAGS_enable_quic_stateless_reject_support = + GetParam().enable_stateless_rejects_via_flag; + } + + void SetUp() override { + QuicDispatcherTest::SetUp(); + clock_ = QuicDispatcherPeer::GetHelper(dispatcher_.get())->GetClock(); + + QuicVersion version = AllSupportedVersions().front(); + CryptoHandshakeMessage chlo = CryptoTestUtils::GenerateDefaultInchoateCHLO( + clock_, version, &crypto_config_); + chlo.SetVector(net::kCOPT, net::QuicTagVector{net::kSREJ}); + // Pass an inchoate CHLO. + CryptoTestUtils::GenerateFullCHLO( + chlo, &crypto_config_, server_ip_, client_addr_, version, clock_, + &proof_, QuicDispatcherPeer::GetCache(dispatcher_.get()), &full_chlo_); + } + + string SerializeFullCHLO() { + return full_chlo_.GetSerialized().AsStringPiece().as_string(); + } + + protected: + IPAddress server_ip_; + IPEndPoint client_addr_; + QuicCryptoProof proof_; + const QuicClock* clock_; + CryptoHandshakeMessage full_chlo_; +}; + +INSTANTIATE_TEST_CASE_P( + BufferedPacketStoreTests, + BufferedPacketStoreTest, + ::testing::ValuesIn(GetBufferedPacketStoreTestParams())); + +TEST_P(BufferedPacketStoreTest, ProcessNonChloPacketsUptoLimitAndProcessChlo) { + InSequence s; + IPEndPoint client_address(Loopback4(), 1); + server_address_ = IPEndPoint(Any4(), 5); + QuicConnectionId conn_id = 1; + // A bunch of non-CHLO should be buffered upon arrival, and the first one + // should trigger OnNewConnectionAdded(). + EXPECT_CALL(*dispatcher_, OnNewConnectionAdded(conn_id)).Times(1); + for (size_t i = 1; i <= kDefaultMaxUndecryptablePackets + 1; ++i) { + ProcessPacket(client_address, conn_id, true, false, + "data packet " + IntToString(i + 1), + PACKET_8BYTE_CONNECTION_ID, PACKET_6BYTE_PACKET_NUMBER, + kDefaultPathId, + /*packet_number=*/i + 1); + } + EXPECT_EQ(0u, dispatcher_->session_map().size()) + << "No session should be created before CHLO arrives."; + + // Pop out the last packet as it is also be dropped by the store. + data_connection_map_[conn_id].pop_back(); + // When CHLO arrives, a new session should be created, and all packets + // buffered should be delivered to the session. + EXPECT_CALL(*dispatcher_, CreateQuicSession(conn_id, client_address)) + .WillOnce(testing::Return(CreateSession( + dispatcher_.get(), config_, conn_id, client_address, &mock_helper_, + &mock_alarm_factory_, &crypto_config_, + QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_))); + + // Only |kDefaultMaxUndecryptablePackets| packets were buffered, and they + // should be delivered in arrival order. + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()), + ProcessUdpPacket(_, _, _)) + .Times(kDefaultMaxUndecryptablePackets + 1) // + 1 for CHLO. + .WillRepeatedly(testing::WithArg<2>( + Invoke(CreateFunctor(&QuicDispatcherTest::ValidatePacket, + base::Unretained(this), conn_id)))); + ProcessPacket(client_address, conn_id, true, false, SerializeFullCHLO()); +} + +TEST_P(BufferedPacketStoreTest, + ProcessNonChloPacketsForDifferentConnectionsUptoLimit) { + InSequence s; + server_address_ = IPEndPoint(Any4(), 5); + // A bunch of non-CHLO should be buffered upon arrival. + size_t kNumConnections = (FLAGS_quic_limit_num_new_sessions_per_epoll_loop + ? kMaxConnectionsWithoutCHLO + : kDefaultMaxConnectionsInStore) + + 1; + for (size_t i = 1; i <= kNumConnections; ++i) { + IPEndPoint client_address(Loopback4(), i); + QuicConnectionId conn_id = i; + if (i <= kNumConnections - 1) { + // As they are on different connection, they should trigger + // OnNewConnectionAdded(). The last packet should be dropped. + EXPECT_CALL(*dispatcher_, OnNewConnectionAdded(conn_id)); + } + ProcessPacket(client_address, conn_id, true, false, + "data packet on connection " + IntToString(i), + PACKET_8BYTE_CONNECTION_ID, PACKET_6BYTE_PACKET_NUMBER, + kDefaultPathId, + /*packet_number=*/2); + } + + // Pop out the packet on last connection as it shouldn't be enqueued in store + // as well. + data_connection_map_[kNumConnections].pop_front(); + + // Reset session creation counter to ensure processing CHLO can always + // create session. + QuicDispatcherPeer::set_new_sessions_allowed_per_event_loop(dispatcher_.get(), + kNumConnections); + // Process CHLOs to create session for these connections. + for (size_t i = 1; i <= kNumConnections; ++i) { + IPEndPoint client_address(Loopback4(), i); + QuicConnectionId conn_id = i; + EXPECT_CALL(*dispatcher_, CreateQuicSession(conn_id, client_address)) + .WillOnce(testing::Return(CreateSession( + dispatcher_.get(), config_, conn_id, client_address, &mock_helper_, + &mock_alarm_factory_, &crypto_config_, + QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_))); + if (conn_id == kNumConnections) { + // The last CHLO should trigger OnNewConnectionAdded() since it's the + // first packet arrives on that connection. + EXPECT_CALL(*dispatcher_, OnNewConnectionAdded(conn_id)); + } + // First |kNumConnections| - 1 connections should have buffered + // a packet in store. The rest should have been dropped. + size_t upper_limit = FLAGS_quic_limit_num_new_sessions_per_epoll_loop + ? kMaxConnectionsWithoutCHLO + : kDefaultMaxConnectionsInStore; + size_t num_packet_to_process = i <= upper_limit ? 2u : 1u; + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()), + ProcessUdpPacket(_, client_address, _)) + .Times(num_packet_to_process) + .WillRepeatedly(testing::WithArg<2>( + Invoke(CreateFunctor(&QuicDispatcherTest::ValidatePacket, + base::Unretained(this), conn_id)))); + ProcessPacket(client_address, conn_id, true, false, SerializeFullCHLO()); + } +} + +// Tests that store delivers empty packet list if CHLO arrives firstly. +TEST_P(BufferedPacketStoreTest, DeliverEmptyPackets) { + QuicConnectionId conn_id = 1; + IPEndPoint client_address(Loopback4(), 1); + EXPECT_CALL(*dispatcher_, OnNewConnectionAdded(conn_id)); + EXPECT_CALL(*dispatcher_, CreateQuicSession(conn_id, client_address)) + .WillOnce(testing::Return(CreateSession( + dispatcher_.get(), config_, conn_id, client_address, &mock_helper_, + &mock_alarm_factory_, &crypto_config_, + QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_))); + ProcessPacket(client_address, conn_id, true, false, SerializeFullCHLO()); +} + +// Tests that a retransmitted CHLO arrives after a connection for the +// CHLO has been created. +TEST_P(BufferedPacketStoreTest, ReceiveRetransmittedCHLO) { + InSequence s; + IPEndPoint client_address(Loopback4(), 1); + server_address_ = IPEndPoint(Any4(), 5); + QuicConnectionId conn_id = 1; + ProcessPacket(client_address, conn_id, true, false, + "data packet " + IntToString(2), PACKET_8BYTE_CONNECTION_ID, + PACKET_6BYTE_PACKET_NUMBER, kDefaultPathId, + /*packet_number=*/2); + + // When CHLO arrives, a new session should be created, and all packets + // buffered should be delivered to the session. + EXPECT_CALL(*dispatcher_, CreateQuicSession(conn_id, client_address)) + .Times(1) // Only triggered by 1st CHLO. + .WillOnce(testing::Return(CreateSession( + dispatcher_.get(), config_, conn_id, client_address, &mock_helper_, + &mock_alarm_factory_, &crypto_config_, + QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_))); + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()), + ProcessUdpPacket(_, _, _)) + .Times(3) // Triggered by 1 data packet and 2 CHLOs. + .WillRepeatedly(testing::WithArg<2>( + Invoke(CreateFunctor(&QuicDispatcherTest::ValidatePacket, + base::Unretained(this), conn_id)))); + ProcessPacket(client_address, conn_id, true, false, SerializeFullCHLO()); + + ProcessPacket(client_address, conn_id, true, false, SerializeFullCHLO()); +} + +// Tests that expiration of a connection add connection id to time wait list. +TEST_P(BufferedPacketStoreTest, ReceiveCHLOAfterExpiration) { + InSequence s; + CreateTimeWaitListManager(); + QuicBufferedPacketStore* store = + QuicDispatcherPeer::GetBufferedPackets(dispatcher_.get()); + QuicBufferedPacketStorePeer::set_clock(store, mock_helper_.GetClock()); + + IPEndPoint client_address(Loopback4(), 1); + server_address_ = IPEndPoint(Any4(), 5); + QuicConnectionId conn_id = 1; + ProcessPacket(client_address, conn_id, true, false, + "data packet " + IntToString(2), PACKET_8BYTE_CONNECTION_ID, + PACKET_6BYTE_PACKET_NUMBER, kDefaultPathId, + /*packet_number=*/2); + + mock_helper_.AdvanceTime( + QuicTime::Delta::FromSeconds(kInitialIdleTimeoutSecs)); + QuicAlarm* alarm = QuicBufferedPacketStorePeer::expiration_alarm(store); + // Cancel alarm as if it had been fired. + alarm->Cancel(); + store->OnExpirationTimeout(); + // New arrived CHLO will be dropped because this connection is in time wait + // list. + ASSERT_TRUE(time_wait_list_manager_->IsConnectionIdInTimeWait(conn_id)); + EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, conn_id, _, _)); + ProcessPacket(client_address, conn_id, true, false, SerializeFullCHLO()); +} + +TEST_P(BufferedPacketStoreTest, ProcessCHLOsUptoLimitAndBufferTheRest) { + FLAGS_quic_limit_num_new_sessions_per_epoll_loop = true; + // Process more than (|kMaxNumSessionsToCreate| + + // |kDefaultMaxConnectionsInStore|) CHLOs, + // the first |kMaxNumSessionsToCreate| should create connections immediately, + // the next |kDefaultMaxConnectionsInStore| should be buffered, + // the rest should be dropped. + QuicBufferedPacketStore* store = + QuicDispatcherPeer::GetBufferedPackets(dispatcher_.get()); + const size_t kNumCHLOs = + kMaxNumSessionsToCreate + kDefaultMaxConnectionsInStore + 1; + for (size_t conn_id = 1; conn_id <= kNumCHLOs; ++conn_id) { + if (conn_id < kNumCHLOs) { + // Except the last connection, all connections for previous CHLOs should + // be regarded as newly added. + EXPECT_CALL(*dispatcher_, OnNewConnectionAdded(conn_id)); + } + if (conn_id <= kMaxNumSessionsToCreate) { + EXPECT_CALL(*dispatcher_, CreateQuicSession(conn_id, client_addr_)) + .WillOnce(testing::Return(CreateSession( + dispatcher_.get(), config_, conn_id, client_addr_, &mock_helper_, + &mock_alarm_factory_, &crypto_config_, + QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_))); + EXPECT_CALL( + *reinterpret_cast<MockQuicConnection*>(session1_->connection()), + ProcessUdpPacket(_, _, _)) + .WillOnce(testing::WithArg<2>( + Invoke(CreateFunctor(&QuicDispatcherTest::ValidatePacket, + base::Unretained(this), conn_id)))); + } + ProcessPacket(client_addr_, conn_id, true, false, SerializeFullCHLO()); + if (conn_id <= kMaxNumSessionsToCreate + kDefaultMaxConnectionsInStore && + conn_id > kMaxNumSessionsToCreate) { + EXPECT_TRUE(store->HasChloForConnection(conn_id)); + } else { + // First |kMaxNumSessionsToCreate| CHLOs should be passed to new + // connections immediately, and the last CHLO should be dropped as the + // store is full. + EXPECT_FALSE(store->HasChloForConnection(conn_id)); + } + } + + // Graduately consume buffered CHLOs. The buffered connections should be + // created but the dropped one shouldn't. + for (size_t conn_id = kMaxNumSessionsToCreate + 1; + conn_id <= kMaxNumSessionsToCreate + kDefaultMaxConnectionsInStore; + ++conn_id) { + EXPECT_CALL(*dispatcher_, CreateQuicSession(conn_id, client_addr_)) + .WillOnce(testing::Return(CreateSession( + dispatcher_.get(), config_, conn_id, client_addr_, &mock_helper_, + &mock_alarm_factory_, &crypto_config_, + QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_))); + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()), + ProcessUdpPacket(_, _, _)) + .WillOnce(testing::WithArg<2>( + Invoke(CreateFunctor(&QuicDispatcherTest::ValidatePacket, + base::Unretained(this), conn_id)))); + } + EXPECT_CALL(*dispatcher_, CreateQuicSession(kNumCHLOs, client_addr_)) + .Times(0); + + while (store->HasChlosBuffered()) { + dispatcher_->ProcessBufferedChlos(kMaxNumSessionsToCreate); + } + + EXPECT_EQ(static_cast<size_t>(kMaxNumSessionsToCreate) + + kDefaultMaxConnectionsInStore, + session1_->connection_id()); +} + +// Duplicated CHLO shouldn't be buffered. +TEST_P(BufferedPacketStoreTest, BufferDuplicatedCHLO) { + FLAGS_quic_limit_num_new_sessions_per_epoll_loop = true; + for (QuicConnectionId conn_id = 1; conn_id <= kMaxNumSessionsToCreate + 1; + ++conn_id) { + // Last CHLO will be buffered. Others will create connection right away. + if (conn_id <= kMaxNumSessionsToCreate) { + EXPECT_CALL(*dispatcher_, CreateQuicSession(conn_id, client_addr_)) + .WillOnce(testing::Return(CreateSession( + dispatcher_.get(), config_, conn_id, client_addr_, &mock_helper_, + &mock_alarm_factory_, &crypto_config_, + QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_))); + EXPECT_CALL( + *reinterpret_cast<MockQuicConnection*>(session1_->connection()), + ProcessUdpPacket(_, _, _)) + .WillOnce(testing::WithArg<2>( + Invoke(CreateFunctor(&QuicDispatcherTest::ValidatePacket, + base::Unretained(this), conn_id)))); + } + ProcessPacket(client_addr_, conn_id, true, false, SerializeFullCHLO()); + } + // Retransmit CHLO on last connection should be dropped. + QuicConnectionId last_connection = kMaxNumSessionsToCreate + 1; + ProcessPacket(client_addr_, last_connection, true, false, + SerializeFullCHLO()); + + size_t packets_buffered = 2; + if (!FLAGS_quic_buffer_packets_after_chlo) { + // The packet sent above is dropped when flag is off. + packets_buffered = 1; + } + + // Reset counter and process buffered CHLO. + EXPECT_CALL(*dispatcher_, CreateQuicSession(last_connection, client_addr_)) + .WillOnce(testing::Return(CreateSession( + dispatcher_.get(), config_, last_connection, client_addr_, + &mock_helper_, &mock_alarm_factory_, &crypto_config_, + QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_))); + // Only one packet(CHLO) should be process. + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()), + ProcessUdpPacket(_, _, _)) + .Times(packets_buffered) + .WillRepeatedly(testing::WithArg<2>( + Invoke(CreateFunctor(&QuicDispatcherTest::ValidatePacket, + base::Unretained(this), last_connection)))); + dispatcher_->ProcessBufferedChlos(kMaxNumSessionsToCreate); +} + +TEST_P(BufferedPacketStoreTest, BufferNonChloPacketsUptoLimitWithChloBuffered) { + FLAGS_quic_limit_num_new_sessions_per_epoll_loop = true; + QuicConnectionId last_connection_id = kMaxNumSessionsToCreate + 1; + for (QuicConnectionId conn_id = 1; conn_id <= last_connection_id; ++conn_id) { + // Last CHLO will be buffered. Others will create connection right away. + if (conn_id <= kMaxNumSessionsToCreate) { + EXPECT_CALL(*dispatcher_, CreateQuicSession(conn_id, client_addr_)) + .WillOnce(testing::Return(CreateSession( + dispatcher_.get(), config_, conn_id, client_addr_, &mock_helper_, + &mock_alarm_factory_, &crypto_config_, + QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_))); + EXPECT_CALL( + *reinterpret_cast<MockQuicConnection*>(session1_->connection()), + ProcessUdpPacket(_, _, _)) + .WillOnce(testing::WithArg<2>( + Invoke(CreateFunctor(&QuicDispatcherTest::ValidatePacket, + base::Unretained(this), conn_id)))); + } + ProcessPacket(client_addr_, conn_id, true, false, SerializeFullCHLO()); + } + + // Process another |kDefaultMaxUndecryptablePackets| + 1 data packets. The + // last one should be dropped. + for (QuicPacketNumber packet_number = 2; + packet_number <= kDefaultMaxUndecryptablePackets + 2; ++packet_number) { + ProcessPacket(client_addr_, last_connection_id, true, false, "data packet"); + } + + // Reset counter and process buffered CHLO. + EXPECT_CALL(*dispatcher_, CreateQuicSession(last_connection_id, client_addr_)) + .WillOnce(testing::Return(CreateSession( + dispatcher_.get(), config_, last_connection_id, client_addr_, + &mock_helper_, &mock_alarm_factory_, &crypto_config_, + QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_))); + // Only CHLO and following |kDefaultMaxUndecryptablePackets| data packets + // should be process. + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()), + ProcessUdpPacket(_, _, _)) + .Times(kDefaultMaxUndecryptablePackets + 1) + .WillRepeatedly(testing::WithArg<2>( + Invoke(CreateFunctor(&QuicDispatcherTest::ValidatePacket, + base::Unretained(this), last_connection_id)))); + dispatcher_->ProcessBufferedChlos(kMaxNumSessionsToCreate); +} + +// Tests that when dispatcher's packet buffer is full, a CHLO on connection +// which doesn't have buffered CHLO should be buffered. +TEST_P(BufferedPacketStoreTest, ReceiveCHLOForBufferedConnection) { + FLAGS_quic_limit_num_new_sessions_per_epoll_loop = true; + QuicBufferedPacketStore* store = + QuicDispatcherPeer::GetBufferedPackets(dispatcher_.get()); + + QuicConnectionId conn_id = 1; + ProcessPacket(client_addr_, conn_id, true, false, "data packet", + PACKET_8BYTE_CONNECTION_ID, PACKET_6BYTE_PACKET_NUMBER, + kDefaultPathId, + /*packet_number=*/1); + // Fill packet buffer to full with CHLOs on other connections. Need to feed + // extra CHLOs because the first |kMaxNumSessionsToCreate| are going to create + // session directly. + for (conn_id = 2; + conn_id <= kDefaultMaxConnectionsInStore + kMaxNumSessionsToCreate; + ++conn_id) { + if (conn_id <= kMaxNumSessionsToCreate + 1) { + EXPECT_CALL(*dispatcher_, CreateQuicSession(conn_id, client_addr_)) + .WillOnce(testing::Return(CreateSession( + dispatcher_.get(), config_, conn_id, client_addr_, &mock_helper_, + &mock_alarm_factory_, &crypto_config_, + QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_))); + EXPECT_CALL( + *reinterpret_cast<MockQuicConnection*>(session1_->connection()), + ProcessUdpPacket(_, _, _)) + .WillOnce(testing::WithArg<2>( + Invoke(CreateFunctor(&QuicDispatcherTest::ValidatePacket, + base::Unretained(this), conn_id)))); + } + ProcessPacket(client_addr_, conn_id, true, false, SerializeFullCHLO()); + } + EXPECT_FALSE(store->HasChloForConnection(/*connection_id=*/1)); + + // CHLO on connection 1 should still be buffered. + ProcessPacket(client_addr_, /*connection_id=*/1, true, false, + SerializeFullCHLO()); + EXPECT_TRUE(store->HasChloForConnection(/*connection_id=*/1)); } } // namespace diff --git a/chromium/net/tools/quic/quic_epoll_alarm_factory.cc b/chromium/net/tools/quic/quic_epoll_alarm_factory.cc index 21a166a70d5..56377b237b3 100644 --- a/chromium/net/tools/quic/quic_epoll_alarm_factory.cc +++ b/chromium/net/tools/quic/quic_epoll_alarm_factory.cc @@ -23,8 +23,7 @@ class QuicEpollAlarm : public QuicAlarm { void SetImpl() override { DCHECK(deadline().IsInitialized()); epoll_server_->RegisterAlarm( - deadline().Subtract(QuicTime::Zero()).ToMicroseconds(), - &epoll_alarm_impl_); + (deadline() - QuicTime::Zero()).ToMicroseconds(), &epoll_alarm_impl_); } void CancelImpl() override { diff --git a/chromium/net/tools/quic/quic_epoll_alarm_factory.h b/chromium/net/tools/quic/quic_epoll_alarm_factory.h index 6b59ccc781e..1f94b79417e 100644 --- a/chromium/net/tools/quic/quic_epoll_alarm_factory.h +++ b/chromium/net/tools/quic/quic_epoll_alarm_factory.h @@ -5,8 +5,8 @@ #ifndef NET_QUIC_QUIC_EPOLL_ALARM_FACTORY_H_ #define NET_QUIC_QUIC_EPOLL_ALARM_FACTORY_H_ -#include "net/quic/quic_alarm.h" -#include "net/quic/quic_alarm_factory.h" +#include "net/quic/core/quic_alarm.h" +#include "net/quic/core/quic_alarm_factory.h" namespace net { diff --git a/chromium/net/tools/quic/quic_epoll_alarm_factory_test.cc b/chromium/net/tools/quic/quic_epoll_alarm_factory_test.cc index 5dfe70185ec..3118be88a91 100644 --- a/chromium/net/tools/quic/quic_epoll_alarm_factory_test.cc +++ b/chromium/net/tools/quic/quic_epoll_alarm_factory_test.cc @@ -48,11 +48,11 @@ TEST_P(QuicEpollAlarmFactoryTest, CreateAlarm) { QuicTime start = clock_.Now(); QuicTime::Delta delta = QuicTime::Delta::FromMicroseconds(1); - alarm->Set(start.Add(delta)); + alarm->Set(start + delta); epoll_server_.AdvanceByAndWaitForEventsAndExecuteCallbacks( delta.ToMicroseconds()); - EXPECT_EQ(start.Add(delta), clock_.Now()); + EXPECT_EQ(start + delta, clock_.Now()); } TEST_P(QuicEpollAlarmFactoryTest, CreateAlarmAndCancel) { @@ -64,11 +64,11 @@ TEST_P(QuicEpollAlarmFactoryTest, CreateAlarmAndCancel) { QuicTime start = clock_.Now(); QuicTime::Delta delta = QuicTime::Delta::FromMicroseconds(1); - alarm->Set(start.Add(delta)); + alarm->Set(start + delta); alarm->Cancel(); epoll_server_.AdvanceByExactlyAndCallCallbacks(delta.ToMicroseconds()); - EXPECT_EQ(start.Add(delta), clock_.Now()); + EXPECT_EQ(start + delta, clock_.Now()); EXPECT_FALSE(unowned_delegate->fired()); } @@ -81,18 +81,18 @@ TEST_P(QuicEpollAlarmFactoryTest, CreateAlarmAndReset) { QuicTime start = clock_.Now(); QuicTime::Delta delta = QuicTime::Delta::FromMicroseconds(1); - alarm->Set(clock_.Now().Add(delta)); + alarm->Set(clock_.Now() + delta); alarm->Cancel(); QuicTime::Delta new_delta = QuicTime::Delta::FromMicroseconds(3); - alarm->Set(clock_.Now().Add(new_delta)); + alarm->Set(clock_.Now() + new_delta); epoll_server_.AdvanceByExactlyAndCallCallbacks(delta.ToMicroseconds()); - EXPECT_EQ(start.Add(delta), clock_.Now()); + EXPECT_EQ(start + delta, clock_.Now()); EXPECT_FALSE(unowned_delegate->fired()); epoll_server_.AdvanceByExactlyAndCallCallbacks( - new_delta.Subtract(delta).ToMicroseconds()); - EXPECT_EQ(start.Add(new_delta), clock_.Now()); + (new_delta - delta).ToMicroseconds()); + EXPECT_EQ(start + new_delta, clock_.Now()); EXPECT_TRUE(unowned_delegate->fired()); } @@ -105,28 +105,25 @@ TEST_P(QuicEpollAlarmFactoryTest, CreateAlarmAndUpdate) { QuicTime start = clock_.Now(); QuicTime::Delta delta = QuicTime::Delta::FromMicroseconds(1); - alarm->Set(clock_.Now().Add(delta)); + alarm->Set(clock_.Now() + delta); QuicTime::Delta new_delta = QuicTime::Delta::FromMicroseconds(3); - alarm->Update(clock_.Now().Add(new_delta), - QuicTime::Delta::FromMicroseconds(1)); + alarm->Update(clock_.Now() + new_delta, QuicTime::Delta::FromMicroseconds(1)); epoll_server_.AdvanceByExactlyAndCallCallbacks(delta.ToMicroseconds()); - EXPECT_EQ(start.Add(delta), clock_.Now()); + EXPECT_EQ(start + delta, clock_.Now()); EXPECT_FALSE(unowned_delegate->fired()); // Move the alarm forward 1us and ensure it doesn't move forward. - alarm->Update(clock_.Now().Add(new_delta), - QuicTime::Delta::FromMicroseconds(2)); + alarm->Update(clock_.Now() + new_delta, QuicTime::Delta::FromMicroseconds(2)); epoll_server_.AdvanceByExactlyAndCallCallbacks( - new_delta.Subtract(delta).ToMicroseconds()); - EXPECT_EQ(start.Add(new_delta), clock_.Now()); + (new_delta - delta).ToMicroseconds()); + EXPECT_EQ(start + new_delta, clock_.Now()); EXPECT_TRUE(unowned_delegate->fired()); // Set the alarm via an update call. new_delta = QuicTime::Delta::FromMicroseconds(5); - alarm->Update(clock_.Now().Add(new_delta), - QuicTime::Delta::FromMicroseconds(1)); + alarm->Update(clock_.Now() + new_delta, QuicTime::Delta::FromMicroseconds(1)); EXPECT_TRUE(alarm->IsSet()); // Update it with an uninitialized time and ensure it's cancelled. diff --git a/chromium/net/tools/quic/quic_epoll_clock.cc b/chromium/net/tools/quic/quic_epoll_clock.cc index cb7717647b7..357bc500c63 100644 --- a/chromium/net/tools/quic/quic_epoll_clock.cc +++ b/chromium/net/tools/quic/quic_epoll_clock.cc @@ -14,13 +14,13 @@ QuicEpollClock::QuicEpollClock(EpollServer* epoll_server) QuicEpollClock::~QuicEpollClock() {} QuicTime QuicEpollClock::ApproximateNow() const { - return QuicTime::Zero().Add( - QuicTime::Delta::FromMicroseconds(epoll_server_->ApproximateNowInUsec())); + return QuicTime::Zero() + QuicTime::Delta::FromMicroseconds( + epoll_server_->ApproximateNowInUsec()); } QuicTime QuicEpollClock::Now() const { - return QuicTime::Zero().Add( - QuicTime::Delta::FromMicroseconds(epoll_server_->NowInUsec())); + return QuicTime::Zero() + + QuicTime::Delta::FromMicroseconds(epoll_server_->NowInUsec()); } QuicWallTime QuicEpollClock::WallNow() const { @@ -30,8 +30,8 @@ QuicWallTime QuicEpollClock::WallNow() const { QuicTime QuicEpollClock::ConvertWallTimeToQuicTime( const QuicWallTime& walltime) const { - return QuicTime::Zero().Add( - QuicTime::Delta::FromMicroseconds(walltime.ToUNIXMicroseconds())); -}; + return QuicTime::Zero() + + QuicTime::Delta::FromMicroseconds(walltime.ToUNIXMicroseconds()); +} } // namespace net diff --git a/chromium/net/tools/quic/quic_epoll_clock.h b/chromium/net/tools/quic/quic_epoll_clock.h index 520c529c9b7..509d3bf7e4d 100644 --- a/chromium/net/tools/quic/quic_epoll_clock.h +++ b/chromium/net/tools/quic/quic_epoll_clock.h @@ -7,8 +7,8 @@ #include "base/compiler_specific.h" #include "base/macros.h" -#include "net/quic/quic_clock.h" -#include "net/quic/quic_time.h" +#include "net/quic/core/quic_clock.h" +#include "net/quic/core/quic_time.h" namespace net { diff --git a/chromium/net/tools/quic/quic_epoll_clock_test.cc b/chromium/net/tools/quic/quic_epoll_clock_test.cc index 039e8bfa318..03d26b82106 100644 --- a/chromium/net/tools/quic/quic_epoll_clock_test.cc +++ b/chromium/net/tools/quic/quic_epoll_clock_test.cc @@ -16,13 +16,13 @@ TEST(QuicEpollClockTest, ApproximateNowInUsec) { epoll_server.set_now_in_usec(1000000); EXPECT_EQ(1000000, - clock.ApproximateNow().Subtract(QuicTime::Zero()).ToMicroseconds()); + (clock.ApproximateNow() - QuicTime::Zero()).ToMicroseconds()); EXPECT_EQ(1u, clock.WallNow().ToUNIXSeconds()); EXPECT_EQ(1000000u, clock.WallNow().ToUNIXMicroseconds()); epoll_server.AdvanceBy(5); EXPECT_EQ(1000005, - clock.ApproximateNow().Subtract(QuicTime::Zero()).ToMicroseconds()); + (clock.ApproximateNow() - QuicTime::Zero()).ToMicroseconds()); EXPECT_EQ(1u, clock.WallNow().ToUNIXSeconds()); EXPECT_EQ(1000005u, clock.WallNow().ToUNIXMicroseconds()); @@ -36,10 +36,10 @@ TEST(QuicEpollClockTest, NowInUsec) { QuicEpollClock clock(&epoll_server); epoll_server.set_now_in_usec(1000000); - EXPECT_EQ(1000000, clock.Now().Subtract(QuicTime::Zero()).ToMicroseconds()); + EXPECT_EQ(1000000, (clock.Now() - QuicTime::Zero()).ToMicroseconds()); epoll_server.AdvanceBy(5); - EXPECT_EQ(1000005, clock.Now().Subtract(QuicTime::Zero()).ToMicroseconds()); + EXPECT_EQ(1000005, (clock.Now() - QuicTime::Zero()).ToMicroseconds()); } } // namespace test diff --git a/chromium/net/tools/quic/quic_epoll_connection_helper.cc b/chromium/net/tools/quic/quic_epoll_connection_helper.cc index eec8fc5fd73..3f9671080e0 100644 --- a/chromium/net/tools/quic/quic_epoll_connection_helper.cc +++ b/chromium/net/tools/quic/quic_epoll_connection_helper.cc @@ -10,7 +10,7 @@ #include "base/logging.h" #include "base/stl_util.h" #include "net/base/ip_endpoint.h" -#include "net/quic/crypto/quic_random.h" +#include "net/quic/core/crypto/quic_random.h" #include "net/tools/epoll_server/epoll_server.h" #include "net/tools/quic/quic_socket_utils.h" diff --git a/chromium/net/tools/quic/quic_epoll_connection_helper.h b/chromium/net/tools/quic/quic_epoll_connection_helper.h index ccd7536b2bc..01964b35b9b 100644 --- a/chromium/net/tools/quic/quic_epoll_connection_helper.h +++ b/chromium/net/tools/quic/quic_epoll_connection_helper.h @@ -12,11 +12,11 @@ #include <set> #include "base/macros.h" -#include "net/quic/quic_connection.h" -#include "net/quic/quic_packet_writer.h" -#include "net/quic/quic_protocol.h" -#include "net/quic/quic_simple_buffer_allocator.h" -#include "net/quic/quic_time.h" +#include "net/quic/core/quic_connection.h" +#include "net/quic/core/quic_packet_writer.h" +#include "net/quic/core/quic_protocol.h" +#include "net/quic/core/quic_simple_buffer_allocator.h" +#include "net/quic/core/quic_time.h" #include "net/tools/quic/quic_default_packet_writer.h" #include "net/tools/quic/quic_epoll_clock.h" diff --git a/chromium/net/tools/quic/quic_epoll_connection_helper_test.cc b/chromium/net/tools/quic/quic_epoll_connection_helper_test.cc index 078eef7b397..0f9106fe088 100644 --- a/chromium/net/tools/quic/quic_epoll_connection_helper_test.cc +++ b/chromium/net/tools/quic/quic_epoll_connection_helper_test.cc @@ -4,7 +4,7 @@ #include "net/tools/quic/quic_epoll_connection_helper.h" -#include "net/quic/crypto/quic_random.h" +#include "net/quic/core/crypto/quic_random.h" #include "net/tools/quic/test_tools/mock_epoll_server.h" #include "testing/gtest/include/gtest/gtest.h" @@ -30,7 +30,7 @@ TEST_F(QuicEpollConnectionHelperTest, GetClock) { QuicTime::Delta delta = QuicTime::Delta::FromMilliseconds(5); epoll_server_.AdvanceBy(delta.ToMicroseconds()); - EXPECT_EQ(start.Add(delta), clock->Now()); + EXPECT_EQ(start + delta, clock->Now()); } TEST_F(QuicEpollConnectionHelperTest, GetRandomGenerator) { diff --git a/chromium/net/tools/quic/quic_in_memory_cache.cc b/chromium/net/tools/quic/quic_in_memory_cache.cc index 3b2f9e00b57..a63f4996e76 100644 --- a/chromium/net/tools/quic/quic_in_memory_cache.cc +++ b/chromium/net/tools/quic/quic_in_memory_cache.cc @@ -14,7 +14,7 @@ #include "base/strings/stringprintf.h" #include "net/http/http_response_headers.h" #include "net/http/http_util.h" -#include "net/quic/quic_bug_tracker.h" +#include "net/quic/core/quic_bug_tracker.h" #include "net/spdy/spdy_http_utils.h" using base::FilePath; @@ -69,7 +69,7 @@ class ResourceFileImpl : public net::QuicInMemoryCache::ResourceFile { body_ = StringPiece(file_contents_.data() + headers_end, file_contents_.size() - headers_end); - CreateSpdyHeadersFromHttpResponse(*http_headers_, HTTP2, &spdy_headers_); + CreateSpdyHeadersFromHttpResponse(*http_headers_, &spdy_headers_); } private: @@ -119,9 +119,10 @@ void QuicInMemoryCache::ResourceFile::SetHostPathFromBase(StringPiece base) { } StringPiece QuicInMemoryCache::ResourceFile::RemoveScheme(StringPiece url) { - if (url.starts_with("https://")) { + if (base::StartsWith(url, "https://", base::CompareCase::INSENSITIVE_ASCII)) { url.remove_prefix(8); - } else if (url.starts_with("http://")) { + } else if (base::StartsWith(url, "http://", + base::CompareCase::INSENSITIVE_ASCII)) { url.remove_prefix(7); } return url; @@ -142,6 +143,8 @@ QuicInMemoryCache* QuicInMemoryCache::GetInstance() { const QuicInMemoryCache::Response* QuicInMemoryCache::GetResponse( StringPiece host, StringPiece path) const { + base::AutoLock lock(response_mutex_); + ResponseMap::const_iterator it = responses_.find(GetKey(host, path)); if (it == responses_.end()) { DVLOG(1) << "Get response for resource failed: host " << host << " path " @@ -178,6 +181,7 @@ void QuicInMemoryCache::AddSimpleResponseWithServerPushResources( } void QuicInMemoryCache::AddDefaultResponse(Response* response) { + base::AutoLock lock(response_mutex_); default_response_.reset(response); } @@ -208,7 +212,8 @@ void QuicInMemoryCache::AddSpecialResponse(StringPiece host, QuicInMemoryCache::QuicInMemoryCache() {} void QuicInMemoryCache::ResetForTests() { - STLDeleteValues(&responses_); + base::AutoLock lock(response_mutex_); + base::STLDeleteValues(&responses_); server_push_resources_.clear(); } @@ -268,6 +273,8 @@ void QuicInMemoryCache::InitializeFromDirectory(const string& cache_directory) { list<ServerPushInfo> QuicInMemoryCache::GetServerPushResources( string request_url) { + base::AutoLock lock(response_mutex_); + list<ServerPushInfo> resources; auto resource_range = server_push_resources_.equal_range(request_url); for (auto it = resource_range.first; it != resource_range.second; ++it) { @@ -279,7 +286,10 @@ list<ServerPushInfo> QuicInMemoryCache::GetServerPushResources( } QuicInMemoryCache::~QuicInMemoryCache() { - STLDeleteValues(&responses_); + { + base::AutoLock lock(response_mutex_); + base::STLDeleteValues(&responses_); + } } void QuicInMemoryCache::AddResponseImpl(StringPiece host, @@ -288,9 +298,11 @@ void QuicInMemoryCache::AddResponseImpl(StringPiece host, SpdyHeaderBlock response_headers, StringPiece response_body, SpdyHeaderBlock response_trailers) { + base::AutoLock lock(response_mutex_); + DCHECK(!host.empty()) << "Host must be populated, e.g. \"www.google.com\""; string key = GetKey(host, path); - if (ContainsKey(responses_, key)) { + if (base::ContainsKey(responses_, key)) { QUIC_BUG << "Response for '" << key << "' already exists!"; return; } @@ -321,13 +333,22 @@ void QuicInMemoryCache::MaybeAddServerPushResources( DVLOG(1) << "Add request-resource association: request url " << request_url << " push url " << push_resource.request_url << " response headers " << push_resource.headers.DebugString(); - server_push_resources_.insert(std::make_pair(request_url, push_resource)); + { + base::AutoLock lock(response_mutex_); + server_push_resources_.insert(std::make_pair(request_url, push_resource)); + } string host = push_resource.request_url.host(); if (host.empty()) { host = request_host.as_string(); } string path = push_resource.request_url.path(); - if (responses_.find(GetKey(host, path)) == responses_.end()) { + bool found_existing_response = false; + { + base::AutoLock lock(response_mutex_); + found_existing_response = + base::ContainsKey(responses_, GetKey(host, path)); + } + if (!found_existing_response) { // Add a server push response to responses map, if it is not in the map. StringPiece body = push_resource.body; DVLOG(1) << "Add response for push resource: host " << host << " path " @@ -339,6 +360,7 @@ void QuicInMemoryCache::MaybeAddServerPushResources( bool QuicInMemoryCache::PushResourceExistsInCache(string original_request_url, ServerPushInfo resource) { + base::AutoLock lock(response_mutex_); auto resource_range = server_push_resources_.equal_range(original_request_url); for (auto it = resource_range.first; it != resource_range.second; ++it) { diff --git a/chromium/net/tools/quic/quic_in_memory_cache.h b/chromium/net/tools/quic/quic_in_memory_cache.h index 2747c9303be..174a7d51635 100644 --- a/chromium/net/tools/quic/quic_in_memory_cache.h +++ b/chromium/net/tools/quic/quic_in_memory_cache.h @@ -17,7 +17,7 @@ #include "base/memory/singleton.h" #include "base/strings/string_piece.h" #include "net/http/http_response_headers.h" -#include "net/quic/spdy_utils.h" +#include "net/quic/core/spdy_utils.h" #include "net/spdy/spdy_framer.h" #include "url/gurl.h" @@ -234,6 +234,10 @@ class QuicInMemoryCache { // A map from request URL to associated server push responses (if any). std::multimap<std::string, ServerPushInfo> server_push_resources_; + // Protects against concurrent access from test threads setting responses, and + // server threads accessing those responses. + mutable base::Lock response_mutex_; + DISALLOW_COPY_AND_ASSIGN(QuicInMemoryCache); }; diff --git a/chromium/net/tools/quic/quic_in_memory_cache_test.cc b/chromium/net/tools/quic/quic_in_memory_cache_test.cc index d31e8fd813e..d220e482af0 100644 --- a/chromium/net/tools/quic/quic_in_memory_cache_test.cc +++ b/chromium/net/tools/quic/quic_in_memory_cache_test.cc @@ -69,7 +69,7 @@ TEST_F(QuicInMemoryCacheTest, AddSimpleResponseGetResponse) { const QuicInMemoryCache::Response* response = cache->GetResponse("www.google.com", "/"); ASSERT_TRUE(response); - ASSERT_TRUE(ContainsKey(response->headers(), ":status")); + ASSERT_TRUE(base::ContainsKey(response->headers(), ":status")); EXPECT_EQ("200", response->headers().find(":status")->second); EXPECT_EQ(response_body.size(), response->body().length()); } @@ -106,9 +106,9 @@ TEST_F(QuicInMemoryCacheTest, ReadsCacheDir) { QuicInMemoryCache::GetInstance()->GetResponse("quic.test.url", "/index.html"); ASSERT_TRUE(response); - ASSERT_TRUE(ContainsKey(response->headers(), ":status")); + ASSERT_TRUE(base::ContainsKey(response->headers(), ":status")); EXPECT_EQ("200", response->headers().find(":status")->second); - ASSERT_TRUE(ContainsKey(response->headers(), "connection")); + ASSERT_TRUE(base::ContainsKey(response->headers(), "connection")); EXPECT_EQ("close", response->headers().find("connection")->second); EXPECT_LT(0U, response->body().length()); } @@ -137,9 +137,9 @@ TEST_F(QuicInMemoryCacheTest, UsesOriginalUrl) { QuicInMemoryCache::GetInstance()->GetResponse("quic.test.url", "/index.html"); ASSERT_TRUE(response); - ASSERT_TRUE(ContainsKey(response->headers(), ":status")); + ASSERT_TRUE(base::ContainsKey(response->headers(), ":status")); EXPECT_EQ("200", response->headers().find(":status")->second); - ASSERT_TRUE(ContainsKey(response->headers(), "connection")); + ASSERT_TRUE(base::ContainsKey(response->headers(), "connection")); EXPECT_EQ("close", response->headers().find("connection")->second); EXPECT_LT(0U, response->body().length()); } @@ -164,20 +164,20 @@ TEST_F(QuicInMemoryCacheTest, DefaultResponse) { // Now we should get the default response for the original request. response = cache->GetResponse("www.google.com", "/"); ASSERT_TRUE(response); - ASSERT_TRUE(ContainsKey(response->headers(), ":status")); + ASSERT_TRUE(base::ContainsKey(response->headers(), ":status")); EXPECT_EQ("200", response->headers().find(":status")->second); // Now add a set response for / and make sure it is returned cache->AddSimpleResponse("www.google.com", "/", 302, ""); response = cache->GetResponse("www.google.com", "/"); ASSERT_TRUE(response); - ASSERT_TRUE(ContainsKey(response->headers(), ":status")); + ASSERT_TRUE(base::ContainsKey(response->headers(), ":status")); EXPECT_EQ("302", response->headers().find(":status")->second); // We should get the default response for other requests. response = cache->GetResponse("www.google.com", "/asd"); ASSERT_TRUE(response); - ASSERT_TRUE(ContainsKey(response->headers(), ":status")); + ASSERT_TRUE(base::ContainsKey(response->headers(), ":status")); EXPECT_EQ("200", response->headers().find(":status")->second); } @@ -249,7 +249,7 @@ TEST_F(QuicInMemoryCacheTest, GetServerPushResourcesAndPushResponses) { const QuicInMemoryCache::Response* response = cache->GetResponse(host, path); ASSERT_TRUE(response); - ASSERT_TRUE(ContainsKey(response->headers(), ":status")); + ASSERT_TRUE(base::ContainsKey(response->headers(), ":status")); EXPECT_EQ(push_response_status[i++], response->headers().find(":status")->second); EXPECT_EQ(push_resource.body, response->body()); diff --git a/chromium/net/tools/quic/quic_packet_printer_bin.cc b/chromium/net/tools/quic/quic_packet_printer_bin.cc index a19b679b735..8be7be5fda3 100644 --- a/chromium/net/tools/quic/quic_packet_printer_bin.cc +++ b/chromium/net/tools/quic/quic_packet_printer_bin.cc @@ -40,8 +40,8 @@ #include "base/command_line.h" #include "base/strings/string_number_conversions.h" #include "base/strings/utf_string_conversions.h" -#include "net/quic/quic_framer.h" -#include "net/quic/quic_utils.h" +#include "net/quic/core/quic_framer.h" +#include "net/quic/core/quic_utils.h" using std::cerr; using std::string; @@ -184,7 +184,7 @@ int main(int argc, char* argv[]) { return 1; } string hex = net::QuicUtils::HexDecode(ArgToString(args[1])); - net::QuicVersionVector versions = net::QuicSupportedVersions(); + net::QuicVersionVector versions = net::AllSupportedVersions(); // Fake a time since we're not actually generating acks. net::QuicTime start(net::QuicTime::Zero()); net::QuicFramer framer(versions, start, perspective); diff --git a/chromium/net/tools/quic/quic_packet_reader.cc b/chromium/net/tools/quic/quic_packet_reader.cc index f3e1fea8480..6a443e57f12 100644 --- a/chromium/net/tools/quic/quic_packet_reader.cc +++ b/chromium/net/tools/quic/quic_packet_reader.cc @@ -15,8 +15,8 @@ #include "base/logging.h" #include "net/base/ip_address.h" #include "net/base/ip_endpoint.h" -#include "net/quic/quic_bug_tracker.h" -#include "net/quic/quic_flags.h" +#include "net/quic/core/quic_bug_tracker.h" +#include "net/quic/core/quic_flags.h" #include "net/tools/quic/quic_dispatcher.h" #include "net/tools/quic/quic_process_packet_interface.h" #include "net/tools/quic/quic_socket_utils.h" @@ -63,21 +63,23 @@ QuicPacketReader::~QuicPacketReader() {} bool QuicPacketReader::ReadAndDispatchPackets( int fd, int port, + bool potentially_small_mtu, const QuicClock& clock, ProcessPacketInterface* processor, QuicPacketCount* packets_dropped) { #if MMSG_MORE - return ReadAndDispatchManyPackets(fd, port, clock, processor, - packets_dropped); + return ReadAndDispatchManyPackets(fd, port, potentially_small_mtu, clock, + processor, packets_dropped); #else - return ReadAndDispatchSinglePacket(fd, port, clock, processor, - packets_dropped); + return ReadAndDispatchSinglePacket(fd, port, potentially_small_mtu, clock, + processor, packets_dropped); #endif } bool QuicPacketReader::ReadAndDispatchManyPackets( int fd, int port, + bool potentially_small_mtu, const QuicClock& clock, ProcessPacketInterface* processor, QuicPacketCount* packets_dropped) { @@ -98,7 +100,6 @@ bool QuicPacketReader::ReadAndDispatchManyPackets( return false; // recvmmsg failed. } - QuicTime fallback_timestamp = QuicTime::Zero(); QuicWallTime fallback_walltimestamp = QuicWallTime::Zero(); for (int i = 0; i < packets_read; ++i) { if (mmsg_hdr_[i].msg_len == 0) { @@ -114,12 +115,9 @@ bool QuicPacketReader::ReadAndDispatchManyPackets( IPEndPoint client_address = IPEndPoint(packets_[i].raw_address); IPAddress server_ip; - QuicTime packet_timestamp = QuicTime::Zero(); QuicWallTime packet_walltimestamp = QuicWallTime::Zero(); - bool latched_walltimestamps = FLAGS_quic_socket_walltimestamps; QuicSocketUtils::GetAddressAndTimestampFromMsghdr( - &mmsg_hdr_[i].msg_hdr, &server_ip, &packet_timestamp, - &packet_walltimestamp, latched_walltimestamps); + &mmsg_hdr_[i].msg_hdr, &server_ip, &packet_walltimestamp); if (!IsInitializedAddress(server_ip)) { QUIC_BUG << "Unable to get server address."; continue; @@ -127,24 +125,19 @@ bool QuicPacketReader::ReadAndDispatchManyPackets( // This isn't particularly desirable, but not all platforms support socket // timestamping. - if (latched_walltimestamps) { - if (packet_walltimestamp.IsZero()) { - if (fallback_walltimestamp.IsZero()) { - fallback_walltimestamp = clock.WallNow(); - } - packet_walltimestamp = fallback_walltimestamp; - } - packet_timestamp = clock.ConvertWallTimeToQuicTime(packet_walltimestamp); - } else { - if (packet_timestamp == QuicTime::Zero()) { - if (fallback_timestamp == QuicTime::Zero()) { - fallback_timestamp = clock.Now(); - } - packet_timestamp = fallback_timestamp; + if (packet_walltimestamp.IsZero()) { + if (fallback_walltimestamp.IsZero()) { + fallback_walltimestamp = clock.WallNow(); } + packet_walltimestamp = fallback_walltimestamp; } + QuicTime timestamp = clock.ConvertWallTimeToQuicTime(packet_walltimestamp); + int ttl = 0; + bool has_ttl = + QuicSocketUtils::GetTtlFromMsghdr(&mmsg_hdr_[i].msg_hdr, &ttl); QuicReceivedPacket packet(reinterpret_cast<char*>(packets_[i].iov.iov_base), - mmsg_hdr_[i].msg_len, packet_timestamp, false); + mmsg_hdr_[i].msg_len, timestamp, false, + potentially_small_mtu, ttl, has_ttl); IPEndPoint server_address(server_ip, port); processor->ProcessPacket(server_address, client_address, packet); } @@ -166,20 +159,18 @@ bool QuicPacketReader::ReadAndDispatchManyPackets( bool QuicPacketReader::ReadAndDispatchSinglePacket( int fd, int port, + bool potentially_small_mtu, const QuicClock& clock, ProcessPacketInterface* processor, QuicPacketCount* packets_dropped) { - bool latched_walltimestamps = FLAGS_quic_socket_walltimestamps; char buf[kMaxPacketSize]; IPEndPoint client_address; IPAddress server_ip; - QuicTime timestamp = QuicTime::Zero(); QuicWallTime walltimestamp = QuicWallTime::Zero(); - int bytes_read = QuicSocketUtils::ReadPacket( - fd, buf, arraysize(buf), packets_dropped, &server_ip, ×tamp, - &walltimestamp, latched_walltimestamps, &client_address); - + int bytes_read = + QuicSocketUtils::ReadPacket(fd, buf, arraysize(buf), packets_dropped, + &server_ip, &walltimestamp, &client_address); if (bytes_read < 0) { return false; // ReadPacket failed. } @@ -190,18 +181,14 @@ bool QuicPacketReader::ReadAndDispatchSinglePacket( } // This isn't particularly desirable, but not all platforms support socket // timestamping. - if (latched_walltimestamps) { - if (walltimestamp.IsZero()) { - walltimestamp = clock.WallNow(); - } - timestamp = clock.ConvertWallTimeToQuicTime(walltimestamp); - } else { - if (timestamp == QuicTime::Zero()) { - timestamp = clock.Now(); - } + if (walltimestamp.IsZero()) { + walltimestamp = clock.WallNow(); } + QuicTime timestamp = clock.ConvertWallTimeToQuicTime(walltimestamp); - QuicReceivedPacket packet(buf, bytes_read, timestamp, false); + QuicReceivedPacket packet(buf, bytes_read, timestamp, false /* owns_buffer */, + potentially_small_mtu, -1 /* ttl */, + false /* ttl_valid */); IPEndPoint server_address(server_ip, port); processor->ProcessPacket(server_address, client_address, packet); diff --git a/chromium/net/tools/quic/quic_packet_reader.h b/chromium/net/tools/quic/quic_packet_reader.h index b388406b478..a4d207581b7 100644 --- a/chromium/net/tools/quic/quic_packet_reader.h +++ b/chromium/net/tools/quic/quic_packet_reader.h @@ -11,8 +11,8 @@ #include <sys/socket.h> #include "base/macros.h" -#include "net/quic/quic_clock.h" -#include "net/quic/quic_protocol.h" +#include "net/quic/core/quic_clock.h" +#include "net/quic/core/quic_protocol.h" #include "net/tools/quic/quic_process_packet_interface.h" #include "net/tools/quic/quic_socket_utils.h" @@ -44,8 +44,11 @@ class QuicPacketReader { // to track dropped packets and some packets are read. // If the socket has timestamping enabled, the per packet timestamps will be // passed to the processor. Otherwise, |clock| will be used. + // If |potentially_small_mtu| is set, the incoming packets have been + // identified as potentially having an unusually small MTU. virtual bool ReadAndDispatchPackets(int fd, int port, + bool potentially_small_mtu, const QuicClock& clock, ProcessPacketInterface* processor, QuicPacketCount* packets_dropped); @@ -57,6 +60,7 @@ class QuicPacketReader { // Reads and dispatches many packets using recvmmsg. bool ReadAndDispatchManyPackets(int fd, int port, + bool potentially_small_mtu, const QuicClock& clock, ProcessPacketInterface* processor, QuicPacketCount* packets_dropped); @@ -64,6 +68,7 @@ class QuicPacketReader { // Reads and dispatches a single packet using recvmsg. static bool ReadAndDispatchSinglePacket(int fd, int port, + bool potentially_small_mtu, const QuicClock& clock, ProcessPacketInterface* processor, QuicPacketCount* packets_dropped); diff --git a/chromium/net/tools/quic/quic_packet_writer_wrapper.cc b/chromium/net/tools/quic/quic_packet_writer_wrapper.cc index 3d008f09c73..de3d92b2e46 100644 --- a/chromium/net/tools/quic/quic_packet_writer_wrapper.cc +++ b/chromium/net/tools/quic/quic_packet_writer_wrapper.cc @@ -4,7 +4,7 @@ #include "net/tools/quic/quic_packet_writer_wrapper.h" -#include "net/quic/quic_types.h" +#include "net/quic/core/quic_types.h" namespace net { diff --git a/chromium/net/tools/quic/quic_packet_writer_wrapper.h b/chromium/net/tools/quic/quic_packet_writer_wrapper.h index 34642a6282d..ea1f9fcb10f 100644 --- a/chromium/net/tools/quic/quic_packet_writer_wrapper.h +++ b/chromium/net/tools/quic/quic_packet_writer_wrapper.h @@ -10,7 +10,7 @@ #include <memory> #include "base/macros.h" -#include "net/quic/quic_packet_writer.h" +#include "net/quic/core/quic_packet_writer.h" namespace net { diff --git a/chromium/net/tools/quic/quic_per_connection_packet_writer.h b/chromium/net/tools/quic/quic_per_connection_packet_writer.h index 6902f3af839..cc48b635e14 100644 --- a/chromium/net/tools/quic/quic_per_connection_packet_writer.h +++ b/chromium/net/tools/quic/quic_per_connection_packet_writer.h @@ -8,8 +8,8 @@ #include <stddef.h> #include "base/macros.h" -#include "net/quic/quic_connection.h" -#include "net/quic/quic_packet_writer.h" +#include "net/quic/core/quic_connection.h" +#include "net/quic/core/quic_packet_writer.h" namespace net { @@ -17,7 +17,7 @@ namespace net { class QuicPerConnectionPacketWriter : public QuicPacketWriter { public: // Does not take ownership of |shared_writer|. - QuicPerConnectionPacketWriter(QuicPacketWriter* shared_writer); + explicit QuicPerConnectionPacketWriter(QuicPacketWriter* shared_writer); ~QuicPerConnectionPacketWriter() override; QuicPacketWriter* shared_writer() const { return shared_writer_; } diff --git a/chromium/net/tools/quic/quic_process_packet_interface.h b/chromium/net/tools/quic/quic_process_packet_interface.h index aa1035d946b..600bc70844b 100644 --- a/chromium/net/tools/quic/quic_process_packet_interface.h +++ b/chromium/net/tools/quic/quic_process_packet_interface.h @@ -7,7 +7,7 @@ #include "base/macros.h" #include "net/base/ip_endpoint.h" -#include "net/quic/quic_protocol.h" +#include "net/quic/core/quic_protocol.h" namespace net { diff --git a/chromium/net/tools/quic/quic_reject_reason_decoder_bin.cc b/chromium/net/tools/quic/quic_reject_reason_decoder_bin.cc new file mode 100644 index 00000000000..aba019947b7 --- /dev/null +++ b/chromium/net/tools/quic/quic_reject_reason_decoder_bin.cc @@ -0,0 +1,47 @@ +// Copyright (c) 2016 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. + +// Decodes the packet HandshakeFailureReason from the chromium histogram +// Net.QuicClientHelloRejectReasons + +#include <iostream> + +#include "base/command_line.h" +#include "base/strings/string_number_conversions.h" +#include "net/quic/core/crypto/crypto_handshake.h" +#include "net/quic/core/crypto/crypto_utils.h" + +using base::CommandLine; +using base::StringToUint; +using net::HandshakeFailureReason; +using net::CryptoUtils; +using net::MAX_FAILURE_REASON; +using std::cerr; +using std::cout; + +int main(int argc, char* argv[]) { + CommandLine::Init(argc, argv); + CommandLine* line = CommandLine::ForCurrentProcess(); + const CommandLine::StringVector& args = line->GetArgs(); + + if (args.size() != 1) { + cerr << "Missing argument (Usage: " << argv[0] << " <packed_reason>\n"; + return 1; + } + + uint32_t packed_error = 0; + if (!StringToUint(args[0], &packed_error)) { + cerr << "Unable to parse: " << args[0] << "\n"; + return 2; + } + + for (int i = 1; i < MAX_FAILURE_REASON; ++i) { + if ((packed_error & (1 << (i - 1))) == 0) { + continue; + } + HandshakeFailureReason reason = static_cast<HandshakeFailureReason>(i); + cout << CryptoUtils::HandshakeFailureReasonToString(reason) << "\n"; + } + return 0; +} diff --git a/chromium/net/tools/quic/quic_server.cc b/chromium/net/tools/quic/quic_server.cc index 94fdfdaca09..fb5d5912b0f 100644 --- a/chromium/net/tools/quic/quic_server.cc +++ b/chromium/net/tools/quic/quic_server.cc @@ -15,19 +15,20 @@ #include "net/base/ip_endpoint.h" #include "net/base/sockaddr_storage.h" -#include "net/quic/crypto/crypto_handshake.h" -#include "net/quic/crypto/quic_random.h" -#include "net/quic/quic_clock.h" -#include "net/quic/quic_crypto_stream.h" -#include "net/quic/quic_data_reader.h" -#include "net/quic/quic_protocol.h" +#include "net/quic/core/crypto/crypto_handshake.h" +#include "net/quic/core/crypto/quic_random.h" +#include "net/quic/core/quic_clock.h" +#include "net/quic/core/quic_crypto_stream.h" +#include "net/quic/core/quic_data_reader.h" +#include "net/quic/core/quic_protocol.h" #include "net/tools/quic/quic_dispatcher.h" #include "net/tools/quic/quic_epoll_alarm_factory.h" #include "net/tools/quic/quic_epoll_clock.h" #include "net/tools/quic/quic_epoll_connection_helper.h" #include "net/tools/quic/quic_in_memory_cache.h" #include "net/tools/quic/quic_packet_reader.h" -#include "net/tools/quic/quic_simple_server_session_helper.h" +#include "net/tools/quic/quic_simple_crypto_server_stream_helper.h" +#include "net/tools/quic/quic_simple_dispatcher.h" #include "net/tools/quic/quic_socket_utils.h" #ifndef SO_RXQ_OVFL @@ -47,14 +48,16 @@ const char kSourceAddressTokenSecret[] = "secret"; } // namespace -QuicServer::QuicServer(ProofSource* proof_source) - : QuicServer(proof_source, +const size_t kNumSessionsToCreatePerSocketEvent = 16; + +QuicServer::QuicServer(std::unique_ptr<ProofSource> proof_source) + : QuicServer(std::move(proof_source), QuicConfig(), QuicCryptoServerConfig::ConfigOptions(), - QuicSupportedVersions()) {} + AllSupportedVersions()) {} QuicServer::QuicServer( - ProofSource* proof_source, + std::unique_ptr<ProofSource> proof_source, const QuicConfig& config, const QuicCryptoServerConfig::ConfigOptions& crypto_config_options, const QuicVersionVector& supported_versions) @@ -65,9 +68,9 @@ QuicServer::QuicServer( config_(config), crypto_config_(kSourceAddressTokenSecret, QuicRandom::GetInstance(), - proof_source), + std::move(proof_source)), crypto_config_options_(crypto_config_options), - supported_versions_(supported_versions), + version_manager_(supported_versions), packet_reader_(new QuicPacketReader()) { Initialize(); } @@ -147,12 +150,12 @@ QuicDefaultPacketWriter* QuicServer::CreateWriter(int fd) { QuicDispatcher* QuicServer::CreateQuicDispatcher() { QuicEpollAlarmFactory alarm_factory(&epoll_server_); - return new QuicDispatcher( - config_, &crypto_config_, supported_versions_, + return new QuicSimpleDispatcher( + config_, &crypto_config_, &version_manager_, std::unique_ptr<QuicEpollConnectionHelper>(new QuicEpollConnectionHelper( &epoll_server_, QuicAllocator::BUFFER_POOL)), - std::unique_ptr<QuicServerSessionBase::Helper>( - new QuicSimpleServerSessionHelper(QuicRandom::GetInstance())), + std::unique_ptr<QuicCryptoServerStream::Helper>( + new QuicSimpleCryptoServerStreamHelper(QuicRandom::GetInstance())), std::unique_ptr<QuicEpollAlarmFactory>( new QuicEpollAlarmFactory(&epoll_server_))); } @@ -176,12 +179,25 @@ void QuicServer::OnEvent(int fd, EpollEvent* event) { if (event->in_events & EPOLLIN) { DVLOG(1) << "EPOLLIN"; + + if (FLAGS_quic_limit_num_new_sessions_per_epoll_loop && + FLAGS_quic_buffer_packet_till_chlo) { + dispatcher_->ProcessBufferedChlos(kNumSessionsToCreatePerSocketEvent); + } + bool more_to_read = true; while (more_to_read) { more_to_read = packet_reader_->ReadAndDispatchPackets( - fd_, port_, QuicEpollClock(&epoll_server_), dispatcher_.get(), + fd_, port_, false /* potentially_small_mtu */, + QuicEpollClock(&epoll_server_), dispatcher_.get(), overflow_supported_ ? &packets_dropped_ : nullptr); } + + if (FLAGS_quic_limit_num_new_sessions_per_epoll_loop && + FLAGS_quic_buffer_packet_till_chlo && dispatcher_->HasChlosBuffered()) { + // Register EPOLLIN event to consume buffered CHLO(s). + event->out_ready_mask |= EPOLLIN; + } } if (event->in_events & EPOLLOUT) { dispatcher_->OnCanWrite(); diff --git a/chromium/net/tools/quic/quic_server.h b/chromium/net/tools/quic/quic_server.h index 76ff17a45b0..0a5b9035e6e 100644 --- a/chromium/net/tools/quic/quic_server.h +++ b/chromium/net/tools/quic/quic_server.h @@ -17,10 +17,10 @@ #include "base/macros.h" #include "net/base/ip_endpoint.h" -#include "net/quic/crypto/quic_crypto_server_config.h" -#include "net/quic/quic_chromium_connection_helper.h" -#include "net/quic/quic_config.h" -#include "net/quic/quic_framer.h" +#include "net/quic/chromium/quic_chromium_connection_helper.h" +#include "net/quic/core/crypto/quic_crypto_server_config.h" +#include "net/quic/core/quic_config.h" +#include "net/quic/core/quic_framer.h" #include "net/tools/epoll_server/epoll_server.h" #include "net/tools/quic/quic_default_packet_writer.h" @@ -35,8 +35,8 @@ class QuicPacketReader; class QuicServer : public EpollCallbackInterface { public: - explicit QuicServer(ProofSource* proof_source); - QuicServer(ProofSource* proof_source, + explicit QuicServer(std::unique_ptr<ProofSource> proof_source); + QuicServer(std::unique_ptr<ProofSource> proof_source, const QuicConfig& config, const QuicCryptoServerConfig::ConfigOptions& server_config_options, const QuicVersionVector& supported_versions); @@ -81,13 +81,12 @@ class QuicServer : public EpollCallbackInterface { const QuicConfig& config() const { return config_; } const QuicCryptoServerConfig& crypto_config() const { return crypto_config_; } - const QuicVersionVector& supported_versions() const { - return supported_versions_; - } EpollServer* epoll_server() { return &epoll_server_; } QuicDispatcher* dispatcher() { return dispatcher_.get(); } + QuicVersionManager* version_manager() { return &version_manager_; } + private: friend class net::test::QuicServerPeer; @@ -122,11 +121,8 @@ class QuicServer : public EpollCallbackInterface { // crypto_config_options_ contains crypto parameters for the handshake. QuicCryptoServerConfig::ConfigOptions crypto_config_options_; - // This vector contains QUIC versions which we currently support. - // This should be ordered such that the highest supported version is the first - // element, with subsequent elements in descending order (versions can be - // skipped as necessary). - QuicVersionVector supported_versions_; + // Used to generate current supported versions. + QuicVersionManager version_manager_; // Point to a QuicPacketReader object on the heap. The reader allocates more // space than allowed on the stack. diff --git a/chromium/net/tools/quic/quic_server_bin.cc b/chromium/net/tools/quic/quic_server_bin.cc index 983c58443bc..b1eafd73f01 100644 --- a/chromium/net/tools/quic/quic_server_bin.cc +++ b/chromium/net/tools/quic/quic_server_bin.cc @@ -14,19 +14,21 @@ #include "base/strings/string_number_conversions.h" #include "net/base/ip_address.h" #include "net/base/ip_endpoint.h" -#include "net/quic/crypto/proof_source_chromium.h" -#include "net/quic/quic_protocol.h" +#include "net/quic/chromium/crypto/proof_source_chromium.h" +#include "net/quic/core/quic_protocol.h" #include "net/tools/quic/quic_in_memory_cache.h" #include "net/tools/quic/quic_server.h" // The port the quic server will listen on. int32_t FLAGS_port = 6121; -net::ProofSource* CreateProofSource(const base::FilePath& cert_path, - const base::FilePath& key_path) { - net::ProofSourceChromium* proof_source = new net::ProofSourceChromium(); +std::unique_ptr<net::ProofSource> CreateProofSource( + const base::FilePath& cert_path, + const base::FilePath& key_path) { + std::unique_ptr<net::ProofSourceChromium> proof_source( + new net::ProofSourceChromium()); CHECK(proof_source->Initialize(cert_path, key_path, base::FilePath())); - return proof_source; + return std::move(proof_source); } int main(int argc, char* argv[]) { @@ -84,7 +86,7 @@ int main(int argc, char* argv[]) { CreateProofSource(line->GetSwitchValuePath("certificate_file"), line->GetSwitchValuePath("key_file")), config, net::QuicCryptoServerConfig::ConfigOptions(), - net::QuicSupportedVersions()); + net::AllSupportedVersions()); server.SetStrikeRegisterNoStartupPeriod(); int rc = server.CreateUDPSocketAndListen(net::IPEndPoint(ip, FLAGS_port)); diff --git a/chromium/net/tools/quic/quic_server_test.cc b/chromium/net/tools/quic/quic_server_test.cc index fbada53be56..0cefdb52fff 100644 --- a/chromium/net/tools/quic/quic_server_test.cc +++ b/chromium/net/tools/quic/quic_server_test.cc @@ -4,13 +4,14 @@ #include "net/tools/quic/quic_server.h" -#include "net/quic/crypto/quic_random.h" -#include "net/quic/quic_utils.h" +#include "net/quic/core/crypto/quic_random.h" +#include "net/quic/core/quic_utils.h" #include "net/quic/test_tools/crypto_test_utils.h" #include "net/quic/test_tools/mock_quic_dispatcher.h" #include "net/tools/quic/quic_epoll_alarm_factory.h" #include "net/tools/quic/quic_epoll_connection_helper.h" -#include "net/tools/quic/quic_simple_server_session_helper.h" +#include "net/tools/quic/quic_simple_crypto_server_stream_helper.h" +#include "net/tools/quic/test_tools/quic_server_peer.h" #include "testing/gtest/include/gtest/gtest.h" using ::testing::_; @@ -22,20 +23,138 @@ namespace test { namespace { +class MockQuicSimpleDispatcher : public QuicSimpleDispatcher { + public: + MockQuicSimpleDispatcher( + const QuicConfig& config, + const QuicCryptoServerConfig* crypto_config, + QuicVersionManager* version_manager, + std::unique_ptr<QuicConnectionHelperInterface> helper, + std::unique_ptr<QuicCryptoServerStream::Helper> session_helper, + std::unique_ptr<QuicAlarmFactory> alarm_factory) + : QuicSimpleDispatcher(config, + crypto_config, + version_manager, + std::move(helper), + std::move(session_helper), + std::move(alarm_factory)) {} + ~MockQuicSimpleDispatcher() override {} + + MOCK_METHOD0(OnCanWrite, void()); + MOCK_CONST_METHOD0(HasPendingWrites, bool()); + MOCK_CONST_METHOD0(HasChlosBuffered, bool()); + MOCK_METHOD1(ProcessBufferedChlos, void(size_t)); +}; + +class TestQuicServer : public QuicServer { + public: + TestQuicServer() : QuicServer(CryptoTestUtils::ProofSourceForTesting()) {} + + ~TestQuicServer() override {} + + MockQuicSimpleDispatcher* mock_dispatcher() { return mock_dispatcher_; } + + protected: + QuicDispatcher* CreateQuicDispatcher() override { + mock_dispatcher_ = new MockQuicSimpleDispatcher( + config(), &crypto_config(), version_manager(), + std::unique_ptr<QuicEpollConnectionHelper>( + new QuicEpollConnectionHelper(epoll_server(), + QuicAllocator::BUFFER_POOL)), + std::unique_ptr<QuicCryptoServerStream::Helper>( + new QuicSimpleCryptoServerStreamHelper(QuicRandom::GetInstance())), + std::unique_ptr<QuicEpollAlarmFactory>( + new QuicEpollAlarmFactory(epoll_server()))); + return mock_dispatcher_; + } + + MockQuicSimpleDispatcher* mock_dispatcher_; +}; + +class QuicServerEpollInTest : public ::testing::Test { + public: + QuicServerEpollInTest() + : port_(net::test::kTestPort), server_address_(Loopback4(), port_) {} + + void StartListening() { + server_.CreateUDPSocketAndListen(server_address_); + ASSERT_TRUE(QuicServerPeer::SetSmallSocket(&server_)); + + if (!server_.overflow_supported()) { + LOG(WARNING) << "Overflow not supported. Not testing."; + return; + } + } + + protected: + QuicFlagSaver saver_; + int port_; + IPEndPoint server_address_; + TestQuicServer server_; +}; + +// Tests that if dispatcher has CHLOs waiting for connection creation, EPOLLIN +// event should try to create connections for them. And set epoll mask with +// EPOLLIN if there are still CHLOs remaining at the end of epoll event. +TEST_F(QuicServerEpollInTest, ProcessBufferedCHLOsOnEpollin) { + FLAGS_quic_limit_num_new_sessions_per_epoll_loop = true; + FLAGS_quic_buffer_packet_till_chlo = true; + // Given an EPOLLIN event, try to create session for buffered CHLOs. In first + // event, dispatcher can't create session for all of CHLOs. So listener should + // register another EPOLLIN event by itself. Even without new packet arrival, + // the rest CHLOs should be process in next epoll event. + StartListening(); + bool more_chlos = true; + MockQuicSimpleDispatcher* dispatcher_ = server_.mock_dispatcher(); + DCHECK(dispatcher_ != nullptr); + EXPECT_CALL(*dispatcher_, OnCanWrite()).Times(testing::AnyNumber()); + EXPECT_CALL(*dispatcher_, ProcessBufferedChlos(_)).Times(2); + EXPECT_CALL(*dispatcher_, HasPendingWrites()).Times(testing::AnyNumber()); + // Expect there are still CHLOs buffered after 1st event. But not any more + // after 2nd event. + EXPECT_CALL(*dispatcher_, HasChlosBuffered()) + .WillOnce(testing::Return(true)) + .WillOnce( + DoAll(testing::Assign(&more_chlos, false), testing::Return(false))); + + // Send a packet to trigger epoll event. + int fd = socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, IPPROTO_UDP); + ASSERT_LT(0, fd); + + char buf[1024]; + memset(buf, 0, arraysize(buf)); + sockaddr_storage storage; + socklen_t storage_size = sizeof(storage); + ASSERT_TRUE(server_address_.ToSockAddr(reinterpret_cast<sockaddr*>(&storage), + &storage_size)); + int rc = sendto(fd, buf, arraysize(buf), 0, + reinterpret_cast<sockaddr*>(&storage), storage_size); + if (rc < 0) { + DVLOG(1) << errno << " " << strerror(errno); + } + + while (more_chlos) { + server_.WaitForEvents(); + } +} + class QuicServerDispatchPacketTest : public ::testing::Test { public: QuicServerDispatchPacketTest() : crypto_config_("blah", QuicRandom::GetInstance(), CryptoTestUtils::ProofSourceForTesting()), + version_manager_(AllSupportedVersions()), dispatcher_( config_, &crypto_config_, + &version_manager_, std::unique_ptr<QuicEpollConnectionHelper>( new QuicEpollConnectionHelper(&eps_, QuicAllocator::BUFFER_POOL)), - std::unique_ptr<QuicServerSessionBase::Helper>( - new QuicSimpleServerSessionHelper(QuicRandom::GetInstance())), + std::unique_ptr<QuicCryptoServerStream::Helper>( + new QuicSimpleCryptoServerStreamHelper( + QuicRandom::GetInstance())), std::unique_ptr<QuicEpollAlarmFactory>( new QuicEpollAlarmFactory(&eps_))) { dispatcher_.InitializeWithWriter(new QuicDefaultPacketWriter(1234)); @@ -49,6 +168,7 @@ class QuicServerDispatchPacketTest : public ::testing::Test { protected: QuicConfig config_; QuicCryptoServerConfig crypto_config_; + QuicVersionManager version_manager_; EpollServer eps_; MockQuicDispatcher dispatcher_; }; diff --git a/chromium/net/tools/quic/quic_simple_client.cc b/chromium/net/tools/quic/quic_simple_client.cc index d179b371a91..48126191ad2 100644 --- a/chromium/net/tools/quic/quic_simple_client.cc +++ b/chromium/net/tools/quic/quic_simple_client.cc @@ -12,63 +12,56 @@ #include "net/base/net_errors.h" #include "net/http/http_request_info.h" #include "net/http/http_response_info.h" -#include "net/quic/crypto/quic_random.h" -#include "net/quic/quic_chromium_alarm_factory.h" -#include "net/quic/quic_chromium_connection_helper.h" -#include "net/quic/quic_chromium_packet_reader.h" -#include "net/quic/quic_chromium_packet_writer.h" -#include "net/quic/quic_connection.h" -#include "net/quic/quic_flags.h" -#include "net/quic/quic_protocol.h" -#include "net/quic/quic_server_id.h" -#include "net/quic/spdy_utils.h" +#include "net/log/net_log_source.h" +#include "net/log/net_log_with_source.h" +#include "net/quic/chromium/quic_chromium_alarm_factory.h" +#include "net/quic/chromium/quic_chromium_connection_helper.h" +#include "net/quic/chromium/quic_chromium_packet_reader.h" +#include "net/quic/chromium/quic_chromium_packet_writer.h" +#include "net/quic/core/crypto/quic_random.h" +#include "net/quic/core/quic_connection.h" +#include "net/quic/core/quic_flags.h" +#include "net/quic/core/quic_protocol.h" +#include "net/quic/core/quic_server_id.h" +#include "net/quic/core/spdy_utils.h" #include "net/spdy/spdy_header_block.h" #include "net/spdy/spdy_http_utils.h" #include "net/udp/udp_client_socket.h" using std::string; using std::vector; +using base::StringPiece; namespace net { -void QuicSimpleClient::ClientQuicDataToResend::Resend() { - client_->SendRequest(*headers_, body_, fin_); - delete headers_; - headers_ = nullptr; -} - -QuicSimpleClient::QuicSimpleClient(IPEndPoint server_address, - const QuicServerId& server_id, - const QuicVersionVector& supported_versions, - ProofVerifier* proof_verifier) - : QuicClientBase(server_id, - supported_versions, - QuicConfig(), - CreateQuicConnectionHelper(), - CreateQuicAlarmFactory(), - proof_verifier), - server_address_(server_address), - local_port_(0), - initialized_(false), - packet_reader_started_(false), - weak_factory_(this) {} - -QuicSimpleClient::QuicSimpleClient(IPEndPoint server_address, - const QuicServerId& server_id, - const QuicVersionVector& supported_versions, - const QuicConfig& config, - ProofVerifier* proof_verifier) +QuicSimpleClient::QuicSimpleClient( + IPEndPoint server_address, + const QuicServerId& server_id, + const QuicVersionVector& supported_versions, + std::unique_ptr<ProofVerifier> proof_verifier) + : QuicSimpleClient(server_address, + server_id, + supported_versions, + QuicConfig(), + std::move(proof_verifier)) {} + +QuicSimpleClient::QuicSimpleClient( + IPEndPoint server_address, + const QuicServerId& server_id, + const QuicVersionVector& supported_versions, + const QuicConfig& config, + std::unique_ptr<ProofVerifier> proof_verifier) : QuicClientBase(server_id, supported_versions, config, CreateQuicConnectionHelper(), CreateQuicAlarmFactory(), - proof_verifier), - server_address_(server_address), - local_port_(0), + std::move(proof_verifier)), initialized_(false), packet_reader_started_(false), - weak_factory_(this) {} + weak_factory_(this) { + set_server_address(server_address); +} QuicSimpleClient::~QuicSimpleClient() { if (connected()) { @@ -76,49 +69,25 @@ QuicSimpleClient::~QuicSimpleClient() { QUIC_PEER_GOING_AWAY, "Shutting down", ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); } - STLDeleteElements(&data_to_resend_on_connect_); - STLDeleteElements(&data_sent_before_handshake_); -} - -bool QuicSimpleClient::Initialize() { - DCHECK(!initialized_); - - QuicClientBase::Initialize(); - - if (!CreateUDPSocket()) { - return false; - } - - initialized_ = true; - return true; -} - -QuicSimpleClient::QuicDataToResend::QuicDataToResend(HttpRequestInfo* headers, - base::StringPiece body, - bool fin) - : headers_(headers), body_(body), fin_(fin) {} - -QuicSimpleClient::QuicDataToResend::~QuicDataToResend() { - if (headers_) { - delete headers_; - } } -bool QuicSimpleClient::CreateUDPSocket() { +bool QuicSimpleClient::CreateUDPSocketAndBind(IPEndPoint server_address, + IPAddress bind_to_address, + int bind_to_port) { std::unique_ptr<UDPClientSocket> socket( new UDPClientSocket(DatagramSocket::DEFAULT_BIND, RandIntCallback(), - &net_log_, NetLog::Source())); + &net_log_, NetLogSource())); - int address_family = server_address_.GetSockAddrFamily(); - if (bind_to_address_.size() != 0) { - client_address_ = IPEndPoint(bind_to_address_, local_port_); + int address_family = server_address.GetSockAddrFamily(); + if (bind_to_address.size() != 0) { + client_address_ = IPEndPoint(bind_to_address, bind_to_port); } else if (address_family == AF_INET) { - client_address_ = IPEndPoint(IPAddress::IPv4AllZeros(), local_port_); + client_address_ = IPEndPoint(IPAddress::IPv4AllZeros(), bind_to_port); } else { - client_address_ = IPEndPoint(IPAddress::IPv6AllZeros(), local_port_); + client_address_ = IPEndPoint(IPAddress::IPv6AllZeros(), bind_to_port); } - int rc = socket->Connect(server_address_); + int rc = socket->Connect(server_address); if (rc != OK) { LOG(ERROR) << "Connect failed: " << ErrorToShortString(rc); return false; @@ -146,7 +115,7 @@ bool QuicSimpleClient::CreateUDPSocket() { packet_reader_.reset(new QuicChromiumPacketReader( socket_.get(), &clock_, this, kQuicYieldAfterPacketsRead, QuicTime::Delta::FromMilliseconds(kQuicYieldAfterDurationMilliseconds), - BoundNetLog())); + NetLogWithSource())); if (socket != nullptr) { socket->Close(); @@ -155,231 +124,23 @@ bool QuicSimpleClient::CreateUDPSocket() { return true; } -void QuicSimpleClient::StartPacketReaderIfNotStarted() { - if (!packet_reader_started_) { - packet_reader_->StartReading(); - packet_reader_started_ = true; - } -} - -bool QuicSimpleClient::Connect() { - // Attempt multiple connects until the maximum number of client hellos have - // been sent. - while (!connected() && - GetNumSentClientHellos() <= QuicCryptoClientStream::kMaxClientHellos) { - StartConnect(); - StartPacketReaderIfNotStarted(); - while (EncryptionBeingEstablished()) { - WaitForEvents(); - } - if (FLAGS_enable_quic_stateless_reject_support && connected() && - !data_to_resend_on_connect_.empty()) { - // A connection has been established and there was previously queued data - // to resend. Resend it and empty the queue. - for (QuicDataToResend* data : data_to_resend_on_connect_) { - data->Resend(); - } - STLDeleteElements(&data_to_resend_on_connect_); - } - if (session() != nullptr && - session()->error() != QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT) { - // We've successfully created a session but we're not connected, and there - // is no stateless reject to recover from. Give up trying. - break; - } - } - if (!connected() && - GetNumSentClientHellos() > QuicCryptoClientStream::kMaxClientHellos && - session() != nullptr && - session()->error() == QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT) { - // The overall connection failed due too many stateless rejects. - set_connection_error(QUIC_CRYPTO_TOO_MANY_REJECTS); - } - return session()->connection()->connected(); -} - -void QuicSimpleClient::StartConnect() { - DCHECK(initialized_); - DCHECK(!connected()); - - set_writer(CreateQuicPacketWriter()); - - if (connected_or_attempting_connect()) { - // Before we destroy the last session and create a new one, gather its stats - // and update the stats for the overall connection. - UpdateStats(); - if (session()->error() == QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT) { - // If the last error was due to a stateless reject, queue up the data to - // be resent on the next successful connection. - // TODO(jokulik): I'm a little bit concerned about ordering here. Maybe - // we should just maintain one queue? - DCHECK(data_to_resend_on_connect_.empty()); - data_to_resend_on_connect_.swap(data_sent_before_handshake_); - } - } - - CreateQuicClientSession(new QuicConnection( - GetNextConnectionId(), server_address_, helper(), alarm_factory(), - writer(), - /* owns_writer= */ false, Perspective::IS_CLIENT, supported_versions())); - - session()->Initialize(); - session()->CryptoConnect(); - set_connected_or_attempting_connect(true); -} - -void QuicSimpleClient::Disconnect() { - DCHECK(initialized_); - - if (connected()) { - session()->connection()->CloseConnection( - QUIC_PEER_GOING_AWAY, "Client disconnecting", - ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); - } - STLDeleteElements(&data_to_resend_on_connect_); - STLDeleteElements(&data_sent_before_handshake_); - +void QuicSimpleClient::CleanUpAllUDPSockets() { reset_writer(); packet_reader_.reset(); packet_reader_started_ = false; - initialized_ = false; } -void QuicSimpleClient::SendRequest(const HttpRequestInfo& headers, - base::StringPiece body, - bool fin) { - QuicSpdyClientStream* stream = CreateReliableClientStream(); - if (stream == nullptr) { - LOG(DFATAL) << "stream creation failed!"; - return; - } - SpdyHeaderBlock header_block; - CreateSpdyHeadersFromHttpRequest(headers, headers.extra_headers, net::HTTP2, - true, &header_block); - stream->set_visitor(this); - stream->SendRequest(std::move(header_block), body, fin); - if (FLAGS_enable_quic_stateless_reject_support) { - // Record this in case we need to resend. - auto* new_headers = new HttpRequestInfo; - *new_headers = headers; - auto* data_to_resend = - new ClientQuicDataToResend(new_headers, body, fin, this); - MaybeAddQuicDataToResend(data_to_resend); - } -} - -void QuicSimpleClient::MaybeAddQuicDataToResend( - QuicDataToResend* data_to_resend) { - DCHECK(FLAGS_enable_quic_stateless_reject_support); - if (session()->IsCryptoHandshakeConfirmed()) { - // The handshake is confirmed. No need to continue saving requests to - // resend. - STLDeleteElements(&data_sent_before_handshake_); - delete data_to_resend; - return; - } - - // The handshake is not confirmed. Push the data onto the queue of data to - // resend if statelessly rejected. - data_sent_before_handshake_.push_back(data_to_resend); -} - -void QuicSimpleClient::SendRequestAndWaitForResponse( - const HttpRequestInfo& request, - base::StringPiece body, - bool fin) { - SendRequest(request, body, fin); - while (WaitForEvents()) { - } -} - -void QuicSimpleClient::SendRequestsAndWaitForResponse( - const base::CommandLine::StringVector& url_list) { - for (size_t i = 0; i < url_list.size(); ++i) { - HttpRequestInfo request; - request.method = "GET"; - request.url = GURL(url_list[i]); - SendRequest(request, "", true); - } - - while (WaitForEvents()) { +void QuicSimpleClient::StartPacketReaderIfNotStarted() { + if (!packet_reader_started_) { + packet_reader_->StartReading(); + packet_reader_started_ = true; } } -bool QuicSimpleClient::WaitForEvents() { - DCHECK(connected()); - +void QuicSimpleClient::RunEventLoop() { + StartPacketReaderIfNotStarted(); base::RunLoop().RunUntilIdle(); - - DCHECK(session() != nullptr); - if (!connected() && - session()->error() == QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT) { - DCHECK(FLAGS_enable_quic_stateless_reject_support); - DVLOG(1) << "Detected stateless reject while waiting for events. " - << "Attempting to reconnect."; - Connect(); - } - - return session()->num_active_requests() != 0; -} - -bool QuicSimpleClient::MigrateSocket(const IPAddress& new_host) { - if (!connected()) { - return false; - } - - bind_to_address_ = new_host; - if (!CreateUDPSocket()) { - return false; - } - - session()->connection()->SetSelfAddress(client_address_); - - QuicPacketWriter* writer = CreateQuicPacketWriter(); - set_writer(writer); - session()->connection()->SetQuicPacketWriter(writer, false); - - return true; -} - -void QuicSimpleClient::OnClose(QuicSpdyStream* stream) { - DCHECK(stream != nullptr); - QuicSpdyClientStream* client_stream = - static_cast<QuicSpdyClientStream*>(stream); - HttpResponseInfo response; - SpdyHeadersToHttpResponse(client_stream->response_headers(), net::HTTP2, - &response); - if (response_listener_.get() != nullptr) { - response_listener_->OnCompleteResponse(stream->id(), *response.headers, - client_stream->data()); - } - - // Store response headers and body. - if (store_response_) { - latest_response_code_ = client_stream->response_code(); - response.headers->GetNormalizedHeaders(&latest_response_headers_); - latest_response_body_ = client_stream->data(); - } -} - -size_t QuicSimpleClient::latest_response_code() const { - LOG_IF(DFATAL, !store_response_) << "Response not stored!"; - return latest_response_code_; -} - -const string& QuicSimpleClient::latest_response_headers() const { - LOG_IF(DFATAL, !store_response_) << "Response not stored!"; - return latest_response_headers_; -} - -const string& QuicSimpleClient::latest_response_body() const { - LOG_IF(DFATAL, !store_response_) << "Response not stored!"; - return latest_response_body_; -} - -QuicConnectionId QuicSimpleClient::GenerateNewConnectionId() { - return helper()->GetRandomGenerator()->RandUint64(); } QuicChromiumConnectionHelper* QuicSimpleClient::CreateQuicConnectionHelper() { @@ -401,6 +162,10 @@ void QuicSimpleClient::OnReadError(int result, Disconnect(); } +IPEndPoint QuicSimpleClient::GetLatestClientAddress() const { + return client_address_; +} + bool QuicSimpleClient::OnPacket(const QuicReceivedPacket& packet, IPEndPoint local_address, IPEndPoint peer_address) { diff --git a/chromium/net/tools/quic/quic_simple_client.h b/chromium/net/tools/quic/quic_simple_client.h index b94399c87a9..a7109b190a0 100644 --- a/chromium/net/tools/quic/quic_simple_client.h +++ b/chromium/net/tools/quic/quic_simple_client.h @@ -20,9 +20,9 @@ #include "net/base/ip_endpoint.h" #include "net/http/http_response_headers.h" #include "net/log/net_log.h" -#include "net/quic/quic_chromium_packet_reader.h" -#include "net/quic/quic_config.h" -#include "net/quic/quic_spdy_stream.h" +#include "net/quic/chromium/quic_chromium_packet_reader.h" +#include "net/quic/core/quic_config.h" +#include "net/quic/core/quic_spdy_stream.h" #include "net/tools/quic/quic_client_base.h" namespace net { @@ -38,168 +38,45 @@ class QuicClientPeer; } // namespace test class QuicSimpleClient : public QuicClientBase, - public QuicSpdyStream::Visitor, public QuicChromiumPacketReader::Visitor { public: - class ResponseListener { - public: - ResponseListener() {} - virtual ~ResponseListener() {} - virtual void OnCompleteResponse(QuicStreamId id, - const HttpResponseHeaders& response_headers, - const std::string& response_body) = 0; - }; - - // The client uses these objects to keep track of any data to resend upon - // receipt of a stateless reject. Recall that the client API allows callers - // to optimistically send data to the server prior to handshake-confirmation. - // If the client subsequently receives a stateless reject, it must tear down - // its existing session, create a new session, and resend all previously sent - // data. It uses these objects to keep track of all the sent data, and to - // resend the data upon a subsequent connection. - class QuicDataToResend { - public: - // Takes ownership of |headers|. |headers| may be null, since it's possible - // to send data without headers. - QuicDataToResend(HttpRequestInfo* headers, - base::StringPiece body, - bool fin); - - virtual ~QuicDataToResend(); - - // Must be overridden by specific classes with the actual method for - // re-sending data. - virtual void Resend() = 0; - - protected: - HttpRequestInfo* headers_; - base::StringPiece body_; - bool fin_; - - private: - DISALLOW_COPY_AND_ASSIGN(QuicDataToResend); - }; - // Create a quic client, which will have events managed by an externally owned // EpollServer. QuicSimpleClient(IPEndPoint server_address, const QuicServerId& server_id, const QuicVersionVector& supported_versions, - ProofVerifier* proof_verifier); + std::unique_ptr<ProofVerifier> proof_verifier); QuicSimpleClient(IPEndPoint server_address, const QuicServerId& server_id, const QuicVersionVector& supported_versions, const QuicConfig& config, - ProofVerifier* proof_verifier); + std::unique_ptr<ProofVerifier> proof_verifier); ~QuicSimpleClient() override; - // From QuicClientBase - bool Initialize() override; - bool WaitForEvents() override; - QuicConnectionId GenerateNewConnectionId() override; - - // "Connect" to the QUIC server, including performing synchronous crypto - // handshake. - bool Connect(); - - // Start the crypto handshake. This can be done in place of the synchronous - // Connect(), but callers are responsible for making sure the crypto handshake - // completes. - void StartConnect(); - - // Disconnects from the QUIC server. - void Disconnect(); - - // Sends an HTTP request and does not wait for response before returning. - void SendRequest(const HttpRequestInfo& headers, - base::StringPiece body, - bool fin); - - // Sends an HTTP request and waits for response before returning. - void SendRequestAndWaitForResponse(const HttpRequestInfo& headers, - base::StringPiece body, - bool fin); - - // Sends a request simple GET for each URL in |args|, and then waits for - // each to complete. - void SendRequestsAndWaitForResponse( - const base::CommandLine::StringVector& url_list); - - // Migrate to a new socket during an active connection. - bool MigrateSocket(const IPAddress& new_host); - // QuicChromiumPacketReader::Visitor void OnReadError(int result, const DatagramClientSocket* socket) override; bool OnPacket(const QuicReceivedPacket& packet, IPEndPoint local_address, IPEndPoint peer_address) override; - // QuicSpdyStream::Visitor - void OnClose(QuicSpdyStream* stream) override; - - // If the crypto handshake has not yet been confirmed, adds the data to the - // queue of data to resend if the client receives a stateless reject. - // Otherwise, deletes the data. Takes ownerership of |data_to_resend|. - void MaybeAddQuicDataToResend(QuicDataToResend* data_to_resend); - - void set_bind_to_address(const IPAddress& address) { - bind_to_address_ = address; - } - - const IPAddress& bind_to_address() const { return bind_to_address_; } - - void set_local_port(int local_port) { local_port_ = local_port; } - - const IPEndPoint& server_address() const { return server_address_; } - - const IPEndPoint& client_address() const { return client_address_; } - - // Takes ownership of the listener. - void set_response_listener(ResponseListener* listener) { - response_listener_.reset(listener); - } - - void set_store_response(bool val) { store_response_ = val; } - - size_t latest_response_code() const; - const std::string& latest_response_headers() const; - const std::string& latest_response_body() const; + // From QuicClientBase + IPEndPoint GetLatestClientAddress() const override; protected: - virtual QuicChromiumAlarmFactory* CreateQuicAlarmFactory(); - virtual QuicChromiumConnectionHelper* CreateQuicConnectionHelper(); - virtual QuicPacketWriter* CreateQuicPacketWriter(); + // From QuicClientBase + QuicPacketWriter* CreateQuicPacketWriter() override; + void RunEventLoop() override; + bool CreateUDPSocketAndBind(IPEndPoint server_address, + IPAddress bind_to_address, + int bind_to_port) override; + void CleanUpAllUDPSockets() override; private: friend class net::test::QuicClientPeer; - // Specific QuicClient class for storing data to resend. - class ClientQuicDataToResend : public QuicDataToResend { - public: - // Takes ownership of |headers|. - ClientQuicDataToResend(HttpRequestInfo* headers, - base::StringPiece body, - bool fin, - QuicSimpleClient* client) - : QuicDataToResend(headers, body, fin), client_(client) { - DCHECK(headers); - DCHECK(client); - } - - ~ClientQuicDataToResend() override {} - - void Resend() override; - - private: - QuicSimpleClient* client_; - - DISALLOW_COPY_AND_ASSIGN(ClientQuicDataToResend); - }; - - // Used during initialization: creates the UDP socket FD, sets socket options, - // and binds the socket to our address. - bool CreateUDPSocket(); + QuicChromiumAlarmFactory* CreateQuicAlarmFactory(); + QuicChromiumConnectionHelper* CreateQuicConnectionHelper(); // Read a UDP packet and hand it to the framer. bool ReadAndProcessPacket(); @@ -209,51 +86,15 @@ class QuicSimpleClient : public QuicClientBase, // Used by |helper_| to time alarms. QuicClock clock_; - // Address of the server. - const IPEndPoint server_address_; - // Address of the client if the client is connected to the server. IPEndPoint client_address_; - // If initialized, the address to bind to. - IPAddress bind_to_address_; - - // Local port to bind to. Initialize to 0. - int local_port_; - // UDP socket connected to the server. std::unique_ptr<UDPClientSocket> socket_; - // Listens for full responses. - std::unique_ptr<ResponseListener> response_listener_; - // Tracks if the client is initialized to connect. bool initialized_; - // If overflow_supported_ is true, this will be the number of packets dropped - // during the lifetime of the server. - QuicPacketCount packets_dropped_; - - // True if the kernel supports SO_RXQ_OVFL, the number of packets dropped - // because the socket would otherwise overflow. - bool overflow_supported_; - - // If true, store the latest response code, headers, and body. - bool store_response_; - // HTTP response code from most recent response. - size_t latest_response_code_; - // HTTP headers from most recent response. - std::string latest_response_headers_; - // Body of most recent response. - std::string latest_response_body_; - - // Keeps track of any data sent before the handshake. - std::vector<QuicDataToResend*> data_sent_before_handshake_; - - // Once the client receives a stateless reject, keeps track of any data that - // must be resent upon a subsequent successful connection. - std::vector<QuicDataToResend*> data_to_resend_on_connect_; - // The log used for the sockets. NetLog net_log_; diff --git a/chromium/net/tools/quic/quic_simple_client_bin.cc b/chromium/net/tools/quic/quic_simple_client_bin.cc index 7cbb6dbdb8b..5fcf760986a 100644 --- a/chromium/net/tools/quic/quic_simple_client_bin.cc +++ b/chromium/net/tools/quic/quic_simple_client_bin.cc @@ -56,11 +56,10 @@ #include "net/cert/multi_log_ct_verifier.h" #include "net/http/http_request_info.h" #include "net/http/transport_security_state.h" -#include "net/log/net_log.h" -#include "net/quic/crypto/proof_verifier_chromium.h" -#include "net/quic/quic_protocol.h" -#include "net/quic/quic_server_id.h" -#include "net/quic/quic_utils.h" +#include "net/quic/chromium/crypto/proof_verifier_chromium.h" +#include "net/quic/core/quic_protocol.h" +#include "net/quic/core/quic_server_id.h" +#include "net/quic/core/quic_utils.h" #include "net/spdy/spdy_header_block.h" #include "net/spdy/spdy_http_utils.h" #include "net/tools/quic/quic_simple_client.h" @@ -113,7 +112,7 @@ class FakeCertVerifier : public net::CertVerifier { net::CertVerifyResult* verify_result, const net::CompletionCallback& callback, std::unique_ptr<Request>* out_req, - const net::BoundNetLog& net_log) override { + const net::NetLogWithSource& net_log) override { return net::OK; } @@ -240,7 +239,7 @@ int main(int argc, char* argv[]) { // Build the client, and try to connect. net::QuicServerId server_id(url.host(), url.EffectiveIntPort(), net::PRIVACY_MODE_DISABLED); - net::QuicVersionVector versions = net::QuicSupportedVersions(); + net::QuicVersionVector versions = net::AllSupportedVersions(); if (FLAGS_quic_version != -1) { versions.clear(); versions.push_back(static_cast<net::QuicVersion>(FLAGS_quic_version)); @@ -254,11 +253,12 @@ int main(int argc, char* argv[]) { new TransportSecurityState); std::unique_ptr<CTVerifier> ct_verifier(new MultiLogCTVerifier()); std::unique_ptr<CTPolicyEnforcer> ct_policy_enforcer(new CTPolicyEnforcer()); - ProofVerifierChromium* proof_verifier = new ProofVerifierChromium( - cert_verifier.get(), ct_policy_enforcer.get(), - transport_security_state.get(), ct_verifier.get()); + std::unique_ptr<ProofVerifierChromium> proof_verifier( + new ProofVerifierChromium(cert_verifier.get(), ct_policy_enforcer.get(), + transport_security_state.get(), + ct_verifier.get())); net::QuicSimpleClient client(net::IPEndPoint(ip_addr, port), server_id, - versions, proof_verifier); + versions, std::move(proof_verifier)); client.set_initial_max_packet_length( FLAGS_initial_mtu != 0 ? FLAGS_initial_mtu : net::kDefaultMaxPacketSize); if (!client.Initialize()) { @@ -316,9 +316,8 @@ int main(int argc, char* argv[]) { // Send the request. net::SpdyHeaderBlock header_block; net::CreateSpdyHeadersFromHttpRequest(request, request.extra_headers, - net::HTTP2, /*direct=*/true, - &header_block); - client.SendRequestAndWaitForResponse(request, body, /*fin=*/true); + /*direct=*/true, &header_block); + client.SendRequestAndWaitForResponse(header_block, body, /*fin=*/true); // Print request and response details. if (!FLAGS_quiet) { @@ -326,9 +325,8 @@ int main(int argc, char* argv[]) { cout << "headers:" << header_block.DebugString(); if (!FLAGS_body_hex.empty()) { // Print the user provided hex, rather than binary body. - cout << "body hex: " << FLAGS_body_hex << endl; - cout << "body ascii: " << net::QuicUtils::BinaryToAscii( - net::QuicUtils::HexDecode(FLAGS_body_hex)) + cout << "body:\n" + << net::QuicUtils::HexDump(net::QuicUtils::HexDecode(FLAGS_body_hex)) << endl; } else { cout << "body: " << body << endl; @@ -339,10 +337,7 @@ int main(int argc, char* argv[]) { string response_body = client.latest_response_body(); if (!FLAGS_body_hex.empty()) { // Assume response is binary data. - cout << "body hex: " << net::QuicUtils::HexEncode(response_body) - << endl; - cout << "body ascii: " << net::QuicUtils::BinaryToAscii(response_body) - << endl; + cout << "body:\n" << net::QuicUtils::HexDump(response_body) << endl; } else { cout << "body: " << response_body << endl; } diff --git a/chromium/net/tools/quic/quic_simple_client_test.cc b/chromium/net/tools/quic/quic_simple_client_test.cc index 02a5807b683..0a571a797b7 100644 --- a/chromium/net/tools/quic/quic_simple_client_test.cc +++ b/chromium/net/tools/quic/quic_simple_client_test.cc @@ -18,7 +18,7 @@ TEST(QuicSimpleClientTest, Initialize) { IPEndPoint server_address(IPEndPoint(net::test::Loopback4(), 80)); QuicServerId server_id("hostname", server_address.port(), PRIVACY_MODE_DISABLED); - QuicVersionVector versions = QuicSupportedVersions(); + QuicVersionVector versions = AllSupportedVersions(); QuicSimpleClient client(server_address, server_id, versions, CryptoTestUtils::ProofVerifierForTesting()); EXPECT_TRUE(client.Initialize()); diff --git a/chromium/net/tools/quic/quic_simple_crypto_server_stream_helper.cc b/chromium/net/tools/quic/quic_simple_crypto_server_stream_helper.cc new file mode 100644 index 00000000000..9f2870ac9a7 --- /dev/null +++ b/chromium/net/tools/quic/quic_simple_crypto_server_stream_helper.cc @@ -0,0 +1,28 @@ +// Copyright (c) 2012 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/tools/quic/quic_simple_crypto_server_stream_helper.h" + +namespace net { + +QuicSimpleCryptoServerStreamHelper::QuicSimpleCryptoServerStreamHelper( + QuicRandom* random) + : random_(random) {} + +QuicSimpleCryptoServerStreamHelper::~QuicSimpleCryptoServerStreamHelper() {} + +QuicConnectionId + QuicSimpleCryptoServerStreamHelper::GenerateConnectionIdForReject( + QuicConnectionId /*connection_id*/) const { + return random_->RandUint64(); +} + +bool QuicSimpleCryptoServerStreamHelper::CanAcceptClientHello( + const CryptoHandshakeMessage& message, + const IPEndPoint& self_address, + std::string* error_details) const { + return true; +} + +} // namespace net diff --git a/chromium/net/tools/quic/quic_simple_crypto_server_stream_helper.h b/chromium/net/tools/quic/quic_simple_crypto_server_stream_helper.h new file mode 100644 index 00000000000..5402a21fc7a --- /dev/null +++ b/chromium/net/tools/quic/quic_simple_crypto_server_stream_helper.h @@ -0,0 +1,35 @@ +// Copyright (c) 2012 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 NET_QUIC_TOOLS_QUIC_SIMPLE_CRYPTO_SERVER_STREAM_HELPER_H_ +#define NET_QUIC_TOOLS_QUIC_SIMPLE_CRYPTO_SERVER_STREAM_HELPER_H_ + +#include "net/quic/core/crypto/quic_random.h" +#include "net/quic/core/quic_crypto_server_stream.h" + +namespace net { + +// Simple helper for server crypto streams which generates a new random +// connection ID for stateless rejects. +class QuicSimpleCryptoServerStreamHelper + : public QuicCryptoServerStream::Helper { + public: + explicit QuicSimpleCryptoServerStreamHelper(QuicRandom* random); + + ~QuicSimpleCryptoServerStreamHelper() override; + + QuicConnectionId GenerateConnectionIdForReject( + QuicConnectionId /*connection_id*/) const override; + + bool CanAcceptClientHello(const CryptoHandshakeMessage& message, + const IPEndPoint& self_address, + std::string* error_details) const override; + + private: + QuicRandom* random_; // Unowned. +}; + +} // namespace net + +#endif // NET_QUIC_TOOLS_QUIC_SIMPLE_CRYPTO_SERVER_STREAM_HELPER_H_ diff --git a/chromium/net/tools/quic/quic_simple_dispatcher.cc b/chromium/net/tools/quic/quic_simple_dispatcher.cc new file mode 100644 index 00000000000..bcad0565cea --- /dev/null +++ b/chromium/net/tools/quic/quic_simple_dispatcher.cc @@ -0,0 +1,43 @@ +// Copyright (c) 2012 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/tools/quic/quic_simple_dispatcher.h" + +#include "net/tools/quic/quic_simple_server_session.h" + +namespace net { + +QuicSimpleDispatcher::QuicSimpleDispatcher( + const QuicConfig& config, + const QuicCryptoServerConfig* crypto_config, + QuicVersionManager* version_manager, + std::unique_ptr<QuicConnectionHelperInterface> helper, + std::unique_ptr<QuicCryptoServerStream::Helper> session_helper, + std::unique_ptr<QuicAlarmFactory> alarm_factory) + : QuicDispatcher(config, + crypto_config, + version_manager, + std::move(helper), + std::move(session_helper), + std::move(alarm_factory)) {} + +QuicSimpleDispatcher::~QuicSimpleDispatcher() {} + +QuicServerSessionBase* QuicSimpleDispatcher::CreateQuicSession( + QuicConnectionId connection_id, + const IPEndPoint& client_address) { + // The QuicServerSessionBase takes ownership of |connection| below. + QuicConnection* connection = new QuicConnection( + connection_id, client_address, helper(), alarm_factory(), + CreatePerConnectionWriter(), + /* owns_writer= */ true, Perspective::IS_SERVER, GetSupportedVersions()); + + QuicServerSessionBase* session = + new QuicSimpleServerSession(config(), connection, this, session_helper(), + crypto_config(), compressed_certs_cache()); + session->Initialize(); + return session; +} + +} // namespace net diff --git a/chromium/net/tools/quic/quic_simple_dispatcher.h b/chromium/net/tools/quic/quic_simple_dispatcher.h new file mode 100644 index 00000000000..15b6b3c09de --- /dev/null +++ b/chromium/net/tools/quic/quic_simple_dispatcher.h @@ -0,0 +1,32 @@ +// Copyright (c) 2012 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 NET_TOOLS_QUIC_QUIC_SIMPLE_DISPATCHER_H_ +#define NET_TOOLS_QUIC_QUIC_SIMPLE_DISPATCHER_H_ + +#include "net/tools/quic/quic_dispatcher.h" + +namespace net { + +class QuicSimpleDispatcher : public QuicDispatcher { + public: + QuicSimpleDispatcher( + const QuicConfig& config, + const QuicCryptoServerConfig* crypto_config, + QuicVersionManager* version_manager, + std::unique_ptr<QuicConnectionHelperInterface> helper, + std::unique_ptr<QuicCryptoServerStream::Helper> session_helper, + std::unique_ptr<QuicAlarmFactory> alarm_factory); + + ~QuicSimpleDispatcher() override; + + protected: + QuicServerSessionBase* CreateQuicSession( + QuicConnectionId connection_id, + const IPEndPoint& client_address) override; +}; + +} // namespace net + +#endif // NET_TOOLS_QUIC_QUIC_SIMPLE_DISPATCHER_H_ diff --git a/chromium/net/tools/quic/quic_simple_per_connection_packet_writer.h b/chromium/net/tools/quic/quic_simple_per_connection_packet_writer.h index d1d4881efb3..c5a62bdc452 100644 --- a/chromium/net/tools/quic/quic_simple_per_connection_packet_writer.h +++ b/chromium/net/tools/quic/quic_simple_per_connection_packet_writer.h @@ -9,8 +9,8 @@ #include "base/macros.h" #include "base/memory/weak_ptr.h" -#include "net/quic/quic_connection.h" -#include "net/quic/quic_packet_writer.h" +#include "net/quic/core/quic_connection.h" +#include "net/quic/core/quic_packet_writer.h" namespace net { diff --git a/chromium/net/tools/quic/quic_simple_server.cc b/chromium/net/tools/quic/quic_simple_server.cc index 814feabd87e..4848cdd901c 100644 --- a/chromium/net/tools/quic/quic_simple_server.cc +++ b/chromium/net/tools/quic/quic_simple_server.cc @@ -11,12 +11,13 @@ #include "base/threading/thread_task_runner_handle.h" #include "net/base/ip_endpoint.h" #include "net/base/net_errors.h" -#include "net/quic/crypto/crypto_handshake.h" -#include "net/quic/crypto/quic_random.h" -#include "net/quic/quic_crypto_stream.h" -#include "net/quic/quic_data_reader.h" -#include "net/quic/quic_protocol.h" -#include "net/tools/quic/quic_dispatcher.h" +#include "net/log/net_log_source.h" +#include "net/quic/core/crypto/crypto_handshake.h" +#include "net/quic/core/crypto/quic_random.h" +#include "net/quic/core/quic_crypto_stream.h" +#include "net/quic/core/quic_data_reader.h" +#include "net/quic/core/quic_protocol.h" +#include "net/tools/quic/quic_simple_dispatcher.h" #include "net/tools/quic/quic_simple_per_connection_packet_writer.h" #include "net/tools/quic/quic_simple_server_packet_writer.h" #include "net/tools/quic/quic_simple_server_session_helper.h" @@ -27,60 +28,30 @@ namespace net { namespace { const char kSourceAddressTokenSecret[] = "secret"; +const size_t kNumSessionsToCreatePerSocketEvent = 16; // Allocate some extra space so we can send an error if the client goes over // the limit. const int kReadBufferSize = 2 * kMaxPacketSize; -class SimpleQuicDispatcher : public QuicDispatcher { - public: - SimpleQuicDispatcher(const QuicConfig& config, - const QuicCryptoServerConfig* crypto_config, - const QuicVersionVector& supported_versions, - QuicConnectionHelperInterface* helper, - QuicAlarmFactory* alarm_factory) - : QuicDispatcher( - config, - crypto_config, - supported_versions, - std::unique_ptr<QuicConnectionHelperInterface>(helper), - std::unique_ptr<QuicServerSessionBase::Helper>( - new QuicSimpleServerSessionHelper(QuicRandom::GetInstance())), - std::unique_ptr<QuicAlarmFactory>(alarm_factory)) {} - - protected: - QuicServerSessionBase* CreateQuicSession( - QuicConnectionId connection_id, - const IPEndPoint& client_address) override { - QuicServerSessionBase* session = - QuicDispatcher::CreateQuicSession(connection_id, client_address); - static_cast<QuicSimplePerConnectionPacketWriter*>( - session->connection()->writer()) - ->set_connection(session->connection()); - return session; - } - - QuicPacketWriter* CreatePerConnectionWriter() override { - return new QuicSimplePerConnectionPacketWriter( - static_cast<QuicSimpleServerPacketWriter*>(writer())); - } -}; - } // namespace -QuicSimpleServer::QuicSimpleServer(ProofSource* proof_source, - const QuicConfig& config, - const QuicVersionVector& supported_versions) - : helper_( +QuicSimpleServer::QuicSimpleServer( + std::unique_ptr<ProofSource> proof_source, + const QuicConfig& config, + const QuicCryptoServerConfig::ConfigOptions& crypto_config_options, + const QuicVersionVector& supported_versions) + : version_manager_(supported_versions), + helper_( new QuicChromiumConnectionHelper(&clock_, QuicRandom::GetInstance())), alarm_factory_(new QuicChromiumAlarmFactory( base::ThreadTaskRunnerHandle::Get().get(), &clock_)), config_(config), + crypto_config_options_(crypto_config_options), crypto_config_(kSourceAddressTokenSecret, QuicRandom::GetInstance(), - proof_source), - supported_versions_(supported_versions), + std::move(proof_source)), read_pending_(false), synchronous_read_count_(0), read_buffer_(new IOBufferWithSize(kReadBufferSize)), @@ -110,14 +81,14 @@ void QuicSimpleServer::Initialize() { std::unique_ptr<CryptoHandshakeMessage> scfg(crypto_config_.AddDefaultConfig( helper_->GetRandomGenerator(), helper_->GetClock(), - QuicCryptoServerConfig::ConfigOptions())); + crypto_config_options_)); } QuicSimpleServer::~QuicSimpleServer() {} int QuicSimpleServer::Listen(const IPEndPoint& address) { std::unique_ptr<UDPServerSocket> socket( - new UDPServerSocket(&net_log_, NetLog::Source())); + new UDPServerSocket(&net_log_, NetLogSource())); socket->AllowAddressReuse(); @@ -153,8 +124,12 @@ int QuicSimpleServer::Listen(const IPEndPoint& address) { socket_.swap(socket); - dispatcher_.reset(new SimpleQuicDispatcher( - config_, &crypto_config_, supported_versions_, helper_, alarm_factory_)); + dispatcher_.reset(new QuicSimpleDispatcher( + config_, &crypto_config_, &version_manager_, + std::unique_ptr<QuicConnectionHelperInterface>(helper_), + std::unique_ptr<QuicCryptoServerStream::Helper>( + new QuicSimpleServerSessionHelper(QuicRandom::GetInstance())), + std::unique_ptr<QuicAlarmFactory>(alarm_factory_))); QuicSimpleServerPacketWriter* writer = new QuicSimpleServerPacketWriter(socket_.get(), dispatcher_.get()); dispatcher_->InitializeWithWriter(writer); @@ -174,6 +149,11 @@ void QuicSimpleServer::Shutdown() { } void QuicSimpleServer::StartReading() { + if (synchronous_read_count_ == 0) { + // Only process buffered packets once per message loop. + dispatcher_->ProcessBufferedChlos(kNumSessionsToCreatePerSocketEvent); + } + if (read_pending_) { return; } @@ -185,6 +165,12 @@ void QuicSimpleServer::StartReading() { if (result == ERR_IO_PENDING) { synchronous_read_count_ = 0; + if (dispatcher_->HasChlosBuffered()) { + // No more packets to read, so yield before processing buffered packets. + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::Bind(&QuicSimpleServer::StartReading, + weak_factory_.GetWeakPtr())); + } return; } diff --git a/chromium/net/tools/quic/quic_simple_server.h b/chromium/net/tools/quic/quic_simple_server.h index 7fe28a424a9..4b24d9cef73 100644 --- a/chromium/net/tools/quic/quic_simple_server.h +++ b/chromium/net/tools/quic/quic_simple_server.h @@ -14,11 +14,11 @@ #include "net/base/io_buffer.h" #include "net/base/ip_endpoint.h" #include "net/log/net_log.h" -#include "net/quic/crypto/quic_crypto_server_config.h" -#include "net/quic/quic_chromium_alarm_factory.h" -#include "net/quic/quic_chromium_connection_helper.h" -#include "net/quic/quic_clock.h" -#include "net/quic/quic_config.h" +#include "net/quic/chromium/quic_chromium_alarm_factory.h" +#include "net/quic/chromium/quic_chromium_connection_helper.h" +#include "net/quic/core/crypto/quic_crypto_server_config.h" +#include "net/quic/core/quic_clock.h" +#include "net/quic/core/quic_config.h" namespace net { @@ -33,9 +33,11 @@ class QuicSimpleServerPeer; class QuicSimpleServer { public: - QuicSimpleServer(ProofSource* proof_source, - const QuicConfig& config, - const QuicVersionVector& supported_versions); + QuicSimpleServer( + std::unique_ptr<ProofSource> proof_source, + const QuicConfig& config, + const QuicCryptoServerConfig::ConfigOptions& crypto_config_options, + const QuicVersionVector& supported_versions); virtual ~QuicSimpleServer(); @@ -59,12 +61,16 @@ class QuicSimpleServer { QuicDispatcher* dispatcher() { return dispatcher_.get(); } + IPEndPoint server_address() const { return server_address_; } + private: friend class test::QuicSimpleServerPeer; // Initialize the internal state of the server. void Initialize(); + QuicVersionManager version_manager_; + // Accepts data from the framer and demuxes clients to sessions. std::unique_ptr<QuicDispatcher> dispatcher_; @@ -83,15 +89,12 @@ class QuicSimpleServer { // config_ contains non-crypto parameters that are negotiated in the crypto // handshake. QuicConfig config_; + // crypto_config_ contains crypto parameters that are negotiated in the crypto + // handshake. + QuicCryptoServerConfig::ConfigOptions crypto_config_options_; // crypto_config_ contains crypto parameters for the handshake. QuicCryptoServerConfig crypto_config_; - // This vector contains QUIC versions which we currently support. - // This should be ordered such that the highest supported version is the first - // element, with subsequent elements in descending order (versions can be - // skipped as necessary). - QuicVersionVector supported_versions_; - // The address that the server listens on. IPEndPoint server_address_; diff --git a/chromium/net/tools/quic/quic_simple_server_bin.cc b/chromium/net/tools/quic/quic_simple_server_bin.cc index b04bf15bce3..9187c9957dc 100644 --- a/chromium/net/tools/quic/quic_simple_server_bin.cc +++ b/chromium/net/tools/quic/quic_simple_server_bin.cc @@ -14,19 +14,21 @@ #include "base/strings/string_number_conversions.h" #include "net/base/ip_address.h" #include "net/base/ip_endpoint.h" -#include "net/quic/crypto/proof_source_chromium.h" -#include "net/quic/quic_protocol.h" +#include "net/quic/chromium/crypto/proof_source_chromium.h" +#include "net/quic/core/quic_protocol.h" #include "net/tools/quic/quic_in_memory_cache.h" #include "net/tools/quic/quic_simple_server.h" // The port the quic server will listen on. int32_t FLAGS_port = 6121; -net::ProofSource* CreateProofSource(const base::FilePath& cert_path, - const base::FilePath& key_path) { - net::ProofSourceChromium* proof_source = new net::ProofSourceChromium(); +std::unique_ptr<net::ProofSource> CreateProofSource( + const base::FilePath& cert_path, + const base::FilePath& key_path) { + std::unique_ptr<net::ProofSourceChromium> proof_source( + new net::ProofSourceChromium()); CHECK(proof_source->Initialize(cert_path, key_path, base::FilePath())); - return proof_source; + return std::move(proof_source); } int main(int argc, char* argv[]) { @@ -83,7 +85,8 @@ int main(int argc, char* argv[]) { net::QuicSimpleServer server( CreateProofSource(line->GetSwitchValuePath("certificate_file"), line->GetSwitchValuePath("key_file")), - config, net::QuicSupportedVersions()); + config, net::QuicCryptoServerConfig::ConfigOptions(), + net::AllSupportedVersions()); server.SetStrikeRegisterNoStartupPeriod(); int rc = server.Listen(net::IPEndPoint(ip, FLAGS_port)); diff --git a/chromium/net/tools/quic/quic_simple_server_packet_writer.cc b/chromium/net/tools/quic/quic_simple_server_packet_writer.cc index 7d64b316e23..a145086d782 100644 --- a/chromium/net/tools/quic/quic_simple_server_packet_writer.cc +++ b/chromium/net/tools/quic/quic_simple_server_packet_writer.cc @@ -45,7 +45,9 @@ void QuicSimpleServerPacketWriter::OnWriteComplete(int rv) { DCHECK_NE(rv, ERR_IO_PENDING); write_blocked_ = false; WriteResult result(rv < 0 ? WRITE_STATUS_ERROR : WRITE_STATUS_OK, rv); - base::ResetAndReturn(&callback_).Run(result); + if (!callback_.is_null()) { + base::ResetAndReturn(&callback_).Run(result); + } blocked_writer_->OnCanWrite(); } @@ -71,7 +73,6 @@ WriteResult QuicSimpleServerPacketWriter::WritePacket( scoped_refptr<StringIOBuffer> buf( new StringIOBuffer(std::string(buffer, buf_len))); DCHECK(!IsWriteBlocked()); - DCHECK(!callback_.is_null()); int rv; if (buf_len <= static_cast<size_t>(std::numeric_limits<int>::max())) { rv = socket_->SendTo( diff --git a/chromium/net/tools/quic/quic_simple_server_packet_writer.h b/chromium/net/tools/quic/quic_simple_server_packet_writer.h index 3a6b562d639..d4cc814eadf 100644 --- a/chromium/net/tools/quic/quic_simple_server_packet_writer.h +++ b/chromium/net/tools/quic/quic_simple_server_packet_writer.h @@ -11,9 +11,9 @@ #include "base/macros.h" #include "base/memory/weak_ptr.h" #include "net/base/ip_endpoint.h" -#include "net/quic/quic_connection.h" -#include "net/quic/quic_packet_writer.h" -#include "net/quic/quic_protocol.h" +#include "net/quic/core/quic_connection.h" +#include "net/quic/core/quic_packet_writer.h" +#include "net/quic/core/quic_protocol.h" namespace net { @@ -33,15 +33,19 @@ class QuicSimpleServerPacketWriter : public QuicPacketWriter { QuicBlockedWriterInterface* blocked_writer); ~QuicSimpleServerPacketWriter() override; - // Use this method to write packets rather than WritePacket: - // QuicSimpleServerPacketWriter requires a callback to exist for every - // write, which will be called once the write completes. - virtual WriteResult WritePacketWithCallback(const char* buffer, - size_t buf_len, - const IPAddress& self_address, - const IPEndPoint& peer_address, - PerPacketOptions* options, - WriteCallback callback); + // Wraps WritePacket, and ensures that |callback| is run on successful write. + WriteResult WritePacketWithCallback(const char* buffer, + size_t buf_len, + const IPAddress& self_address, + const IPEndPoint& peer_address, + PerPacketOptions* options, + WriteCallback callback); + + WriteResult WritePacket(const char* buffer, + size_t buf_len, + const IPAddress& self_address, + const IPEndPoint& peer_address, + PerPacketOptions* options) override; void OnWriteComplete(int rv); @@ -51,14 +55,6 @@ class QuicSimpleServerPacketWriter : public QuicPacketWriter { void SetWritable() override; QuicByteCount GetMaxPacketSize(const IPEndPoint& peer_address) const override; - protected: - // Do not call WritePacket on its own -- use WritePacketWithCallback - WriteResult WritePacket(const char* buffer, - size_t buf_len, - const IPAddress& self_address, - const IPEndPoint& peer_address, - PerPacketOptions* options) override; - private: UDPServerSocket* socket_; diff --git a/chromium/net/tools/quic/quic_simple_server_session.cc b/chromium/net/tools/quic/quic_simple_server_session.cc index 3db89a4cafe..64d987bacdf 100644 --- a/chromium/net/tools/quic/quic_simple_server_session.cc +++ b/chromium/net/tools/quic/quic_simple_server_session.cc @@ -8,11 +8,11 @@ #include "base/logging.h" #include "base/stl_util.h" -#include "net/quic/proto/cached_network_parameters.pb.h" -#include "net/quic/quic_connection.h" -#include "net/quic/quic_flags.h" -#include "net/quic/quic_spdy_session.h" -#include "net/quic/reliable_quic_stream.h" +#include "net/quic/core/proto/cached_network_parameters.pb.h" +#include "net/quic/core/quic_connection.h" +#include "net/quic/core/quic_flags.h" +#include "net/quic/core/quic_spdy_session.h" +#include "net/quic/core/reliable_quic_stream.h" #include "net/tools/quic/quic_simple_server_stream.h" #include "url/gurl.h" @@ -24,7 +24,7 @@ QuicSimpleServerSession::QuicSimpleServerSession( const QuicConfig& config, QuicConnection* connection, QuicServerSessionBase::Visitor* visitor, - QuicServerSessionBase::Helper* helper, + QuicCryptoServerStream::Helper* helper, const QuicCryptoServerConfig* crypto_config, QuicCompressedCertsCache* compressed_certs_cache) : QuicServerSessionBase(config, @@ -35,7 +35,9 @@ QuicSimpleServerSession::QuicSimpleServerSession( compressed_certs_cache), highest_promised_stream_id_(0) {} -QuicSimpleServerSession::~QuicSimpleServerSession() {} +QuicSimpleServerSession::~QuicSimpleServerSession() { + delete connection(); +} QuicCryptoServerStreamBase* QuicSimpleServerSession::CreateQuicCryptoServerStream( @@ -43,7 +45,7 @@ QuicSimpleServerSession::CreateQuicCryptoServerStream( QuicCompressedCertsCache* compressed_certs_cache) { return new QuicCryptoServerStream(crypto_config, compressed_certs_cache, FLAGS_enable_quic_stateless_reject_support, - this); + this, stream_helper()); } void QuicSimpleServerSession::StreamDraining(QuicStreamId id) { @@ -175,7 +177,7 @@ void QuicSimpleServerSession::SendPushPromise(QuicStreamId original_stream_id, DVLOG(1) << "stream " << original_stream_id << " send PUSH_PROMISE for promised stream " << promised_stream_id; headers_stream()->WritePushPromise(original_stream_id, promised_stream_id, - std::move(headers), nullptr); + std::move(headers)); } void QuicSimpleServerSession::HandlePromisedPushRequests() { diff --git a/chromium/net/tools/quic/quic_simple_server_session.h b/chromium/net/tools/quic/quic_simple_server_session.h index fe806a43463..16ebdfb22fd 100644 --- a/chromium/net/tools/quic/quic_simple_server_session.h +++ b/chromium/net/tools/quic/quic_simple_server_session.h @@ -18,10 +18,10 @@ #include <vector> #include "base/macros.h" -#include "net/quic/quic_crypto_server_stream.h" -#include "net/quic/quic_protocol.h" -#include "net/quic/quic_server_session_base.h" -#include "net/quic/quic_spdy_session.h" +#include "net/quic/core/quic_crypto_server_stream.h" +#include "net/quic/core/quic_protocol.h" +#include "net/quic/core/quic_server_session_base.h" +#include "net/quic/core/quic_spdy_session.h" #include "net/tools/quic/quic_in_memory_cache.h" #include "net/tools/quic/quic_simple_server_stream.h" @@ -58,10 +58,11 @@ class QuicSimpleServerSession : public QuicServerSessionBase { bool is_cancelled; }; + // Takes ownership of |connection|. QuicSimpleServerSession(const QuicConfig& config, QuicConnection* connection, QuicServerSessionBase::Visitor* visitor, - QuicServerSessionBase::Helper* helper, + QuicCryptoServerStream::Helper* helper, const QuicCryptoServerConfig* crypto_config, QuicCompressedCertsCache* compressed_certs_cache); diff --git a/chromium/net/tools/quic/quic_simple_server_session_helper.h b/chromium/net/tools/quic/quic_simple_server_session_helper.h index 6ef33fa8d11..a876983b758 100644 --- a/chromium/net/tools/quic/quic_simple_server_session_helper.h +++ b/chromium/net/tools/quic/quic_simple_server_session_helper.h @@ -5,14 +5,14 @@ #ifndef NET_TOOLS_QUIC_QUIC_SIMPLE_SERVER_SESSION_HELPER_H_ #define NET_TOOLS_QUIC_QUIC_SIMPLE_SERVER_SESSION_HELPER_H_ -#include "net/quic/crypto/quic_random.h" -#include "net/quic/quic_server_session_base.h" +#include "net/quic/core/crypto/quic_random.h" +#include "net/quic/core/quic_server_session_base.h" namespace net { // Simple helper for server sessions which generates a new random // connection ID for stateless rejects. -class QuicSimpleServerSessionHelper : public QuicServerSessionBase::Helper { +class QuicSimpleServerSessionHelper : public QuicCryptoServerStream::Helper { public: explicit QuicSimpleServerSessionHelper(QuicRandom* random); diff --git a/chromium/net/tools/quic/quic_simple_server_session_helper_test.cc b/chromium/net/tools/quic/quic_simple_server_session_helper_test.cc index e739ada62da..c25c60e54a8 100644 --- a/chromium/net/tools/quic/quic_simple_server_session_helper_test.cc +++ b/chromium/net/tools/quic/quic_simple_server_session_helper_test.cc @@ -2,16 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "net/tools/quic/quic_simple_server_session_helper.h" - #include "net/quic/test_tools/mock_random.h" +#include "net/tools/quic/quic_simple_crypto_server_stream_helper.h" + #include "testing/gtest/include/gtest/gtest.h" namespace net { -TEST(QuicSimpleServerSessionHelperTest, GenerateConnectionIdForReject) { +TEST(QuicSimpleCryptoServerStreamHelperTest, GenerateConnectionIdForReject) { test::MockRandom random; - QuicSimpleServerSessionHelper helper(&random); + QuicSimpleCryptoServerStreamHelper helper(&random); EXPECT_EQ(random.RandUint64(), helper.GenerateConnectionIdForReject(42)); } diff --git a/chromium/net/tools/quic/quic_simple_server_session_test.cc b/chromium/net/tools/quic/quic_simple_server_session_test.cc index 8e93f9bb37f..518ce1da058 100644 --- a/chromium/net/tools/quic/quic_simple_server_session_test.cc +++ b/chromium/net/tools/quic/quic_simple_server_session_test.cc @@ -9,12 +9,12 @@ #include "base/macros.h" #include "base/strings/string_number_conversions.h" -#include "net/quic/crypto/quic_crypto_server_config.h" -#include "net/quic/crypto/quic_random.h" -#include "net/quic/proto/cached_network_parameters.pb.h" -#include "net/quic/quic_connection.h" -#include "net/quic/quic_crypto_server_stream.h" -#include "net/quic/quic_utils.h" +#include "net/quic/core/crypto/quic_crypto_server_config.h" +#include "net/quic/core/crypto/quic_random.h" +#include "net/quic/core/proto/cached_network_parameters.pb.h" +#include "net/quic/core/quic_connection.h" +#include "net/quic/core/quic_crypto_server_stream.h" +#include "net/quic/core/quic_utils.h" #include "net/quic/test_tools/crypto_test_utils.h" #include "net/quic/test_tools/quic_config_peer.h" #include "net/quic/test_tools/quic_connection_peer.h" @@ -43,7 +43,6 @@ using net::test::QuicSessionPeer; using net::test::QuicSpdySessionPeer; using net::test::QuicSustainedBandwidthRecorderPeer; using net::test::SupportedVersions; -using net::test::ValueRestore; using net::test::kClientDataStreamId1; using net::test::kClientDataStreamId2; using net::test::kClientDataStreamId3; @@ -54,6 +53,7 @@ using testing::StrictMock; using testing::_; using testing::InSequence; using testing::Return; +using testing::AtLeast; namespace net { namespace test { @@ -70,16 +70,14 @@ class MockQuicHeadersStream : public QuicHeadersStream { // mocked directly. size_t WritePushPromise(QuicStreamId original_stream_id, QuicStreamId promised_stream_id, - SpdyHeaderBlock headers, - QuicAckListenerInterface* ack_listener) override { - return WritePushPromiseMock(original_stream_id, promised_stream_id, headers, - ack_listener); + SpdyHeaderBlock headers) override { + return WritePushPromiseMock(original_stream_id, promised_stream_id, + headers); } - MOCK_METHOD4(WritePushPromiseMock, + MOCK_METHOD3(WritePushPromiseMock, size_t(QuicStreamId original_stream_id, QuicStreamId promised_stream_id, - const SpdyHeaderBlock& headers, - QuicAckListenerInterface* ack_listener)); + const SpdyHeaderBlock& headers)); size_t WriteHeaders(QuicStreamId stream_id, SpdyHeaderBlock headers, @@ -101,11 +99,13 @@ class MockQuicCryptoServerStream : public QuicCryptoServerStream { explicit MockQuicCryptoServerStream( const QuicCryptoServerConfig* crypto_config, QuicCompressedCertsCache* compressed_certs_cache, - QuicServerSessionBase* session) + QuicServerSessionBase* session, + QuicCryptoServerStream::Helper* helper) : QuicCryptoServerStream(crypto_config, compressed_certs_cache, FLAGS_enable_quic_stateless_reject_support, - session) {} + session, + helper) {} ~MockQuicCryptoServerStream() override {} MOCK_METHOD1(SendServerConfigUpdate, @@ -181,7 +181,6 @@ class QuicSimpleServerSessionTest CryptoTestUtils::ProofSourceForTesting()), compressed_certs_cache_( QuicCompressedCertsCache::kQuicCompressedCertsCacheSize) { - FLAGS_quic_always_log_bugs_for_tests = true; config_.SetMaxStreamsPerConnection(kMaxStreamsForTest, kMaxStreamsForTest); config_.SetMaxIncomingDynamicStreamsToSend(kMaxStreamsForTest); QuicConfigPeer::SetReceivedMaxIncomingDynamicStreams(&config_, @@ -194,9 +193,9 @@ class QuicSimpleServerSessionTest connection_ = new StrictMock<MockQuicConnectionWithSendStreamData>( &helper_, &alarm_factory_, Perspective::IS_SERVER, SupportedVersions(GetParam())); - session_.reset(new QuicSimpleServerSession( - config_, connection_, &owner_, &session_helper_, &crypto_config_, - &compressed_certs_cache_)); + session_.reset(new QuicSimpleServerSession(config_, connection_, &owner_, + &stream_helper_, &crypto_config_, + &compressed_certs_cache_)); MockClock clock; handshake_message_.reset(crypto_config_.AddDefaultConfig( QuicRandom::GetInstance(), &clock, @@ -205,14 +204,13 @@ class QuicSimpleServerSessionTest visitor_ = QuicConnectionPeer::GetVisitor(connection_); headers_stream_ = new MockQuicHeadersStream(session_.get()); QuicSpdySessionPeer::SetHeadersStream(session_.get(), headers_stream_); - // TODO(jri): Remove this line once tests pass. - FLAGS_quic_cede_correctly = false; session_->OnConfigNegotiated(); } + QuicFlagSaver flags_; // Save/restore all QUIC flag values. StrictMock<MockQuicServerSessionVisitor> owner_; - StrictMock<MockQuicServerSessionHelper> session_helper_; + StrictMock<MockQuicCryptoServerStreamHelper> stream_helper_; MockQuicConnectionHelper helper_; MockAlarmFactory alarm_factory_; StrictMock<MockQuicConnectionWithSendStreamData>* connection_; @@ -227,7 +225,7 @@ class QuicSimpleServerSessionTest INSTANTIATE_TEST_CASE_P(Tests, QuicSimpleServerSessionTest, - ::testing::ValuesIn(QuicSupportedVersions())); + ::testing::ValuesIn(AllSupportedVersions())); TEST_P(QuicSimpleServerSessionTest, CloseStreamDueToReset) { // Open a stream, then reset it. @@ -302,9 +300,9 @@ TEST_P(QuicSimpleServerSessionTest, CreateIncomingDynamicStreamDisconnected) { // Tests that incoming stream creation fails when connection is not connected. size_t initial_num_open_stream = session_->GetNumOpenIncomingStreams(); QuicConnectionPeer::TearDownLocalConnectionState(connection_); - EXPECT_DFATAL(QuicSimpleServerSessionPeer::CreateIncomingDynamicStream( - session_.get(), kClientDataStreamId1), - "ShouldCreateIncomingDynamicStream called when disconnected"); + EXPECT_QUIC_BUG(QuicSimpleServerSessionPeer::CreateIncomingDynamicStream( + session_.get(), kClientDataStreamId1), + "ShouldCreateIncomingDynamicStream called when disconnected"); EXPECT_EQ(initial_num_open_stream, session_->GetNumOpenIncomingStreams()); } @@ -330,9 +328,9 @@ TEST_P(QuicSimpleServerSessionTest, CreateOutgoingDynamicStreamDisconnected) { // Tests that outgoing stream creation fails when connection is not connected. size_t initial_num_open_stream = session_->GetNumOpenOutgoingStreams(); QuicConnectionPeer::TearDownLocalConnectionState(connection_); - EXPECT_DFATAL(QuicSimpleServerSessionPeer::CreateOutgoingDynamicStream( - session_.get(), kDefaultPriority), - "ShouldCreateOutgoingDynamicStream called when disconnected"); + EXPECT_QUIC_BUG(QuicSimpleServerSessionPeer::CreateOutgoingDynamicStream( + session_.get(), kDefaultPriority), + "ShouldCreateOutgoingDynamicStream called when disconnected"); EXPECT_EQ(initial_num_open_stream, session_->GetNumOpenOutgoingStreams()); } @@ -341,9 +339,9 @@ TEST_P(QuicSimpleServerSessionTest, CreateOutgoingDynamicStreamUnencrypted) { // Tests that outgoing stream creation fails when encryption has not yet been // established. size_t initial_num_open_stream = session_->GetNumOpenOutgoingStreams(); - EXPECT_DFATAL(QuicSimpleServerSessionPeer::CreateOutgoingDynamicStream( - session_.get(), kDefaultPriority), - "Encryption not established so no outgoing stream created."); + EXPECT_QUIC_BUG(QuicSimpleServerSessionPeer::CreateOutgoingDynamicStream( + session_.get(), kDefaultPriority), + "Encryption not established so no outgoing stream created."); EXPECT_EQ(initial_num_open_stream, session_->GetNumOpenOutgoingStreams()); } @@ -360,8 +358,9 @@ TEST_P(QuicSimpleServerSessionTest, CreateOutgoingDynamicStreamUptoLimit) { EXPECT_EQ(0u, session_->GetNumOpenOutgoingStreams()); // Assume encryption already established. - MockQuicCryptoServerStream* crypto_stream = new MockQuicCryptoServerStream( - &crypto_config_, &compressed_certs_cache_, session_.get()); + MockQuicCryptoServerStream* crypto_stream = + new MockQuicCryptoServerStream(&crypto_config_, &compressed_certs_cache_, + session_.get(), &stream_helper_); crypto_stream->set_encryption_established(true); QuicSimpleServerSessionPeer::SetCryptoStream(session_.get(), crypto_stream); @@ -434,9 +433,9 @@ class QuicSimpleServerSessionServerPushTest connection_ = new StrictMock<MockQuicConnectionWithSendStreamData>( &helper_, &alarm_factory_, Perspective::IS_SERVER, SupportedVersions(GetParam())); - session_.reset(new QuicSimpleServerSession( - config_, connection_, &owner_, &session_helper_, &crypto_config_, - &compressed_certs_cache_)); + session_.reset(new QuicSimpleServerSession(config_, connection_, &owner_, + &stream_helper_, &crypto_config_, + &compressed_certs_cache_)); session_->Initialize(); // Needed to make new session flow control window and server push work. session_->OnConfigNegotiated(); @@ -447,7 +446,9 @@ class QuicSimpleServerSessionServerPushTest // Assume encryption already established. MockQuicCryptoServerStream* crypto_stream = new MockQuicCryptoServerStream( - &crypto_config_, &compressed_certs_cache_, session_.get()); + &crypto_config_, &compressed_certs_cache_, session_.get(), + &stream_helper_); + crypto_stream->set_encryption_established(true); QuicSimpleServerSessionPeer::SetCryptoStream(session_.get(), crypto_stream); } @@ -483,9 +484,8 @@ class QuicSimpleServerSessionServerPushTest push_resources.push_back(QuicInMemoryCache::ServerPushInfo( resource_url, SpdyHeaderBlock(), kDefaultPriority, body)); // PUSH_PROMISED are sent for all the resources. - EXPECT_CALL( - *headers_stream_, - WritePushPromiseMock(kClientDataStreamId1, stream_id, _, nullptr)); + EXPECT_CALL(*headers_stream_, + WritePushPromiseMock(kClientDataStreamId1, stream_id, _)); if (i <= kMaxStreamsForTest) { // |kMaxStreamsForTest| promised responses should be sent. EXPECT_CALL( @@ -493,11 +493,25 @@ class QuicSimpleServerSessionServerPushTest WriteHeadersMock(stream_id, _, false, kDefaultPriority, nullptr)); // Since flow control window is smaller than response body, not the // whole body will be sent. - EXPECT_CALL(*connection_, - SendStreamData(stream_id, _, 0, false, nullptr)) - .WillOnce( - Return(QuicConsumedData(kStreamFlowControlWindowSize, false))); - EXPECT_CALL(*connection_, SendBlocked(stream_id)); + if (!session_->force_hol_blocking()) { + EXPECT_CALL(*connection_, + SendStreamData(stream_id, _, 0, false, nullptr)) + .WillOnce(Return( + QuicConsumedData(kStreamFlowControlWindowSize, false))); + EXPECT_CALL(*connection_, SendBlocked(stream_id)); + } else { + // The forced HOL blocking encapsulates the stream data into + // HTTP/2 DATA frames within the headers stream. HTTP/2 + // DATA frames are limited to a max size of 16KB, so the + // 64KB body will be fragemented into four DATA frames. + EXPECT_CALL(*connection_, SendStreamData(_, _, _, false, nullptr)) + .Times(body_size / 16384) + .WillOnce(Return(QuicConsumedData(9 + 16394, false))) + .WillOnce(Return(QuicConsumedData(9 + 16394, false))) + .WillOnce(Return(QuicConsumedData(9 + 16394, false))) + .WillOnce(Return(QuicConsumedData(9 + 16394, false))); + EXPECT_CALL(*connection_, SendBlocked(_)); + } } } session_->PromisePushResources(request_url, push_resources, @@ -507,12 +521,16 @@ class QuicSimpleServerSessionServerPushTest INSTANTIATE_TEST_CASE_P(Tests, QuicSimpleServerSessionServerPushTest, - ::testing::ValuesIn(QuicSupportedVersions())); + ::testing::ValuesIn(AllSupportedVersions())); TEST_P(QuicSimpleServerSessionServerPushTest, TestPromisePushResources) { // Tests that given more than kMaxOpenStreamForTest resources, all their // PUSH_PROMISE's will be sent out and only |kMaxOpenStreamForTest| streams // will be opened and send push response. + + if (session_->force_hol_blocking()) { + return; + } size_t num_resources = kMaxStreamsForTest + 5; PromisePushResources(num_resources); EXPECT_EQ(kMaxStreamsForTest, session_->GetNumOpenOutgoingStreams()); @@ -520,6 +538,10 @@ TEST_P(QuicSimpleServerSessionServerPushTest, TestPromisePushResources) { TEST_P(QuicSimpleServerSessionServerPushTest, HandlePromisedPushRequestsAfterStreamDraining) { + if (session_->force_hol_blocking()) { + return; + } + // Tests that after promised stream queued up, when an opened stream is marked // draining, a queued promised stream will become open and send push response. size_t num_resources = kMaxStreamsForTest + 1; @@ -543,6 +565,9 @@ TEST_P(QuicSimpleServerSessionServerPushTest, TEST_P(QuicSimpleServerSessionServerPushTest, ResetPromisedStreamToCancelServerPush) { + if (session_->force_hol_blocking()) { + return; + } // Tests that after all resources are promised, a RST frame from client can // prevent a promised resource to be send out. @@ -579,6 +604,9 @@ TEST_P(QuicSimpleServerSessionServerPushTest, TEST_P(QuicSimpleServerSessionServerPushTest, CloseStreamToHandleMorePromisedStream) { + if (session_->force_hol_blocking()) { + return; + } // Tests that closing a open outgoing stream can trigger a promised resource // in the queue to be send out. size_t num_resources = kMaxStreamsForTest + 1; diff --git a/chromium/net/tools/quic/quic_simple_server_stream.cc b/chromium/net/tools/quic/quic_simple_server_stream.cc index dda6a420d18..685955f3c61 100644 --- a/chromium/net/tools/quic/quic_simple_server_stream.cc +++ b/chromium/net/tools/quic/quic_simple_server_stream.cc @@ -12,10 +12,10 @@ #include "base/strings/string_number_conversions.h" #include "base/strings/string_piece.h" #include "base/strings/string_split.h" -#include "net/quic/quic_bug_tracker.h" -#include "net/quic/quic_flags.h" -#include "net/quic/quic_spdy_stream.h" -#include "net/quic/spdy_utils.h" +#include "net/quic/core/quic_bug_tracker.h" +#include "net/quic/core/quic_flags.h" +#include "net/quic/core/quic_spdy_stream.h" +#include "net/quic/core/spdy_utils.h" #include "net/spdy/spdy_protocol.h" #include "net/tools/quic/quic_in_memory_cache.h" #include "net/tools/quic/quic_simple_server_session.h" @@ -136,8 +136,8 @@ void QuicSimpleServerStream::PushResponse( } void QuicSimpleServerStream::SendResponse() { - if (!ContainsKey(request_headers_, ":authority") || - !ContainsKey(request_headers_, ":path")) { + if (!base::ContainsKey(request_headers_, ":authority") || + !base::ContainsKey(request_headers_, ":path")) { DVLOG(1) << "Request headers do not contain :authority or :path."; SendErrorResponse(); return; @@ -250,11 +250,13 @@ void QuicSimpleServerStream::SendHeadersAndBodyAndTrailers( return; } - // Send the body, with a FIN if there's nothing else to send. + // Send the body, with a FIN if there's no trailers to send. send_fin = response_trailers.empty(); DVLOG(1) << "Writing body (fin = " << send_fin << ") with size: " << body.size(); - WriteOrBufferData(body, send_fin, nullptr); + if (!body.empty() || send_fin) { + WriteOrBufferData(body, send_fin, nullptr); + } if (send_fin) { // Nothing else to send. return; diff --git a/chromium/net/tools/quic/quic_simple_server_stream.h b/chromium/net/tools/quic/quic_simple_server_stream.h index e270ed65f17..7217ad3aef6 100644 --- a/chromium/net/tools/quic/quic_simple_server_stream.h +++ b/chromium/net/tools/quic/quic_simple_server_stream.h @@ -10,8 +10,8 @@ #include <string> #include "base/macros.h" -#include "net/quic/quic_protocol.h" -#include "net/quic/quic_spdy_stream.h" +#include "net/quic/core/quic_protocol.h" +#include "net/quic/core/quic_spdy_stream.h" #include "net/spdy/spdy_framer.h" namespace net { diff --git a/chromium/net/tools/quic/quic_simple_server_stream_test.cc b/chromium/net/tools/quic/quic_simple_server_stream_test.cc index 5161c9f223c..eddc1a08b1c 100644 --- a/chromium/net/tools/quic/quic_simple_server_stream_test.cc +++ b/chromium/net/tools/quic/quic_simple_server_stream_test.cc @@ -8,11 +8,11 @@ #include "base/strings/string_number_conversions.h" #include "base/strings/string_piece.h" -#include "net/quic/quic_connection.h" -#include "net/quic/quic_flags.h" -#include "net/quic/quic_protocol.h" -#include "net/quic/quic_utils.h" -#include "net/quic/spdy_utils.h" +#include "net/quic/core/quic_connection.h" +#include "net/quic/core/quic_flags.h" +#include "net/quic/core/quic_protocol.h" +#include "net/quic/core/quic_utils.h" +#include "net/quic/core/spdy_utils.h" #include "net/quic/test_tools/crypto_test_utils.h" #include "net/quic/test_tools/quic_test_utils.h" #include "net/quic/test_tools/reliable_quic_stream_peer.h" @@ -87,7 +87,7 @@ class MockQuicSimpleServerSession : public QuicSimpleServerSession { explicit MockQuicSimpleServerSession( QuicConnection* connection, MockQuicServerSessionVisitor* owner, - MockQuicServerSessionHelper* helper, + MockQuicCryptoServerStreamHelper* helper, QuicCryptoServerConfig* crypto_config, QuicCompressedCertsCache* compressed_certs_cache) : QuicSimpleServerSession(DefaultQuicConfig(), @@ -190,7 +190,6 @@ class QuicSimpleServerStreamTest crypto_config_.get(), &compressed_certs_cache_), body_("hello world") { - FLAGS_quic_always_log_bugs_for_tests = true; SpdyHeaderBlock request_headers; request_headers[":host"] = ""; request_headers[":authority"] = "www.google.com"; @@ -233,7 +232,7 @@ class QuicSimpleServerStreamTest MockAlarmFactory alarm_factory_; StrictMock<MockQuicConnection>* connection_; StrictMock<MockQuicServerSessionVisitor> session_owner_; - StrictMock<MockQuicServerSessionHelper> session_helper_; + StrictMock<MockQuicCryptoServerStreamHelper> session_helper_; std::unique_ptr<QuicCryptoServerConfig> crypto_config_; QuicCompressedCertsCache compressed_certs_cache_; StrictMock<MockQuicSimpleServerSession> session_; @@ -244,7 +243,7 @@ class QuicSimpleServerStreamTest INSTANTIATE_TEST_CASE_P(Tests, QuicSimpleServerStreamTest, - ::testing::ValuesIn(QuicSupportedVersions())); + ::testing::ValuesIn(AllSupportedVersions())); TEST_P(QuicSimpleServerStreamTest, TestFraming) { EXPECT_CALL(session_, WritevData(_, _, _, _, _, _)) @@ -286,11 +285,7 @@ TEST_P(QuicSimpleServerStreamTest, SendQuicRstStreamNoErrorInStopReading) { stream_->set_fin_sent(true); stream_->CloseWriteSide(); - if (GetParam() > QUIC_VERSION_28) { - EXPECT_CALL(session_, SendRstStream(_, QUIC_STREAM_NO_ERROR, _)).Times(1); - } else { - EXPECT_CALL(session_, SendRstStream(_, QUIC_STREAM_NO_ERROR, _)).Times(0); - } + EXPECT_CALL(session_, SendRstStream(_, QUIC_STREAM_NO_ERROR, _)).Times(1); stream_->StopReading(); } @@ -472,9 +467,9 @@ TEST_P(QuicSimpleServerStreamTest, SendReponseWithPushResources) { TEST_P(QuicSimpleServerStreamTest, PushResponseOnClientInitiatedStream) { // Calling PushResponse() on a client initialted stream is never supposed to // happen. - EXPECT_DFATAL(stream_->PushResponse(SpdyHeaderBlock()), - "Client initiated stream" - " shouldn't be used as promised stream."); + EXPECT_QUIC_BUG(stream_->PushResponse(SpdyHeaderBlock()), + "Client initiated stream" + " shouldn't be used as promised stream."); } TEST_P(QuicSimpleServerStreamTest, PushResponseOnServerInitiatedStream) { @@ -601,11 +596,7 @@ TEST_P(QuicSimpleServerStreamTest, SendQuicRstStreamNoErrorWithEarlyResponse) { EXPECT_CALL(session_, WritevData(_, _, _, _, _, _)) .Times(1) .WillOnce(Return(QuicConsumedData(3, true))); - if (GetParam() > QUIC_VERSION_28) { - EXPECT_CALL(session_, SendRstStream(_, QUIC_STREAM_NO_ERROR, _)).Times(1); - } else { - EXPECT_CALL(session_, SendRstStream(_, QUIC_STREAM_NO_ERROR, _)).Times(0); - } + EXPECT_CALL(session_, SendRstStream(_, QUIC_STREAM_NO_ERROR, _)).Times(1); EXPECT_FALSE(stream_->fin_received()); QuicSimpleServerStreamPeer::SendErrorResponse(stream_); EXPECT_TRUE(stream_->reading_stopped()); diff --git a/chromium/net/tools/quic/quic_simple_server_test.cc b/chromium/net/tools/quic/quic_simple_server_test.cc index 2bf39601b99..091c08761f5 100644 --- a/chromium/net/tools/quic/quic_simple_server_test.cc +++ b/chromium/net/tools/quic/quic_simple_server_test.cc @@ -4,8 +4,9 @@ #include "net/tools/quic/quic_simple_server.h" -#include "net/quic/crypto/quic_random.h" -#include "net/quic/quic_utils.h" +#include "net/quic/core/crypto/quic_random.h" +#include "net/quic/core/quic_crypto_stream.h" +#include "net/quic/core/quic_utils.h" #include "net/quic/test_tools/crypto_test_utils.h" #include "net/quic/test_tools/mock_quic_dispatcher.h" #include "net/quic/test_tools/quic_test_utils.h" @@ -25,12 +26,14 @@ class QuicChromeServerDispatchPacketTest : public ::testing::Test { : crypto_config_("blah", QuicRandom::GetInstance(), CryptoTestUtils::ProofSourceForTesting()), + version_manager_(AllSupportedVersions()), dispatcher_( config_, &crypto_config_, + &version_manager_, std::unique_ptr<MockQuicConnectionHelper>( new net::test::MockQuicConnectionHelper), - std::unique_ptr<QuicServerSessionBase::Helper>( + std::unique_ptr<QuicCryptoServerStream::Helper>( new QuicSimpleServerSessionHelper(QuicRandom::GetInstance())), std::unique_ptr<MockAlarmFactory>( new net::test::MockAlarmFactory)) { @@ -45,6 +48,7 @@ class QuicChromeServerDispatchPacketTest : public ::testing::Test { protected: QuicConfig config_; QuicCryptoServerConfig crypto_config_; + QuicVersionManager version_manager_; net::test::MockQuicDispatcher dispatcher_; }; diff --git a/chromium/net/tools/quic/quic_socket_utils.cc b/chromium/net/tools/quic/quic_socket_utils.cc index 61ec3783511..a6bc4c0c496 100644 --- a/chromium/net/tools/quic/quic_socket_utils.cc +++ b/chromium/net/tools/quic/quic_socket_utils.cc @@ -13,9 +13,9 @@ #include <string> #include "base/logging.h" -#include "net/quic/quic_bug_tracker.h" -#include "net/quic/quic_flags.h" -#include "net/quic/quic_protocol.h" +#include "net/quic/core/quic_bug_tracker.h" +#include "net/quic/core/quic_flags.h" +#include "net/quic/core/quic_protocol.h" #ifndef SO_RXQ_OVFL #define SO_RXQ_OVFL 40 @@ -27,9 +27,7 @@ namespace net { void QuicSocketUtils::GetAddressAndTimestampFromMsghdr( struct msghdr* hdr, IPAddress* address, - QuicTime* timestamp, - QuicWallTime* walltimestamp, - bool latched_walltimestamps) { + QuicWallTime* walltimestamp) { if (hdr->msg_controllen > 0) { for (cmsghdr* cmsg = CMSG_FIRSTHDR(hdr); cmsg != nullptr; cmsg = CMSG_NXTHDR(hdr, cmsg)) { @@ -52,12 +50,7 @@ void QuicSocketUtils::GetAddressAndTimestampFromMsghdr( timespec* ts = <s->systime; int64_t usec = (static_cast<int64_t>(ts->tv_sec) * 1000 * 1000) + (static_cast<int64_t>(ts->tv_nsec) / 1000); - if (latched_walltimestamps) { - *walltimestamp = QuicWallTime::FromUNIXMicroseconds(usec); - } else { - *timestamp = - QuicTime::Zero().Add(QuicTime::Delta::FromMicroseconds(usec)); - } + *walltimestamp = QuicWallTime::FromUNIXMicroseconds(usec); } } } @@ -71,7 +64,24 @@ bool QuicSocketUtils::GetOverflowFromMsghdr(struct msghdr* hdr, for (cmsg = CMSG_FIRSTHDR(hdr); cmsg != nullptr; cmsg = CMSG_NXTHDR(hdr, cmsg)) { if (cmsg->cmsg_type == SO_RXQ_OVFL) { - *dropped_packets = *(reinterpret_cast<int*> CMSG_DATA(cmsg)); + *dropped_packets = *(reinterpret_cast<uint32_t*> CMSG_DATA(cmsg)); + return true; + } + } + } + return false; +} + +// static +bool QuicSocketUtils::GetTtlFromMsghdr(struct msghdr* hdr, int* ttl) { + if (hdr->msg_controllen > 0) { + struct cmsghdr* cmsg; + for (cmsg = CMSG_FIRSTHDR(hdr); cmsg != nullptr; + cmsg = CMSG_NXTHDR(hdr, cmsg)) { + if ((cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_TTL) || + (cmsg->cmsg_level == IPPROTO_IPV6 && + cmsg->cmsg_type == IPV6_HOPLIMIT)) { + *ttl = *(reinterpret_cast<int*>(CMSG_DATA(cmsg))); return true; } } @@ -122,9 +132,7 @@ int QuicSocketUtils::ReadPacket(int fd, size_t buf_len, QuicPacketCount* dropped_packets, IPAddress* self_address, - QuicTime* timestamp, QuicWallTime* walltimestamp, - bool latched_walltimestamps, IPEndPoint* peer_address) { DCHECK(peer_address != nullptr); char cbuf[kSpaceForCmsg]; @@ -176,13 +184,7 @@ int QuicSocketUtils::ReadPacket(int fd, walltimestamp = &stack_walltimestamp; } - QuicTime stack_timestamp = QuicTime::Zero(); - if (timestamp == nullptr) { - timestamp = &stack_timestamp; - } - - GetAddressAndTimestampFromMsghdr(&hdr, self_address, timestamp, walltimestamp, - latched_walltimestamps); + GetAddressAndTimestampFromMsghdr(&hdr, self_address, walltimestamp); if (raw_address.ss_family == AF_INET) { CHECK(peer_address->FromSockAddr( diff --git a/chromium/net/tools/quic/quic_socket_utils.h b/chromium/net/tools/quic/quic_socket_utils.h index 0bad8381969..f8cc5392c5f 100644 --- a/chromium/net/tools/quic/quic_socket_utils.h +++ b/chromium/net/tools/quic/quic_socket_utils.h @@ -16,8 +16,8 @@ #include "base/macros.h" #include "net/base/ip_address.h" #include "net/base/ip_endpoint.h" -#include "net/quic/quic_bandwidth.h" -#include "net/quic/quic_types.h" +#include "net/quic/core/quic_bandwidth.h" +#include "net/quic/core/quic_types.h" namespace net { @@ -38,30 +38,31 @@ class QuicSocketUtils { public: // The first integer is for overflow. The in6_pktinfo is the larger of the // address structures present. LinuxTimestamping is present for socket - // timestamping. + // timestamping. The subsequent int is for ttl. // The final int is a sentinel so the msg_controllen feedback // can be used to detect larger control messages than there is space for. static const int kSpaceForCmsg = CMSG_SPACE(CMSG_LEN(sizeof(int)) + CMSG_LEN(sizeof(in6_pktinfo)) + CMSG_LEN(sizeof(LinuxTimestamping)) + + CMSG_LEN(sizeof(int)) + CMSG_LEN(sizeof(int))); // Fills in |address| if |hdr| contains IP_PKTINFO or IPV6_PKTINFO. Fills in // |timestamp| if |hdr| contains |SO_TIMESTAMPING|. |address| and |timestamp| // must not be null. - // TODO(rjshade): Delete the |timestamp| argument when removing - // FLAGS_quic_socket_timestamps_walltime static void GetAddressAndTimestampFromMsghdr(struct msghdr* hdr, IPAddress* address, - QuicTime* timestamp, - QuicWallTime* walltimestamp, - bool latched_walltimestamps); + QuicWallTime* walltimestamp); // If the msghdr contains an SO_RXQ_OVFL entry, this will set dropped_packets // to the correct value and return true. Otherwise it will return false. static bool GetOverflowFromMsghdr(struct msghdr* hdr, QuicPacketCount* dropped_packets); + // If the msghdr contains an IP_TTL entry, this will set ttl to the correct + // value and return true. Otherwise it will return false. + static bool GetTtlFromMsghdr(struct msghdr* hdr, int* ttl); + // Sets either IP_PKTINFO or IPV6_PKTINFO on the socket, based on // address_family. Returns the return code from setsockopt. static int SetGetAddressInfo(int fd, int address_family); @@ -90,16 +91,12 @@ class QuicSocketUtils { // received packet, assuming a packet was read and the platform supports // packet receipt timestamping. If the platform does not support packet // receipt timestamping, timestamp will not be changed. - // TODO(rjshade): Delete the |timestamp| argument when removing - // FLAGS_quic_socket_timestamps_walltime static int ReadPacket(int fd, char* buffer, size_t buf_len, QuicPacketCount* dropped_packets, IPAddress* self_address, - QuicTime* timestamp, QuicWallTime* walltimestamp, - bool latched_walltimestamps, IPEndPoint* peer_address); // Writes buf_len to the socket. If writing is successful, sets the result's diff --git a/chromium/net/tools/quic/quic_spdy_client_stream.cc b/chromium/net/tools/quic/quic_spdy_client_stream.cc index 521c7b66e1f..253571ea39f 100644 --- a/chromium/net/tools/quic/quic_spdy_client_stream.cc +++ b/chromium/net/tools/quic/quic_spdy_client_stream.cc @@ -9,9 +9,9 @@ #include "base/logging.h" #include "base/stl_util.h" #include "base/strings/string_number_conversions.h" -#include "net/quic/quic_alarm.h" -#include "net/quic/quic_client_promised_info.h" -#include "net/quic/spdy_utils.h" +#include "net/quic/core/quic_alarm.h" +#include "net/quic/core/quic_client_promised_info.h" +#include "net/quic/core/spdy_utils.h" #include "net/spdy/spdy_protocol.h" #include "net/tools/quic/quic_client_session.h" #include "net/tools/quic/spdy_balsa_utils.h" @@ -103,7 +103,7 @@ void QuicSpdyClientStream::OnTrailingHeadersComplete( size_t frame_len, const QuicHeaderList& header_list) { QuicSpdyStream::OnTrailingHeadersComplete(fin, frame_len, header_list); - MarkTrailersConsumed(decompressed_trailers().length()); + MarkTrailersConsumed(); } void QuicSpdyClientStream::OnPromiseHeadersComplete(QuicStreamId promised_id, @@ -149,12 +149,10 @@ void QuicSpdyClientStream::OnPromiseHeaderList( } void QuicSpdyClientStream::OnDataAvailable() { - if (FLAGS_quic_supports_push_promise) { - // For push streams, visitor will not be set until the rendezvous - // between server promise and client request is complete. - if (visitor() == nullptr) - return; - } + // For push streams, visitor will not be set until the rendezvous + // between server promise and client request is complete. + if (visitor() == nullptr) + return; while (HasBytesToRead()) { struct iovec iov; diff --git a/chromium/net/tools/quic/quic_spdy_client_stream.h b/chromium/net/tools/quic/quic_spdy_client_stream.h index 04eb4932620..96ce11d086a 100644 --- a/chromium/net/tools/quic/quic_spdy_client_stream.h +++ b/chromium/net/tools/quic/quic_spdy_client_stream.h @@ -11,8 +11,8 @@ #include "base/macros.h" #include "base/strings/string_piece.h" -#include "net/quic/quic_protocol.h" -#include "net/quic/quic_spdy_stream.h" +#include "net/quic/core/quic_protocol.h" +#include "net/quic/core/quic_spdy_stream.h" #include "net/spdy/spdy_framer.h" namespace net { diff --git a/chromium/net/tools/quic/quic_spdy_client_stream_test.cc b/chromium/net/tools/quic/quic_spdy_client_stream_test.cc index af3cb3c8cde..20f524a2ba1 100644 --- a/chromium/net/tools/quic/quic_spdy_client_stream_test.cc +++ b/chromium/net/tools/quic/quic_spdy_client_stream_test.cc @@ -8,8 +8,8 @@ #include "base/macros.h" #include "base/strings/string_number_conversions.h" -#include "net/quic/quic_utils.h" -#include "net/quic/spdy_utils.h" +#include "net/quic/core/quic_utils.h" +#include "net/quic/core/spdy_utils.h" #include "net/quic/test_tools/crypto_test_utils.h" #include "net/quic/test_tools/quic_test_utils.h" #include "net/tools/quic/quic_client_session.h" @@ -26,7 +26,6 @@ using net::test::kClientDataStreamId1; using net::test::kServerDataStreamId1; using net::test::kInitialSessionFlowControlWindowForTest; using net::test::kInitialStreamFlowControlWindowForTest; -using net::test::ValueRestore; using std::string; using testing::StrictMock; diff --git a/chromium/net/tools/quic/quic_time_wait_list_manager.cc b/chromium/net/tools/quic/quic_time_wait_list_manager.cc index 6686238a03c..4f99cb02fb9 100644 --- a/chromium/net/tools/quic/quic_time_wait_list_manager.cc +++ b/chromium/net/tools/quic/quic_time_wait_list_manager.cc @@ -12,15 +12,15 @@ #include "base/macros.h" #include "base/stl_util.h" #include "net/base/ip_endpoint.h" -#include "net/quic/crypto/crypto_protocol.h" -#include "net/quic/crypto/quic_decrypter.h" -#include "net/quic/crypto/quic_encrypter.h" -#include "net/quic/quic_clock.h" -#include "net/quic/quic_flags.h" -#include "net/quic/quic_framer.h" -#include "net/quic/quic_protocol.h" -#include "net/quic/quic_server_session_base.h" -#include "net/quic/quic_utils.h" +#include "net/quic/core/crypto/crypto_protocol.h" +#include "net/quic/core/crypto/quic_decrypter.h" +#include "net/quic/core/crypto/quic_encrypter.h" +#include "net/quic/core/quic_clock.h" +#include "net/quic/core/quic_flags.h" +#include "net/quic/core/quic_framer.h" +#include "net/quic/core/quic_protocol.h" +#include "net/quic/core/quic_server_session_base.h" +#include "net/quic/core/quic_utils.h" using base::StringPiece; @@ -91,7 +91,7 @@ QuicTimeWaitListManager::QuicTimeWaitListManager( QuicTimeWaitListManager::~QuicTimeWaitListManager() { connection_id_clean_up_alarm_->Cancel(); - STLDeleteElements(&pending_packets_queue_); + base::STLDeleteElements(&pending_packets_queue_); } void QuicTimeWaitListManager::AddConnectionIdToTimeWait( @@ -127,7 +127,7 @@ void QuicTimeWaitListManager::AddConnectionIdToTimeWait( bool QuicTimeWaitListManager::IsConnectionIdInTimeWait( QuicConnectionId connection_id) const { - return ContainsKey(connection_id_map_, connection_id); + return base::ContainsKey(connection_id_map_, connection_id); } QuicVersion QuicTimeWaitListManager::GetQuicVersionFromConnectionId( @@ -261,15 +261,13 @@ bool QuicTimeWaitListManager::WriteToWire(QueuedPacket* queued_packet) { } void QuicTimeWaitListManager::SetConnectionIdCleanUpAlarm() { - connection_id_clean_up_alarm_->Cancel(); QuicTime::Delta next_alarm_interval = QuicTime::Delta::Zero(); if (!connection_id_map_.empty()) { QuicTime oldest_connection_id = connection_id_map_.begin()->second.time_added; QuicTime now = clock_->ApproximateNow(); - if (now.Subtract(oldest_connection_id) < time_wait_period_) { - next_alarm_interval = - oldest_connection_id.Add(time_wait_period_).Subtract(now); + if (now - oldest_connection_id < time_wait_period_) { + next_alarm_interval = oldest_connection_id + time_wait_period_ - now; } else { LOG(ERROR) << "ConnectionId lingered for longer than time_wait_period_"; } @@ -278,8 +276,8 @@ void QuicTimeWaitListManager::SetConnectionIdCleanUpAlarm() { next_alarm_interval = time_wait_period_; } - connection_id_clean_up_alarm_->Set( - clock_->ApproximateNow().Add(next_alarm_interval)); + connection_id_clean_up_alarm_->Update( + clock_->ApproximateNow() + next_alarm_interval, QuicTime::Delta::Zero()); } bool QuicTimeWaitListManager::MaybeExpireOldestConnection( @@ -300,7 +298,7 @@ bool QuicTimeWaitListManager::MaybeExpireOldestConnection( void QuicTimeWaitListManager::CleanUpOldConnectionIds() { QuicTime now = clock_->ApproximateNow(); - QuicTime expiration = now.Subtract(time_wait_period_); + QuicTime expiration = now - time_wait_period_; while (MaybeExpireOldestConnection(expiration)) { } diff --git a/chromium/net/tools/quic/quic_time_wait_list_manager.h b/chromium/net/tools/quic/quic_time_wait_list_manager.h index 136ee4bc650..52daa92a31b 100644 --- a/chromium/net/tools/quic/quic_time_wait_list_manager.h +++ b/chromium/net/tools/quic/quic_time_wait_list_manager.h @@ -16,16 +16,17 @@ #include "base/macros.h" #include "net/base/linked_hash_map.h" -#include "net/quic/quic_blocked_writer_interface.h" -#include "net/quic/quic_connection.h" -#include "net/quic/quic_framer.h" -#include "net/quic/quic_packet_writer.h" -#include "net/quic/quic_protocol.h" -#include "net/quic/quic_server_session_base.h" +#include "net/quic/core/quic_blocked_writer_interface.h" +#include "net/quic/core/quic_connection.h" +#include "net/quic/core/quic_framer.h" +#include "net/quic/core/quic_packet_writer.h" +#include "net/quic/core/quic_protocol.h" +#include "net/quic/core/quic_server_session_base.h" namespace net { namespace test { +class QuicDispatcherPeer; class QuicTimeWaitListManagerPeer; } // namespace test @@ -113,6 +114,7 @@ class QuicTimeWaitListManager : public QuicBlockedWriterInterface { const QuicPublicResetPacket& packet); private: + friend class test::QuicDispatcherPeer; friend class test::QuicTimeWaitListManagerPeer; // Internal structure to store pending public reset packets. diff --git a/chromium/net/tools/quic/quic_time_wait_list_manager_test.cc b/chromium/net/tools/quic/quic_time_wait_list_manager_test.cc index a1d06b4c4fd..54b5ea809d4 100644 --- a/chromium/net/tools/quic/quic_time_wait_list_manager_test.cc +++ b/chromium/net/tools/quic/quic_time_wait_list_manager_test.cc @@ -7,17 +7,16 @@ #include <errno.h> #include <memory> -#include "net/quic/crypto/crypto_protocol.h" -#include "net/quic/crypto/null_encrypter.h" -#include "net/quic/crypto/quic_decrypter.h" -#include "net/quic/crypto/quic_encrypter.h" -#include "net/quic/quic_chromium_connection_helper.h" -#include "net/quic/quic_data_reader.h" -#include "net/quic/quic_flags.h" -#include "net/quic/quic_framer.h" -#include "net/quic/quic_packet_writer.h" -#include "net/quic/quic_protocol.h" -#include "net/quic/quic_utils.h" +#include "net/quic/core/crypto/crypto_protocol.h" +#include "net/quic/core/crypto/null_encrypter.h" +#include "net/quic/core/crypto/quic_decrypter.h" +#include "net/quic/core/crypto/quic_encrypter.h" +#include "net/quic/core/quic_data_reader.h" +#include "net/quic/core/quic_flags.h" +#include "net/quic/core/quic_framer.h" +#include "net/quic/core/quic_packet_writer.h" +#include "net/quic/core/quic_protocol.h" +#include "net/quic/core/quic_utils.h" #include "net/quic/test_tools/quic_test_utils.h" #include "net/tools/quic/quic_epoll_alarm_factory.h" #include "net/tools/quic/quic_epoll_connection_helper.h" @@ -31,7 +30,6 @@ using net::test::BuildUnsizedDataPacket; using net::test::NoOpFramerVisitor; using net::test::QuicVersionMax; using net::test::QuicVersionMin; -using net::test::ValueRestore; using net::test::MockPacketWriter; using testing::Args; @@ -154,6 +152,7 @@ class QuicTimeWaitListManagerTest : public ::testing::Test { packet_number, "data"); } + QuicFlagSaver flags_; // Save/restore all QUIC flag values. NiceMock<MockFakeTimeEpollServer> epoll_server_; QuicEpollConnectionHelper helper_; QuicEpollAlarmFactory alarm_factory_; @@ -177,7 +176,7 @@ class ValidatePublicResetPacketPredicate const std::tr1::tuple<const char*, int> packet_buffer, testing::MatchResultListener* /* listener */) const override { FramerVisitorCapturingPublicReset visitor; - QuicFramer framer(QuicSupportedVersions(), QuicTime::Zero(), + QuicFramer framer(AllSupportedVersions(), QuicTime::Zero(), Perspective::IS_CLIENT); framer.set_visitor(&visitor); QuicEncryptedPacket encrypted(std::tr1::get<0>(packet_buffer), @@ -227,15 +226,14 @@ TEST_F(QuicTimeWaitListManagerTest, CheckStatelessConnectionIdInTimeWait) { TEST_F(QuicTimeWaitListManagerTest, SendVersionNegotiationPacket) { std::unique_ptr<QuicEncryptedPacket> packet( QuicFramer::BuildVersionNegotiationPacket(connection_id_, - QuicSupportedVersions())); + AllSupportedVersions())); EXPECT_CALL(writer_, WritePacket(_, packet->length(), server_address_.address(), client_address_, _)) .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 1))); time_wait_list_manager_.SendVersionNegotiationPacket( - connection_id_, QuicSupportedVersions(), server_address_, - client_address_); + connection_id_, AllSupportedVersions(), server_address_, client_address_); EXPECT_EQ(0u, time_wait_list_manager_.num_connections()); } @@ -353,11 +351,11 @@ TEST_F(QuicTimeWaitListManagerTest, CleanUpOldConnectionIds) { QuicTime::Delta offset = QuicTime::Delta::FromMicroseconds(39); // Now set the current time as time_wait_period + offset usecs. - epoll_server_.set_now_in_usec(time_wait_period.Add(offset).ToMicroseconds()); + epoll_server_.set_now_in_usec((time_wait_period + offset).ToMicroseconds()); // After all the old connection_ids are cleaned up, check the next alarm // interval. int64_t next_alarm_time = epoll_server_.ApproximateNowInUsec() + - time_wait_period.Subtract(offset).ToMicroseconds(); + (time_wait_period - offset).ToMicroseconds(); EXPECT_CALL(epoll_server_, RegisterAlarm(next_alarm_time, _)); time_wait_list_manager_.CleanUpOldConnectionIds(); @@ -479,7 +477,7 @@ TEST_F(QuicTimeWaitListManagerTest, AddConnectionIdTwice) { QuicTime::Delta offset = QuicTime::Delta::FromMicroseconds(39); // Now set the current time as time_wait_period + offset usecs. - epoll_server_.set_now_in_usec(time_wait_period.Add(offset).ToMicroseconds()); + epoll_server_.set_now_in_usec((time_wait_period + offset).ToMicroseconds()); // After the connection_ids are cleaned up, check the next alarm interval. int64_t next_alarm_time = epoll_server_.ApproximateNowInUsec() + time_wait_period.ToMicroseconds(); diff --git a/chromium/net/tools/quic/spdy_balsa_utils.cc b/chromium/net/tools/quic/spdy_balsa_utils.cc index 8f1ebd2eac4..b5764208990 100644 --- a/chromium/net/tools/quic/spdy_balsa_utils.cc +++ b/chromium/net/tools/quic/spdy_balsa_utils.cc @@ -12,8 +12,8 @@ #include "base/strings/string_split.h" #include "base/strings/string_util.h" #include "net/base/linked_hash_map.h" -#include "net/quic/quic_flags.h" -#include "net/quic/spdy_utils.h" +#include "net/quic/core/quic_flags.h" +#include "net/quic/core/spdy_utils.h" #include "net/spdy/spdy_frame_builder.h" #include "net/spdy/spdy_framer.h" #include "net/spdy/spdy_protocol.h" @@ -189,25 +189,29 @@ void SpdyHeadersToRequestHeaders(const SpdyHeaderBlock& header_block, method = method_it->second.as_string(); } string uri; - if (path_it == end_it) { - uri = "/"; - } else { - uri = path_it->second.as_string(); - } - request_headers->SetRequestFirstlineFromStringPieces( - method, uri, net::kHttp2VersionString); - if (scheme_it == end_it) { - request_headers->AppendHeader("Scheme", "https"); + uri += "https"; } else { - request_headers->AppendHeader("Scheme", scheme_it->second); + uri += scheme_it->second.as_string(); } + uri += "://"; + if (authority_it != end_it) { + uri += authority_it->second.as_string(); request_headers->AppendHeader("host", authority_it->second); } else if (host_it != end_it) { + uri += host_it->second.as_string(); request_headers->AppendHeader("host", host_it->second); } + if (path_it == end_it) { + uri += "/"; + } else { + uri += path_it->second.as_string(); + } + request_headers->SetRequestFirstlineFromStringPieces( + method, uri, net::kHttp2VersionString); + for (BlockIt it = header_block.begin(); it != header_block.end(); ++it) { if (!IsSpecialSpdyHeader(it, request_headers)) { if (it->second.empty()) { diff --git a/chromium/net/tools/quic/spdy_balsa_utils.h b/chromium/net/tools/quic/spdy_balsa_utils.h index a1d555a642b..dadb0eca479 100644 --- a/chromium/net/tools/quic/spdy_balsa_utils.h +++ b/chromium/net/tools/quic/spdy_balsa_utils.h @@ -8,7 +8,7 @@ #include <string> #include "base/macros.h" -#include "net/quic/quic_protocol.h" +#include "net/quic/core/quic_protocol.h" #include "net/spdy/spdy_framer.h" #include "net/spdy/spdy_header_block.h" #include "net/spdy/spdy_protocol.h" diff --git a/chromium/net/tools/quic/spdy_balsa_utils_test.cc b/chromium/net/tools/quic/spdy_balsa_utils_test.cc index c8591ea0df9..bf26667be27 100644 --- a/chromium/net/tools/quic/spdy_balsa_utils_test.cc +++ b/chromium/net/tools/quic/spdy_balsa_utils_test.cc @@ -58,7 +58,7 @@ TEST(SpdyBalsaUtilsTest, SpdyHeadersToRequestHeaders) { SpdyBalsaUtils::SpdyHeadersToRequestHeaders(spdy_headers, &request_headers); EXPECT_EQ("GET", request_headers.request_method()); EXPECT_EQ("HTTP/1.1", request_headers.request_version()); - EXPECT_EQ("/foo", request_headers.request_uri()); + EXPECT_EQ("https://www.google.com/foo", request_headers.request_uri()); EXPECT_EQ("www.google.com", request_headers.GetHeader("host")); EXPECT_TRUE(request_headers.HasHeader("bar")); EXPECT_EQ("", request_headers.GetHeader("bar")); @@ -76,7 +76,7 @@ TEST(SpdyBalsaUtilsTest, SpdyHeadersToRequestHeaders) { SpdyBalsaUtils::SpdyHeadersToRequestHeaders(spdy_headers1, &request_headers1); EXPECT_EQ("GET", request_headers1.request_method()); EXPECT_EQ("HTTP/1.1", request_headers1.request_version()); - EXPECT_EQ("/foo", request_headers1.request_uri()); + EXPECT_EQ("http://www.google.com/foo", request_headers1.request_uri()); EXPECT_EQ("www.google.com", request_headers1.GetHeader("host")); } diff --git a/chromium/net/tools/quic/stateless_rejector.cc b/chromium/net/tools/quic/stateless_rejector.cc index 0fbbedd88c4..6cf95304d90 100644 --- a/chromium/net/tools/quic/stateless_rejector.cc +++ b/chromium/net/tools/quic/stateless_rejector.cc @@ -4,26 +4,33 @@ #include "net/tools/quic/stateless_rejector.h" -#include "net/quic/quic_crypto_server_stream.h" -#include "net/quic/quic_flags.h" +#include "net/quic/core/quic_bug_tracker.h" +#include "net/quic/core/quic_crypto_server_stream.h" +#include "net/quic/core/quic_flags.h" namespace net { class StatelessRejector::ValidateCallback : public ValidateClientHelloResultCallback { public: - explicit ValidateCallback(StatelessRejector* rejector) - : rejector_(rejector) {} + explicit ValidateCallback( + std::unique_ptr<StatelessRejector> rejector, + std::unique_ptr<StatelessRejector::ProcessDoneCallback> cb) + : rejector_(std::move(rejector)), cb_(std::move(cb)) {} ~ValidateCallback() override {} - void RunImpl(const CryptoHandshakeMessage& client_hello, - const Result& result) override { - rejector_->ProcessClientHello(client_hello, result); + void Run(scoped_refptr<Result> result, + std::unique_ptr<ProofSource::Details> /* proof_source_details */) + override { + StatelessRejector* rejector_ptr = rejector_.get(); + rejector_ptr->ProcessClientHello(std::move(result), std::move(rejector_), + std::move(cb_)); } private: - StatelessRejector* rejector_; + std::unique_ptr<StatelessRejector> rejector_; + std::unique_ptr<StatelessRejector::ProcessDoneCallback> cb_; }; StatelessRejector::StatelessRejector( @@ -33,20 +40,21 @@ StatelessRejector::StatelessRejector( QuicCompressedCertsCache* compressed_certs_cache, const QuicClock* clock, QuicRandom* random, + QuicByteCount chlo_packet_size, const IPEndPoint& client_address, const IPEndPoint& server_address) - : state_(FAILED), + : state_(UNKNOWN), error_(QUIC_INTERNAL_ERROR), version_(version), versions_(versions), connection_id_(0), + chlo_packet_size_(chlo_packet_size), client_address_(client_address), server_address_(server_address), clock_(clock), random_(random), crypto_config_(crypto_config), - compressed_certs_cache_(compressed_certs_cache), - chlo_(nullptr) {} + compressed_certs_cache_(compressed_certs_cache) {} StatelessRejector::~StatelessRejector() {} @@ -56,6 +64,7 @@ void StatelessRejector::OnChlo(QuicVersion version, const CryptoHandshakeMessage& message) { DCHECK_EQ(kCHLO, message.tag()); DCHECK_NE(connection_id, server_designated_connection_id); + DCHECK_EQ(state_, UNKNOWN); if (!FLAGS_enable_quic_stateless_reject_support || !FLAGS_quic_use_cheap_stateless_rejects || @@ -67,36 +76,85 @@ void StatelessRejector::OnChlo(QuicVersion version, connection_id_ = connection_id; server_designated_connection_id_ = server_designated_connection_id; - chlo_ = &message; + chlo_ = message; // Note: copies the message +} - crypto_config_->ValidateClientHello( - message, client_address_.address(), server_address_.address(), version_, - clock_, &proof_, new ValidateCallback(this)); +void StatelessRejector::Process(std::unique_ptr<StatelessRejector> rejector, + std::unique_ptr<ProcessDoneCallback> done_cb) { + // If we were able to make a decision about this CHLO based purely on the + // information available in OnChlo, just invoke the done callback immediately. + if (rejector->state() != UNKNOWN) { + done_cb->Run(std::move(rejector)); + return; + } + + StatelessRejector* rejector_ptr = rejector.get(); + rejector_ptr->crypto_config_->ValidateClientHello( + rejector_ptr->chlo_, rejector_ptr->client_address_.address(), + rejector_ptr->server_address_.address(), rejector_ptr->version_, + rejector_ptr->clock_, &rejector_ptr->proof_, + std::unique_ptr<ValidateCallback>( + new ValidateCallback(std::move(rejector), std::move(done_cb)))); } +class StatelessRejector::ProcessClientHelloCallback + : public ProcessClientHelloResultCallback { + public: + ProcessClientHelloCallback( + std::unique_ptr<StatelessRejector> rejector, + std::unique_ptr<StatelessRejector::ProcessDoneCallback> done_cb) + : rejector_(std::move(rejector)), done_cb_(std::move(done_cb)) {} + + void Run( + QuicErrorCode error, + const std::string& error_details, + std::unique_ptr<CryptoHandshakeMessage> message, + std::unique_ptr<DiversificationNonce> diversification_nonce) override { + StatelessRejector* rejector_ptr = rejector_.get(); + rejector_ptr->ProcessClientHelloDone( + error, error_details, std::move(message), std::move(rejector_), + std::move(done_cb_)); + } + + private: + std::unique_ptr<StatelessRejector> rejector_; + std::unique_ptr<StatelessRejector::ProcessDoneCallback> done_cb_; +}; + void StatelessRejector::ProcessClientHello( - const CryptoHandshakeMessage& client_hello, - const ValidateClientHelloResultCallback::Result& result) { - QuicCryptoNegotiatedParameters params; - DiversificationNonce diversification_nonce; - QuicErrorCode error = crypto_config_->ProcessClientHello( + scoped_refptr<ValidateClientHelloResultCallback::Result> result, + std::unique_ptr<StatelessRejector> rejector, + std::unique_ptr<StatelessRejector::ProcessDoneCallback> done_cb) { + std::unique_ptr<ProcessClientHelloCallback> cb( + new ProcessClientHelloCallback(std::move(rejector), std::move(done_cb))); + crypto_config_->ProcessClientHello( result, /*reject_only=*/true, connection_id_, server_address_.address(), client_address_, version_, versions_, /*use_stateless_rejects=*/true, server_designated_connection_id_, clock_, - random_, compressed_certs_cache_, ¶ms, &proof_, &reply_, - &diversification_nonce, &error_details_); + random_, compressed_certs_cache_, ¶ms_, &proof_, + QuicCryptoStream::CryptoMessageFramingOverhead(version_), + chlo_packet_size_, std::move(cb)); +} + +void StatelessRejector::ProcessClientHelloDone( + QuicErrorCode error, + const std::string& error_details, + std::unique_ptr<CryptoHandshakeMessage> message, + std::unique_ptr<StatelessRejector> rejector, + std::unique_ptr<StatelessRejector::ProcessDoneCallback> done_cb) { + reply_ = std::move(message); + if (error != QUIC_NO_ERROR) { error_ = error; - return; - } - - if (reply_.tag() == kSREJ) { + error_details_ = error_details; + state_ = FAILED; + } else if (reply_->tag() == kSREJ) { state_ = REJECTED; - return; + } else { + state_ = ACCEPTED; } - - state_ = ACCEPTED; + done_cb->Run(std::move(rejector)); } } // namespace net diff --git a/chromium/net/tools/quic/stateless_rejector.h b/chromium/net/tools/quic/stateless_rejector.h index 07a666d0cc7..c8dcd4b97c8 100644 --- a/chromium/net/tools/quic/stateless_rejector.h +++ b/chromium/net/tools/quic/stateless_rejector.h @@ -6,9 +6,9 @@ #define NET_QUIC_STATELESS_REJECTOR_H_ #include "base/strings/string_piece.h" -#include "net/quic/crypto/crypto_framer.h" -#include "net/quic/crypto/quic_crypto_server_config.h" -#include "net/quic/quic_protocol.h" +#include "net/quic/core/crypto/crypto_framer.h" +#include "net/quic/core/crypto/quic_crypto_server_config.h" +#include "net/quic/core/quic_protocol.h" namespace net { @@ -17,6 +17,7 @@ namespace net { class StatelessRejector { public: enum State { + UNKNOWN, // State has not yet been determined UNSUPPORTED, // Stateless rejects are not supported FAILED, // There was an error processing the CHLO. ACCEPTED, // The CHLO was accepted @@ -29,18 +30,30 @@ class StatelessRejector { QuicCompressedCertsCache* compressed_certs_cache, const QuicClock* clock, QuicRandom* random, + QuicByteCount chlo_packet_size, const IPEndPoint& client_address, const IPEndPoint& server_address); ~StatelessRejector(); - // Called when |chlo| is received for |connection_id| to determine - // if it should be statelessly rejected. + // Called when |chlo| is received for |connection_id|. void OnChlo(QuicVersion version, QuicConnectionId connection_id, QuicConnectionId server_designated_connection_id, const CryptoHandshakeMessage& chlo); + class ProcessDoneCallback { + public: + virtual ~ProcessDoneCallback() = default; + virtual void Run(std::unique_ptr<StatelessRejector> rejector) = 0; + }; + + // Perform processing to determine whether the CHLO received in OnChlo should + // be statelessly rejected, and invoke the callback once a decision has been + // made. + static void Process(std::unique_ptr<StatelessRejector> rejector, + std::unique_ptr<ProcessDoneCallback> done_cb); + // Returns the state of the rejector after OnChlo() has been called. State state() const { return state_; } @@ -50,8 +63,11 @@ class StatelessRejector { // Returns the error details when state() returns FAILED. std::string error_details() const { return error_details_; } + // Returns the connection ID. + QuicConnectionId connection_id() const { return connection_id_; } + // Returns the SREJ message when state() returns REJECTED. - const CryptoHandshakeMessage& reply() const { return reply_; } + const CryptoHandshakeMessage& reply() const { return *reply_; } private: // Helper class which is passed in to @@ -59,9 +75,20 @@ class StatelessRejector { class ValidateCallback; friend class ValidateCallback; + class ProcessClientHelloCallback; + friend class ProcessClientHelloCallback; + void ProcessClientHello( - const CryptoHandshakeMessage& client_hello, - const ValidateClientHelloResultCallback::Result& result); + scoped_refptr<ValidateClientHelloResultCallback::Result> result, + std::unique_ptr<StatelessRejector> rejector, + std::unique_ptr<StatelessRejector::ProcessDoneCallback> done_cb); + + void ProcessClientHelloDone( + QuicErrorCode error, + const std::string& error_details, + std::unique_ptr<CryptoHandshakeMessage> message, + std::unique_ptr<StatelessRejector> rejector, + std::unique_ptr<StatelessRejector::ProcessDoneCallback> done_cb); State state_; QuicErrorCode error_; @@ -70,16 +97,18 @@ class StatelessRejector { QuicVersionVector versions_; QuicConnectionId connection_id_; QuicConnectionId server_designated_connection_id_; + QuicByteCount chlo_packet_size_; IPEndPoint client_address_; IPEndPoint server_address_; const QuicClock* clock_; QuicRandom* random_; const QuicCryptoServerConfig* crypto_config_; QuicCompressedCertsCache* compressed_certs_cache_; - const CryptoHandshakeMessage* chlo_; - CryptoHandshakeMessage reply_; + CryptoHandshakeMessage chlo_; + std::unique_ptr<CryptoHandshakeMessage> reply_; CryptoFramer crypto_framer_; QuicCryptoProof proof_; + QuicCryptoNegotiatedParameters params_; DISALLOW_COPY_AND_ASSIGN(StatelessRejector); }; diff --git a/chromium/net/tools/quic/stateless_rejector_test.cc b/chromium/net/tools/quic/stateless_rejector_test.cc index 97f7839998a..d773ff53de7 100644 --- a/chromium/net/tools/quic/stateless_rejector_test.cc +++ b/chromium/net/tools/quic/stateless_rejector_test.cc @@ -7,9 +7,11 @@ #include <memory> #include <vector> -#include "net/quic/crypto/crypto_handshake_message.h" -#include "net/quic/crypto/proof_source.h" -#include "net/quic/quic_utils.h" +#include "base/memory/ptr_util.h" +#include "base/strings/stringprintf.h" +#include "net/quic/core/crypto/crypto_handshake_message.h" +#include "net/quic/core/crypto/proof_source.h" +#include "net/quic/core/quic_utils.h" #include "net/quic/test_tools/crypto_test_utils.h" #include "net/quic/test_tools/quic_crypto_server_config_peer.h" #include "net/quic/test_tools/quic_test_utils.h" @@ -28,21 +30,38 @@ const QuicConnectionId kServerDesignateConnectionId = 24; // All four combinations of the two flags involved. enum FlagsMode { ENABLED, STATELESS_DISABLED, CHEAP_DISABLED, BOTH_DISABLED }; +const char* FlagsModeToString(FlagsMode mode) { + switch (mode) { + case ENABLED: + return "ENABLED"; + case STATELESS_DISABLED: + return "STATELESS_DISABLED"; + case CHEAP_DISABLED: + return "CHEAP_DISABLED"; + case BOTH_DISABLED: + return "BOTH_DISABLED"; + default: + DLOG(FATAL) << "Unexpected FlagsMode"; + return nullptr; + } +} + // Test various combinations of QUIC version and flag state. struct TestParams { QuicVersion version; FlagsMode flags; - friend ostream& operator<<(ostream& os, const TestParams& p) { - os << "{ version: " << p.version << " flags: " << p.flags << " }"; - return os; - } }; +string TestParamToString(const testing::TestParamInfo<TestParams>& params) { + return base::StringPrintf("v%i_%s", params.param.version, + FlagsModeToString(params.param.flags)); +} + vector<TestParams> GetTestParams() { vector<TestParams> params; for (FlagsMode flags : {ENABLED, STATELESS_DISABLED, CHEAP_DISABLED, BOTH_DISABLED}) { - for (QuicVersion version : QuicSupportedVersions()) { + for (QuicVersion version : AllSupportedVersions()) { TestParams param; param.version = version; param.flags = flags; @@ -62,14 +81,16 @@ class StatelessRejectorTest : public ::testing::TestWithParam<TestParams> { config_peer_(&config_), compressed_certs_cache_( QuicCompressedCertsCache::kQuicCompressedCertsCacheSize), - rejector_(GetParam().version, - QuicSupportedVersions(), - &config_, - &compressed_certs_cache_, - &clock_, - QuicRandom::GetInstance(), - IPEndPoint(net::test::Loopback4(), 12345), - IPEndPoint(net::test::Loopback4(), 443)) { + rejector_(base::MakeUnique<StatelessRejector>( + GetParam().version, + AllSupportedVersions(), + &config_, + &compressed_certs_cache_, + &clock_, + QuicRandom::GetInstance(), + kDefaultMaxPacketSize, + IPEndPoint(net::test::Loopback4(), 12345), + IPEndPoint(net::test::Loopback4(), 443))) { FLAGS_enable_quic_stateless_reject_support = GetParam().flags == ENABLED || GetParam().flags == CHEAP_DISABLED; FLAGS_quic_use_cheap_stateless_rejects = @@ -111,13 +132,26 @@ class StatelessRejectorTest : public ::testing::TestWithParam<TestParams> { } protected: + class ProcessDoneCallback : public StatelessRejector::ProcessDoneCallback { + public: + explicit ProcessDoneCallback(StatelessRejectorTest* test) : test_(test) {} + void Run(std::unique_ptr<StatelessRejector> rejector) override { + test_->rejector_ = std::move(rejector); + } + + private: + StatelessRejectorTest* test_; + }; + + QuicFlagSaver flags_; // Save/restore all QUIC flag values. + std::unique_ptr<ProofSource> proof_source_; MockClock clock_; QuicCryptoServerConfig config_; QuicCryptoServerConfigPeer config_peer_; QuicCompressedCertsCache compressed_certs_cache_; QuicCryptoServerConfig::ConfigOptions config_options_; - StatelessRejector rejector_; + std::unique_ptr<StatelessRejector> rejector_; // Values used in CHLO messages string scid_hex_; @@ -129,31 +163,39 @@ class StatelessRejectorTest : public ::testing::TestWithParam<TestParams> { INSTANTIATE_TEST_CASE_P(Flags, StatelessRejectorTest, - ::testing::ValuesIn(GetTestParams())); + ::testing::ValuesIn(GetTestParams()), + TestParamToString); TEST_P(StatelessRejectorTest, InvalidChlo) { // clang-format off const CryptoHandshakeMessage client_hello = CryptoTestUtils::Message( "CHLO", + "PDMD", "X509", "COPT", "SREJ", nullptr); // clang-format on - rejector_.OnChlo(GetParam().version, kConnectionId, - kServerDesignateConnectionId, client_hello); + rejector_->OnChlo(GetParam().version, kConnectionId, + kServerDesignateConnectionId, client_hello); if (GetParam().flags != ENABLED || GetParam().version <= QUIC_VERSION_32) { - EXPECT_EQ(StatelessRejector::UNSUPPORTED, rejector_.state()); + EXPECT_EQ(StatelessRejector::UNSUPPORTED, rejector_->state()); return; } - EXPECT_EQ(StatelessRejector::FAILED, rejector_.state()); - EXPECT_EQ(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER, rejector_.error()); + // The StatelessRejector is undecided - proceed with async processing + ASSERT_EQ(StatelessRejector::UNKNOWN, rejector_->state()); + StatelessRejector::Process(std::move(rejector_), + base::MakeUnique<ProcessDoneCallback>(this)); + + EXPECT_EQ(StatelessRejector::FAILED, rejector_->state()); + EXPECT_EQ(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER, rejector_->error()); } TEST_P(StatelessRejectorTest, ValidChloWithoutSrejSupport) { // clang-format off const CryptoHandshakeMessage client_hello = CryptoTestUtils::Message( "CHLO", + "PDMD", "X509", "AEAD", "AESG", "KEXS", "C255", "PUBS", pubs_hex_.c_str(), @@ -163,15 +205,16 @@ TEST_P(StatelessRejectorTest, ValidChloWithoutSrejSupport) { nullptr); // clang-format on - rejector_.OnChlo(GetParam().version, kConnectionId, - kServerDesignateConnectionId, client_hello); - EXPECT_EQ(StatelessRejector::UNSUPPORTED, rejector_.state()); + rejector_->OnChlo(GetParam().version, kConnectionId, + kServerDesignateConnectionId, client_hello); + EXPECT_EQ(StatelessRejector::UNSUPPORTED, rejector_->state()); } TEST_P(StatelessRejectorTest, RejectChlo) { // clang-format off const CryptoHandshakeMessage client_hello = CryptoTestUtils::Message( "CHLO", + "PDMD", "X509", "AEAD", "AESG", "KEXS", "C255", "COPT", "SREJ", @@ -184,14 +227,20 @@ TEST_P(StatelessRejectorTest, RejectChlo) { nullptr); // clang-format on - rejector_.OnChlo(GetParam().version, kConnectionId, - kServerDesignateConnectionId, client_hello); + rejector_->OnChlo(GetParam().version, kConnectionId, + kServerDesignateConnectionId, client_hello); if (GetParam().flags != ENABLED || GetParam().version <= QUIC_VERSION_32) { - EXPECT_EQ(StatelessRejector::UNSUPPORTED, rejector_.state()); + EXPECT_EQ(StatelessRejector::UNSUPPORTED, rejector_->state()); return; } - ASSERT_EQ(StatelessRejector::REJECTED, rejector_.state()); - const CryptoHandshakeMessage& reply = rejector_.reply(); + + // The StatelessRejector is undecided - proceed with async processing + ASSERT_EQ(StatelessRejector::UNKNOWN, rejector_->state()); + StatelessRejector::Process(std::move(rejector_), + base::MakeUnique<ProcessDoneCallback>(this)); + + ASSERT_EQ(StatelessRejector::REJECTED, rejector_->state()); + const CryptoHandshakeMessage& reply = rejector_->reply(); EXPECT_EQ(kSREJ, reply.tag()); const uint32_t* reject_reasons; size_t num_reject_reasons; @@ -210,6 +259,7 @@ TEST_P(StatelessRejectorTest, AcceptChlo) { // clang-format off const CryptoHandshakeMessage client_hello = CryptoTestUtils::Message( "CHLO", + "PDMD", "X509", "AEAD", "AESG", "KEXS", "C255", "COPT", "SREJ", @@ -223,13 +273,19 @@ TEST_P(StatelessRejectorTest, AcceptChlo) { nullptr); // clang-format on - rejector_.OnChlo(GetParam().version, kConnectionId, - kServerDesignateConnectionId, client_hello); + rejector_->OnChlo(GetParam().version, kConnectionId, + kServerDesignateConnectionId, client_hello); if (GetParam().flags != ENABLED || GetParam().version <= QUIC_VERSION_32) { - EXPECT_EQ(StatelessRejector::UNSUPPORTED, rejector_.state()); + EXPECT_EQ(StatelessRejector::UNSUPPORTED, rejector_->state()); return; } - EXPECT_EQ(StatelessRejector::ACCEPTED, rejector_.state()); + + // The StatelessRejector is undecided - proceed with async processing + ASSERT_EQ(StatelessRejector::UNKNOWN, rejector_->state()); + StatelessRejector::Process(std::move(rejector_), + base::MakeUnique<ProcessDoneCallback>(this)); + + EXPECT_EQ(StatelessRejector::ACCEPTED, rejector_->state()); } } // namespace diff --git a/chromium/net/tools/quic/synchronous_host_resolver.cc b/chromium/net/tools/quic/synchronous_host_resolver.cc index 7bdf5fd6e25..ba1e51b3f5b 100644 --- a/chromium/net/tools/quic/synchronous_host_resolver.cc +++ b/chromium/net/tools/quic/synchronous_host_resolver.cc @@ -15,7 +15,8 @@ #include "net/base/host_port_pair.h" #include "net/base/net_errors.h" #include "net/dns/host_resolver_impl.h" -#include "net/dns/single_request_host_resolver.h" +#include "net/log/net_log.h" +#include "net/log/net_log_with_source.h" namespace net { @@ -60,16 +61,16 @@ void ResolverThread::Run() { net::HostResolver::Options options; options.max_concurrent_resolves = 6; options.max_retry_attempts = 3u; - std::unique_ptr<net::HostResolverImpl> resolver_impl( + std::unique_ptr<net::HostResolverImpl> resolver( new net::HostResolverImpl(options, &net_log)); - SingleRequestHostResolver resolver(resolver_impl.get()); + std::unique_ptr<net::HostResolver::Request> request; HostPortPair host_port_pair(host_, 80); - rv_ = resolver.Resolve(HostResolver::RequestInfo(host_port_pair), - DEFAULT_PRIORITY, addresses_, - base::Bind(&ResolverThread::OnResolutionComplete, - weak_factory_.GetWeakPtr()), - BoundNetLog()); + rv_ = resolver->Resolve(HostResolver::RequestInfo(host_port_pair), + DEFAULT_PRIORITY, addresses_, + base::Bind(&ResolverThread::OnResolutionComplete, + weak_factory_.GetWeakPtr()), + &request, NetLogWithSource()); if (rv_ != ERR_IO_PENDING) return; diff --git a/chromium/net/tools/quic/synchronous_host_resolver.h b/chromium/net/tools/quic/synchronous_host_resolver.h index 7089c874251..d41f0afb79b 100644 --- a/chromium/net/tools/quic/synchronous_host_resolver.h +++ b/chromium/net/tools/quic/synchronous_host_resolver.h @@ -8,7 +8,6 @@ #define NET_TOOLS_QUIC_SYNCHRONOUS_HOST_RESOLVER_H_ #include "net/base/address_list.h" -#include "net/base/net_export.h" #include "net/dns/host_resolver.h" namespace net { diff --git a/chromium/net/tools/quic/test_tools/limited_mtu_test_writer.h b/chromium/net/tools/quic/test_tools/limited_mtu_test_writer.h index a9efa7f1d91..c58b7662f26 100644 --- a/chromium/net/tools/quic/test_tools/limited_mtu_test_writer.h +++ b/chromium/net/tools/quic/test_tools/limited_mtu_test_writer.h @@ -8,7 +8,7 @@ #include <stddef.h> #include "base/macros.h" -#include "net/quic/quic_protocol.h" +#include "net/quic/core/quic_protocol.h" #include "net/tools/quic/quic_packet_writer_wrapper.h" namespace net { diff --git a/chromium/net/tools/quic/test_tools/mock_quic_server_session_visitor.cc b/chromium/net/tools/quic/test_tools/mock_quic_server_session_visitor.cc index 9b5d4c0a186..f3ae2b05686 100644 --- a/chromium/net/tools/quic/test_tools/mock_quic_server_session_visitor.cc +++ b/chromium/net/tools/quic/test_tools/mock_quic_server_session_visitor.cc @@ -11,9 +11,9 @@ MockQuicServerSessionVisitor::MockQuicServerSessionVisitor() {} MockQuicServerSessionVisitor::~MockQuicServerSessionVisitor() {} -MockQuicServerSessionHelper::MockQuicServerSessionHelper() {} +MockQuicCryptoServerStreamHelper::MockQuicCryptoServerStreamHelper() {} -MockQuicServerSessionHelper::~MockQuicServerSessionHelper() {} +MockQuicCryptoServerStreamHelper::~MockQuicCryptoServerStreamHelper() {} } // namespace test } // namespace net diff --git a/chromium/net/tools/quic/test_tools/mock_quic_server_session_visitor.h b/chromium/net/tools/quic/test_tools/mock_quic_server_session_visitor.h index 52d59d2e795..d89f49cb8a0 100644 --- a/chromium/net/tools/quic/test_tools/mock_quic_server_session_visitor.h +++ b/chromium/net/tools/quic/test_tools/mock_quic_server_session_visitor.h @@ -6,7 +6,8 @@ #define NET_TOOLS_QUIC_TEST_TOOLS_MOCK_QUIC_SERVER_SESSION_VISITOR_H_ #include "base/macros.h" -#include "net/quic/quic_server_session_base.h" +#include "net/quic/core/quic_crypto_server_stream.h" +#include "net/quic/core/quic_server_session_base.h" #include "testing/gmock/include/gmock/gmock.h" namespace net { @@ -24,15 +25,17 @@ class MockQuicServerSessionVisitor : public QuicServerSessionBase::Visitor { void(QuicBlockedWriterInterface* blocked_writer)); MOCK_METHOD1(OnConnectionAddedToTimeWaitList, void(QuicConnectionId connection_id)); + MOCK_METHOD1(OnPacketBeingDispatchedToSession, + void(QuicServerSessionBase* session)); private: DISALLOW_COPY_AND_ASSIGN(MockQuicServerSessionVisitor); }; -class MockQuicServerSessionHelper : public QuicServerSessionBase::Helper { +class MockQuicCryptoServerStreamHelper : public QuicCryptoServerStream::Helper { public: - MockQuicServerSessionHelper(); - ~MockQuicServerSessionHelper() override; + MockQuicCryptoServerStreamHelper(); + ~MockQuicCryptoServerStreamHelper() override; MOCK_CONST_METHOD1(GenerateConnectionIdForReject, QuicConnectionId(QuicConnectionId connection_id)); MOCK_CONST_METHOD3(CanAcceptClientHello, @@ -41,7 +44,7 @@ class MockQuicServerSessionHelper : public QuicServerSessionBase::Helper { std::string* error_details)); private: - DISALLOW_COPY_AND_ASSIGN(MockQuicServerSessionHelper); + DISALLOW_COPY_AND_ASSIGN(MockQuicCryptoServerStreamHelper); }; } // namespace test diff --git a/chromium/net/tools/quic/test_tools/packet_dropping_test_writer.cc b/chromium/net/tools/quic/test_tools/packet_dropping_test_writer.cc index 4d85d5a85f9..54424887951 100644 --- a/chromium/net/tools/quic/test_tools/packet_dropping_test_writer.cc +++ b/chromium/net/tools/quic/test_tools/packet_dropping_test_writer.cc @@ -50,6 +50,7 @@ PacketDroppingTestWriter::PacketDroppingTestWriter() : clock_(nullptr), cur_buffer_size_(0), num_calls_to_write_(0), + max_allowed_packet_size_(std::numeric_limits<QuicByteCount>::max()), config_mutex_(), fake_packet_loss_percentage_(0), fake_drop_first_n_packets_(0), @@ -81,6 +82,7 @@ WriteResult PacketDroppingTestWriter::WritePacket( const IPAddress& self_address, const IPEndPoint& peer_address, PerPacketOptions* options) { + CHECK_LE(buf_len, max_allowed_packet_size_); ++num_calls_to_write_; ReleaseOldPackets(); @@ -118,14 +120,14 @@ WriteResult PacketDroppingTestWriter::WritePacket( } // Queue it to be sent. - QuicTime send_time = clock_->ApproximateNow().Add(fake_packet_delay_); + QuicTime send_time = clock_->ApproximateNow() + fake_packet_delay_; if (!fake_bandwidth_.IsZero()) { // Calculate a time the bandwidth limit would impose. QuicTime::Delta bandwidth_delay = QuicTime::Delta::FromMicroseconds( (buf_len * kNumMicrosPerSecond) / fake_bandwidth_.ToBytesPerSecond()); send_time = delayed_packets_.empty() - ? send_time.Add(bandwidth_delay) - : delayed_packets_.back().send_time.Add(bandwidth_delay); + ? send_time + bandwidth_delay + : delayed_packets_.back().send_time + bandwidth_delay; } std::unique_ptr<PerPacketOptions> delayed_options; if (options != nullptr) { diff --git a/chromium/net/tools/quic/test_tools/packet_dropping_test_writer.h b/chromium/net/tools/quic/test_tools/packet_dropping_test_writer.h index 2e3fcbca0ea..ff058b5660a 100644 --- a/chromium/net/tools/quic/test_tools/packet_dropping_test_writer.h +++ b/chromium/net/tools/quic/test_tools/packet_dropping_test_writer.h @@ -16,7 +16,7 @@ #include "base/macros.h" #include "base/synchronization/lock.h" #include "net/base/ip_address.h" -#include "net/quic/quic_alarm.h" +#include "net/quic/core/quic_alarm.h" #include "net/quic/test_tools/quic_test_utils.h" #include "net/tools/quic/quic_epoll_clock.h" #include "net/tools/quic/quic_packet_writer_wrapper.h" @@ -121,6 +121,12 @@ class PacketDroppingTestWriter : public QuicPacketWriterWrapper { // Useful for reproducing very flaky issues. void set_seed(uint64_t seed) { simple_random_.set_seed(seed); } + // Sets the maximum allowed packet size to be sent. Packets larger than + // |packet_size| will cause the write to check-fail. + void set_max_allowed_packet_size(QuicByteCount packet_size) { + max_allowed_packet_size_ = packet_size; + } + private: // Writes out the next packet to the contained writer and returns the time // for the next delayed packet to be written. @@ -162,6 +168,7 @@ class PacketDroppingTestWriter : public QuicPacketWriterWrapper { DelayedPacketList delayed_packets_; QuicByteCount cur_buffer_size_; uint64_t num_calls_to_write_; + QuicByteCount max_allowed_packet_size_; base::Lock config_mutex_; int32_t fake_packet_loss_percentage_; diff --git a/chromium/net/tools/quic/test_tools/packet_reordering_writer.cc b/chromium/net/tools/quic/test_tools/packet_reordering_writer.cc new file mode 100644 index 00000000000..a11f7d8e393 --- /dev/null +++ b/chromium/net/tools/quic/test_tools/packet_reordering_writer.cc @@ -0,0 +1,50 @@ +// Copyright 2013 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/tools/quic/test_tools/packet_reordering_writer.h" + +namespace net { +namespace test { + +PacketReorderingWriter::PacketReorderingWriter() {} + +PacketReorderingWriter::~PacketReorderingWriter() {} + +WriteResult PacketReorderingWriter::WritePacket(const char* buffer, + size_t buf_len, + const IPAddress& self_address, + const IPEndPoint& peer_address, + PerPacketOptions* options) { + if (!delay_next_) { + WriteResult wr = QuicPacketWriterWrapper::WritePacket( + buffer, buf_len, self_address, peer_address, options); + --num_packets_to_wait_; + if (num_packets_to_wait_ == 0) { + // It's time to write the delayed packet. + QuicPacketWriterWrapper::WritePacket( + delayed_data_.data(), delayed_data_.length(), delayed_self_address_, + delayed_peer_address_, delayed_options_.get()); + } + return wr; + } + // Still have packet to wait. + DCHECK_LT(0u, num_packets_to_wait_) << "Only allow one packet to be delayed"; + delayed_data_ = std::string(buffer, buf_len); + delayed_self_address_ = self_address; + delayed_peer_address_ = peer_address; + if (options != nullptr) { + delayed_options_.reset(options->Clone()); + } + delay_next_ = false; + return WriteResult(WRITE_STATUS_OK, buf_len); +} + +void PacketReorderingWriter::SetDelay(size_t num_packets_to_wait) { + DCHECK_GT(num_packets_to_wait, 0u); + num_packets_to_wait_ = num_packets_to_wait; + delay_next_ = true; +} + +} // namespace test +} // namespace net diff --git a/chromium/net/tools/quic/test_tools/packet_reordering_writer.h b/chromium/net/tools/quic/test_tools/packet_reordering_writer.h new file mode 100644 index 00000000000..d09aeb84a3a --- /dev/null +++ b/chromium/net/tools/quic/test_tools/packet_reordering_writer.h @@ -0,0 +1,45 @@ +// Copyright 2013 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 NET_TOOLS_QUIC_TEST_TOOLS_PACKET_REORDERING_WRITER_H_ +#define NET_TOOLS_QUIC_TEST_TOOLS_PACKET_REORDERING_WRITER_H_ + +#include "net/tools/quic/quic_packet_writer_wrapper.h" + +namespace net { + +namespace test { + +// This packet writer allows delaying writing the next packet after +// SetDelay(num_packets_to_wait) +// is called and buffer this packet and write it after it writes next +// |num_packets_to_wait| packets. It doesn't support delaying a packet while +// there is already a packet delayed. +class PacketReorderingWriter : public QuicPacketWriterWrapper { + public: + PacketReorderingWriter(); + + ~PacketReorderingWriter() override; + + WriteResult WritePacket(const char* buffer, + size_t buf_len, + const IPAddress& self_address, + const IPEndPoint& peer_address, + PerPacketOptions* options) override; + + void SetDelay(size_t num_packets_to_wait); + + private: + bool delay_next_ = false; + size_t num_packets_to_wait_ = 0; + std::string delayed_data_; + IPAddress delayed_self_address_; + IPEndPoint delayed_peer_address_; + std::unique_ptr<PerPacketOptions> delayed_options_; +}; + +} // namespace test +} // namespace net + +#endif // NET_TOOLS_QUIC_TEST_TOOLS_PACKET_REORDERING_WRITER_H_ diff --git a/chromium/net/tools/quic/test_tools/quic_client_peer.cc b/chromium/net/tools/quic/test_tools/quic_client_peer.cc index 961cc823e2d..aca80df0c20 100644 --- a/chromium/net/tools/quic/test_tools/quic_client_peer.cc +++ b/chromium/net/tools/quic/test_tools/quic_client_peer.cc @@ -16,7 +16,9 @@ QuicCryptoClientConfig* QuicClientPeer::GetCryptoConfig(QuicClient* client) { // static bool QuicClientPeer::CreateUDPSocketAndBind(QuicClient* client) { - return client->CreateUDPSocketAndBind(); + return client->CreateUDPSocketAndBind(client->server_address(), + client->bind_to_address(), + client->local_port()); } // static diff --git a/chromium/net/tools/quic/test_tools/quic_dispatcher_peer.cc b/chromium/net/tools/quic/test_tools/quic_dispatcher_peer.cc index 48a61f71d68..bb528841e83 100644 --- a/chromium/net/tools/quic/test_tools/quic_dispatcher_peer.cc +++ b/chromium/net/tools/quic/test_tools/quic_dispatcher_peer.cc @@ -62,10 +62,35 @@ QuicErrorCode QuicDispatcherPeer::GetAndClearLastError( } // static +QuicBufferedPacketStore* QuicDispatcherPeer::GetBufferedPackets( + QuicDispatcher* dispatcher) { + return &(dispatcher->buffered_packets_); +} + +// static const QuicDispatcher::SessionMap& QuicDispatcherPeer::session_map( QuicDispatcher* dispatcher) { return dispatcher->session_map(); } +// static +void QuicDispatcherPeer::set_new_sessions_allowed_per_event_loop( + QuicDispatcher* dispatcher, + size_t num_session_allowed) { + return dispatcher->set_new_sessions_allowed_per_event_loop( + num_session_allowed); +} + +// static +void QuicDispatcherPeer::SendPublicReset( + QuicDispatcher* dispatcher, + const IPEndPoint& server_address, + const IPEndPoint& client_address, + QuicConnectionId connection_id, + QuicPacketNumber rejected_packet_number) { + dispatcher->time_wait_list_manager()->SendPublicReset( + server_address, client_address, connection_id, rejected_packet_number); +} + } // namespace test } // namespace net diff --git a/chromium/net/tools/quic/test_tools/quic_dispatcher_peer.h b/chromium/net/tools/quic/test_tools/quic_dispatcher_peer.h index 4ff4d2d38b6..c415fc39ad1 100644 --- a/chromium/net/tools/quic/test_tools/quic_dispatcher_peer.h +++ b/chromium/net/tools/quic/test_tools/quic_dispatcher_peer.h @@ -41,9 +41,22 @@ class QuicDispatcherPeer { // visitor's OnError() method. Then set that record to QUIC_NO_ERROR. static QuicErrorCode GetAndClearLastError(QuicDispatcher* dispatcher); + static QuicBufferedPacketStore* GetBufferedPackets( + QuicDispatcher* dispatcher); + static const QuicDispatcher::SessionMap& session_map( QuicDispatcher* dispatcher); + static void set_new_sessions_allowed_per_event_loop( + QuicDispatcher* dispatcher, + size_t num_session_allowed); + + static void SendPublicReset(QuicDispatcher* dispatcher, + const IPEndPoint& server_address, + const IPEndPoint& client_address, + QuicConnectionId connection_id, + QuicPacketNumber rejected_packet_number); + private: DISALLOW_COPY_AND_ASSIGN(QuicDispatcherPeer); }; diff --git a/chromium/net/tools/quic/test_tools/quic_test_client.cc b/chromium/net/tools/quic/test_tools/quic_test_client.cc index 914c4f8ab03..e58916b0ee5 100644 --- a/chromium/net/tools/quic/test_tools/quic_test_client.cc +++ b/chromium/net/tools/quic/test_tools/quic_test_client.cc @@ -7,14 +7,17 @@ #include <memory> #include <utility> +#include "base/memory/ptr_util.h" #include "base/time/time.h" #include "net/base/completion_callback.h" #include "net/base/net_errors.h" #include "net/cert/cert_verify_result.h" #include "net/cert/x509_certificate.h" -#include "net/quic/crypto/proof_verifier.h" -#include "net/quic/quic_flags.h" -#include "net/quic/quic_server_id.h" +#include "net/quic/core/crypto/proof_verifier.h" +#include "net/quic/core/quic_flags.h" +#include "net/quic/core/quic_server_id.h" +#include "net/quic/core/quic_utils.h" +#include "net/quic/test_tools/crypto_test_utils.h" #include "net/quic/test_tools/quic_connection_peer.h" #include "net/quic/test_tools/quic_spdy_session_peer.h" #include "net/quic/test_tools/quic_test_utils.h" @@ -35,28 +38,36 @@ using net::test::QuicSpdySessionPeer; using net::test::ReliableQuicStreamPeer; using std::string; using std::vector; +using testing::_; +using testing::Invoke; namespace net { namespace test { namespace { // RecordingProofVerifier accepts any certificate chain and records the common -// name of the leaf. +// name of the leaf and then delegates the actual verfication to an actual +// verifier. If no optional verifier is provided, then VerifyProof will return +// success. class RecordingProofVerifier : public ProofVerifier { public: + explicit RecordingProofVerifier(std::unique_ptr<ProofVerifier> verifier) + : verifier_(std::move(verifier)) {} + // ProofVerifier interface. - QuicAsyncStatus VerifyProof(const string& hostname, - const uint16_t port, - const string& server_config, - QuicVersion quic_version, - StringPiece chlo_hash, - const vector<string>& certs, - const string& cert_sct, - const string& signature, - const ProofVerifyContext* context, - string* error_details, - std::unique_ptr<ProofVerifyDetails>* details, - ProofVerifierCallback* callback) override { + QuicAsyncStatus VerifyProof( + const string& hostname, + const uint16_t port, + const string& server_config, + QuicVersion quic_version, + StringPiece chlo_hash, + const vector<string>& certs, + const string& cert_sct, + const string& signature, + const ProofVerifyContext* context, + string* error_details, + std::unique_ptr<ProofVerifyDetails>* details, + std::unique_ptr<ProofVerifierCallback> callback) override { common_name_.clear(); if (certs.empty()) { return QUIC_FAILURE; @@ -79,6 +90,22 @@ class RecordingProofVerifier : public ProofVerifier { // common_name_ = cert->subject().GetDisplayName(); cert_sct_ = cert_sct; + if (!verifier_) { + return QUIC_SUCCESS; + } + + return verifier_->VerifyProof( + hostname, port, server_config, quic_version, chlo_hash, certs, cert_sct, + signature, context, error_details, details, std::move(callback)); + } + + QuicAsyncStatus VerifyCertChain( + const std::string& hostname, + const std::vector<std::string>& certs, + const ProofVerifyContext* verify_context, + std::string* error_details, + std::unique_ptr<ProofVerifyDetails>* verify_details, + std::unique_ptr<ProofVerifierCallback> callback) override { return QUIC_SUCCESS; } @@ -87,6 +114,7 @@ class RecordingProofVerifier : public ProofVerifier { const string& cert_sct() const { return cert_sct_; } private: + std::unique_ptr<ProofVerifier> verifier_; string common_name_; string cert_sct_; }; @@ -103,9 +131,11 @@ BalsaHeaders* MungeHeaders(const BalsaHeaders* const_headers) { } BalsaHeaders* headers = new BalsaHeaders; headers->CopyFrom(*const_headers); - if (!uri.starts_with("https://") && !uri.starts_with("http://")) { + if (!base::StartsWith(uri, "https://", + base::CompareCase::INSENSITIVE_ASCII) && + !base::StartsWith(uri, "http://", base::CompareCase::INSENSITIVE_ASCII)) { // If we have a relative URL, set some defaults. - string full_uri = "https://www.google.com"; + string full_uri = "https://test.example.com"; full_uri.append(uri.as_string()); headers->SetRequestUri(full_uri); } @@ -129,14 +159,39 @@ MockableQuicClient::MockableQuicClient( const QuicConfig& config, const QuicVersionVector& supported_versions, EpollServer* epoll_server) + : MockableQuicClient(server_address, + server_id, + config, + supported_versions, + epoll_server, + nullptr) {} + +MockableQuicClient::MockableQuicClient( + IPEndPoint server_address, + const QuicServerId& server_id, + const QuicConfig& config, + const QuicVersionVector& supported_versions, + EpollServer* epoll_server, + std::unique_ptr<ProofVerifier> proof_verifier) : QuicClient(server_address, server_id, supported_versions, config, epoll_server, - new RecordingProofVerifier()), + base::WrapUnique( + new RecordingProofVerifier(std::move(proof_verifier)))), override_connection_id_(0), - test_writer_(nullptr) {} + test_writer_(nullptr), + track_last_incoming_packet_(false) {} + +void MockableQuicClient::ProcessPacket(const IPEndPoint& self_address, + const IPEndPoint& peer_address, + const QuicReceivedPacket& packet) { + QuicClient::ProcessPacket(self_address, peer_address, packet); + if (track_last_incoming_packet_) { + last_incoming_packet_.reset(packet.Clone()); + } +} MockableQuicClient::~MockableQuicClient() { if (connected()) { @@ -187,11 +242,31 @@ QuicTestClient::QuicTestClient(IPEndPoint server_address, config, supported_versions, &epoll_server_)), + response_complete_(false), allow_bidirectional_data_(false) { Initialize(); } -QuicTestClient::QuicTestClient() : allow_bidirectional_data_(false) {} +QuicTestClient::QuicTestClient(IPEndPoint server_address, + const string& server_hostname, + const QuicConfig& config, + const QuicVersionVector& supported_versions, + std::unique_ptr<ProofVerifier> proof_verifier) + : client_(new MockableQuicClient(server_address, + QuicServerId(server_hostname, + server_address.port(), + PRIVACY_MODE_DISABLED), + config, + supported_versions, + &epoll_server_, + std::move(proof_verifier))), + response_complete_(false), + allow_bidirectional_data_(false) { + Initialize(); +} + +QuicTestClient::QuicTestClient() + : response_complete_(false), allow_bidirectional_data_(false) {} QuicTestClient::~QuicTestClient() { if (stream_) { @@ -248,10 +323,10 @@ ssize_t QuicTestClient::GetOrCreateStreamAndSendRequest( return 1; if (rv == QUIC_PENDING) { // May need to retry request if asynchronous rendezvous fails. - auto* new_headers = new BalsaHeaders; - new_headers->CopyFrom(*headers); - push_promise_data_to_resend_.reset( - new TestClientDataToResend(new_headers, body, fin, this, delegate)); + std::unique_ptr<SpdyHeaderBlock> new_headers(new SpdyHeaderBlock( + SpdyBalsaUtils::RequestHeadersToSpdyHeaders(*headers))); + push_promise_data_to_resend_.reset(new TestClientDataToResend( + std::move(new_headers), body, fin, this, delegate)); return 1; } } @@ -286,24 +361,29 @@ ssize_t QuicTestClient::GetOrCreateStreamAndSendRequest( ret = body.length(); } if (FLAGS_enable_quic_stateless_reject_support) { - BalsaHeaders* new_headers = nullptr; + std::unique_ptr<SpdyHeaderBlock> new_headers; if (headers) { - new_headers = new BalsaHeaders; - new_headers->CopyFrom(*headers); + new_headers.reset(new SpdyHeaderBlock( + SpdyBalsaUtils::RequestHeadersToSpdyHeaders(*headers))); } - auto* data_to_resend = - new TestClientDataToResend(new_headers, body, fin, this, delegate); - client()->MaybeAddQuicDataToResend(data_to_resend); + std::unique_ptr<QuicClientBase::QuicDataToResend> data_to_resend( + new TestClientDataToResend(std::move(new_headers), body, fin, this, + delegate)); + client()->MaybeAddQuicDataToResend(std::move(data_to_resend)); } return ret; } ssize_t QuicTestClient::SendMessage(const HTTPMessage& message) { stream_ = nullptr; // Always force creation of a stream for SendMessage. + // Any response we might have received for a previous request would no longer + // be valid. TODO(jeffpiazza): There's probably additional client state that + // should be reset here, too, if we were being more careful. + response_complete_ = false; // If we're not connected, try to find an sni hostname. if (!connected()) { - GURL url(message.headers()->request_uri().as_string()); + GURL url(message.headers()->request_uri()); if (override_sni_set_) { client_->set_server_id(QuicServerId(override_sni_, url.EffectiveIntPort(), PRIVACY_MODE_DISABLED)); @@ -386,6 +466,14 @@ string QuicTestClient::SendSynchronousRequest(const string& uri) { return SendCustomSynchronousRequest(message); } +void QuicTestClient::SetStream(QuicSpdyClientStream* stream) { + stream_ = stream; + if (stream_ != nullptr) { + response_complete_ = false; + stream_->set_visitor(this); + } +} + QuicSpdyClientStream* QuicTestClient::GetOrCreateStream() { if (!connect_attempted_ || auto_reconnect_) { if (!connected()) { @@ -396,14 +484,11 @@ QuicSpdyClientStream* QuicTestClient::GetOrCreateStream() { } } if (!stream_) { - stream_ = client_->CreateReliableClientStream(); - if (stream_ == nullptr) { - return nullptr; + SetStream(client_->CreateReliableClientStream()); + if (stream_) { + stream_->SetPriority(priority_); + stream_->set_allow_bidirectional_data(allow_bidirectional_data_); } - stream_->set_visitor(this); - QuicSpdyClientStream* cs = reinterpret_cast<QuicSpdyClientStream*>(stream_); - cs->SetPriority(priority_); - cs->set_allow_bidirectional_data(allow_bidirectional_data_); } return stream_; @@ -485,7 +570,7 @@ bool QuicTestClient::HaveActiveStream() { !client_->session()->IsClosedStream(stream_->id())); } -void QuicTestClient::WaitForResponseForMs(int timeout_ms) { +void QuicTestClient::WaitUntil(int timeout_ms, std::function<bool()> trigger) { int64_t timeout_us = timeout_ms * base::Time::kMicrosecondsPerMillisecond; int64_t old_timeout_us = epoll_server()->timeout_in_us(); if (timeout_us > 0) { @@ -495,35 +580,16 @@ void QuicTestClient::WaitForResponseForMs(int timeout_ms) { QuicConnectionPeer::GetHelper(client()->session()->connection()) ->GetClock(); QuicTime end_waiting_time = - clock->Now().Add(QuicTime::Delta::FromMicroseconds(timeout_us)); - while (HaveActiveStream() && + clock->Now() + QuicTime::Delta::FromMicroseconds(timeout_us); + while (HaveActiveStream() && !(trigger && trigger()) && (timeout_us < 0 || clock->Now() < end_waiting_time)) { client_->WaitForEvents(); } if (timeout_us > 0) { epoll_server()->set_timeout_in_us(old_timeout_us); } -} - -void QuicTestClient::WaitForInitialResponseForMs(int timeout_ms) { - int64_t timeout_us = timeout_ms * base::Time::kMicrosecondsPerMillisecond; - int64_t old_timeout_us = epoll_server()->timeout_in_us(); - if (timeout_us > 0) { - epoll_server()->set_timeout_in_us(timeout_us); - } - const QuicClock* clock = - QuicConnectionPeer::GetHelper(client()->session()->connection()) - ->GetClock(); - QuicTime end_waiting_time = - clock->Now().Add(QuicTime::Delta::FromMicroseconds(timeout_us)); - while (stream_ != nullptr && - !client_->session()->IsClosedStream(stream_->id()) && - stream_->stream_bytes_read() == 0 && - (timeout_us < 0 || clock->Now() < end_waiting_time)) { - client_->WaitForEvents(); - } - if (timeout_us > 0) { - epoll_server()->set_timeout_in_us(old_timeout_us); + if (trigger && !trigger()) { + VLOG(1) << "Client WaitUntil returning with trigger returning false."; } } @@ -553,15 +619,27 @@ const SpdyHeaderBlock& QuicTestClient::response_trailers() const { } int64_t QuicTestClient::response_size() const { - return bytes_read_; + return bytes_read(); } size_t QuicTestClient::bytes_read() const { - return bytes_read_; + // While stream_ is available, its member functions provide more accurate + // information. bytes_read_ is updated only when stream_ becomes null. + if (stream_) { + return stream_->stream_bytes_read() + stream_->header_bytes_read(); + } else { + return bytes_read_; + } } size_t QuicTestClient::bytes_written() const { - return bytes_written_; + // While stream_ is available, its member functions provide more accurate + // information. bytes_written_ is updated only when stream_ becomes null. + if (stream_) { + return stream_->stream_bytes_written() + stream_->header_bytes_written(); + } else { + return bytes_written_; + } } void QuicTestClient::OnClose(QuicSpdyStream* stream) { @@ -601,9 +679,8 @@ bool QuicTestClient::CheckVary(const SpdyHeaderBlock& client_request, void QuicTestClient::OnRendezvousResult(QuicSpdyStream* stream) { std::unique_ptr<TestClientDataToResend> data_to_resend = std::move(push_promise_data_to_resend_); - stream_ = static_cast<QuicSpdyClientStream*>(stream); + SetStream(static_cast<QuicSpdyClientStream*>(stream)); if (stream) { - stream->set_visitor(this); stream->OnDataAvailable(); } else if (data_to_resend.get()) { data_to_resend->Resend(); @@ -662,12 +739,11 @@ void QuicTestClient::WaitForWriteToFlush() { } void QuicTestClient::TestClientDataToResend::Resend() { - test_client_->GetOrCreateStreamAndSendRequest(headers_, body_, fin_, + BalsaHeaders balsa_headers; + SpdyBalsaUtils::SpdyHeadersToRequestHeaders(*headers_, &balsa_headers); + test_client_->GetOrCreateStreamAndSendRequest(&balsa_headers, body_, fin_, delegate_); - if (headers_ != nullptr) { - delete headers_; - headers_ = nullptr; - } + headers_.reset(); } // static diff --git a/chromium/net/tools/quic/test_tools/quic_test_client.h b/chromium/net/tools/quic/test_tools/quic_test_client.h index b76d1cfce20..3e52fc956db 100644 --- a/chromium/net/tools/quic/test_tools/quic_test_client.h +++ b/chromium/net/tools/quic/test_tools/quic_test_client.h @@ -15,14 +15,15 @@ #include "base/macros.h" #include "net/base/ip_address.h" #include "net/base/ip_endpoint.h" -#include "net/quic/proto/cached_network_parameters.pb.h" -#include "net/quic/quic_framer.h" -#include "net/quic/quic_packet_creator.h" -#include "net/quic/quic_protocol.h" +#include "net/quic/core/proto/cached_network_parameters.pb.h" +#include "net/quic/core/quic_framer.h" +#include "net/quic/core/quic_packet_creator.h" +#include "net/quic/core/quic_protocol.h" #include "net/tools/balsa/balsa_frame.h" #include "net/tools/epoll_server/epoll_server.h" #include "net/tools/quic/quic_client.h" #include "net/tools/quic/test_tools/simple_client.h" +#include "testing/gmock/include/gmock/gmock.h" using base::StringPiece; @@ -30,6 +31,7 @@ namespace net { class ProofVerifier; +class ProofVerifier; class QuicPacketWriterWrapper; namespace test { @@ -37,7 +39,7 @@ namespace test { class HTTPMessage; class MockableQuicClient; -// A quic client which allows mocking out writes. +// A quic client which allows mocking out reads and writes. class MockableQuicClient : public QuicClient { public: MockableQuicClient(IPEndPoint server_address, @@ -51,7 +53,19 @@ class MockableQuicClient : public QuicClient { const QuicVersionVector& supported_versions, EpollServer* epoll_server); + MockableQuicClient(IPEndPoint server_address, + const QuicServerId& server_id, + const QuicConfig& config, + const QuicVersionVector& supported_versions, + EpollServer* epoll_server, + std::unique_ptr<ProofVerifier> proof_verifier); + ~MockableQuicClient() override; + + void ProcessPacket(const IPEndPoint& self_address, + const IPEndPoint& peer_address, + const QuicReceivedPacket& packet) override; + QuicPacketWriter* CreateQuicPacketWriter() override; QuicConnectionId GenerateNewConnectionId() override; void UseWriter(QuicPacketWriterWrapper* writer); @@ -60,11 +74,21 @@ class MockableQuicClient : public QuicClient { const CachedNetworkParameters& cached_network_params) { cached_network_paramaters_ = cached_network_params; } + const QuicReceivedPacket* last_incoming_packet() { + return last_incoming_packet_.get(); + } + void set_track_last_incoming_packet(bool track) { + track_last_incoming_packet_ = track; + } private: QuicConnectionId override_connection_id_; // ConnectionId to use, if nonzero QuicPacketWriterWrapper* test_writer_; CachedNetworkParameters cached_network_paramaters_; + // The last incoming packet, iff |track_last_incoming_packet_| is true. + std::unique_ptr<QuicReceivedPacket> last_incoming_packet_; + // If true, copy each packet from ProcessPacket into |last_incoming_packet_| + bool track_last_incoming_packet_; DISALLOW_COPY_AND_ASSIGN(MockableQuicClient); }; @@ -81,6 +105,11 @@ class QuicTestClient : public test::SimpleClient, const std::string& server_hostname, const QuicConfig& config, const QuicVersionVector& supported_versions); + QuicTestClient(IPEndPoint server_address, + const std::string& server_hostname, + const QuicConfig& config, + const QuicVersionVector& supported_versions, + std::unique_ptr<ProofVerifier> proof_verifier); ~QuicTestClient() override; @@ -111,8 +140,7 @@ class QuicTestClient : public test::SimpleClient, void Disconnect() override; IPEndPoint local_address() const override; void ClearPerRequestState() override; - void WaitForResponseForMs(int timeout_ms) override; - void WaitForInitialResponseForMs(int timeout_ms) override; + void WaitUntil(int timeout_ms, std::function<bool()> trigger) override; ssize_t Send(const void* buffer, size_t size) override; bool response_complete() const override; bool response_headers_complete() const override; @@ -157,6 +185,8 @@ class QuicTestClient : public test::SimpleClient, // ConnectionId instead of a random one. void UseConnectionId(QuicConnectionId connection_id); + // Update internal stream_ pointer and perform accompanying housekeeping. + void SetStream(QuicSpdyClientStream* stream); // Returns nullptr if the maximum number of streams have already been created. QuicSpdyClientStream* GetOrCreateStream(); @@ -202,6 +232,10 @@ class QuicTestClient : public test::SimpleClient, size_t num_responses() const { return num_responses_; } + void set_server_address(const IPEndPoint& server_address) { + client_->set_server_address(server_address); + } + // Explicitly set the SNI value for this client, overriding the default // behavior which extracts the SNI value from the request URL. void OverrideSni(const std::string& sni) { @@ -219,12 +253,12 @@ class QuicTestClient : public test::SimpleClient, private: class TestClientDataToResend : public QuicClient::QuicDataToResend { public: - TestClientDataToResend(BalsaHeaders* headers, - StringPiece body, + TestClientDataToResend(std::unique_ptr<SpdyHeaderBlock> headers, + base::StringPiece body, bool fin, QuicTestClient* test_client, QuicAckListenerInterface* delegate) - : QuicClient::QuicDataToResend(headers, body, fin), + : QuicClient::QuicDataToResend(std::move(headers), body, fin), test_client_(test_client), delegate_(delegate) {} @@ -257,6 +291,8 @@ class QuicTestClient : public test::SimpleClient, SpdyPriority priority_; std::string response_; + // bytes_read_ and bytes_written_ are updated only when stream_ is released; + // prefer bytes_read() and bytes_written() member functions. uint64_t bytes_read_; uint64_t bytes_written_; // The number of uncompressed HTTP header bytes received. diff --git a/chromium/net/tools/quic/test_tools/quic_test_server.cc b/chromium/net/tools/quic/test_tools/quic_test_server.cc index 1edf41a448f..6c400a52fe3 100644 --- a/chromium/net/tools/quic/test_tools/quic_test_server.cc +++ b/chromium/net/tools/quic/test_tools/quic_test_server.cc @@ -10,19 +10,19 @@ #include "base/threading/thread_task_runner_handle.h" #include "net/base/ip_endpoint.h" #include "net/base/net_errors.h" -#include "net/quic/crypto/crypto_handshake.h" -#include "net/quic/crypto/quic_crypto_server_config.h" -#include "net/quic/crypto/quic_random.h" -#include "net/quic/quic_chromium_connection_helper.h" -#include "net/quic/quic_config.h" -#include "net/quic/quic_connection.h" -#include "net/quic/quic_packet_writer.h" -#include "net/quic/quic_protocol.h" -#include "net/tools/quic/quic_dispatcher.h" +#include "net/quic/chromium/quic_chromium_connection_helper.h" +#include "net/quic/core/crypto/crypto_handshake.h" +#include "net/quic/core/crypto/quic_crypto_server_config.h" +#include "net/quic/core/crypto/quic_random.h" +#include "net/quic/core/quic_config.h" +#include "net/quic/core/quic_connection.h" +#include "net/quic/core/quic_packet_writer.h" +#include "net/quic/core/quic_protocol.h" #include "net/tools/quic/quic_epoll_alarm_factory.h" #include "net/tools/quic/quic_epoll_connection_helper.h" +#include "net/tools/quic/quic_simple_crypto_server_stream_helper.h" +#include "net/tools/quic/quic_simple_dispatcher.h" #include "net/tools/quic/quic_simple_server_session.h" -#include "net/tools/quic/quic_simple_server_session_helper.h" #include "net/tools/quic/quic_simple_server_stream.h" namespace net { @@ -34,7 +34,7 @@ class CustomStreamSession : public QuicSimpleServerSession { const QuicConfig& config, QuicConnection* connection, QuicServerSessionBase::Visitor* visitor, - QuicServerSessionBase::Helper* helper, + QuicCryptoServerStream::Helper* helper, const QuicCryptoServerConfig* crypto_config, QuicCompressedCertsCache* compressed_certs_cache, QuicTestServer::StreamFactory* factory, @@ -75,21 +75,21 @@ class CustomStreamSession : public QuicSimpleServerSession { QuicTestServer::CryptoStreamFactory* crypto_stream_factory_; // Not owned. }; -class QuicTestDispatcher : public QuicDispatcher { +class QuicTestDispatcher : public QuicSimpleDispatcher { public: QuicTestDispatcher( const QuicConfig& config, const QuicCryptoServerConfig* crypto_config, - const QuicVersionVector& versions, + QuicVersionManager* version_manager, std::unique_ptr<QuicConnectionHelperInterface> helper, - std::unique_ptr<QuicServerSessionBase::Helper> session_helper, + std::unique_ptr<QuicCryptoServerStream::Helper> session_helper, std::unique_ptr<QuicAlarmFactory> alarm_factory) - : QuicDispatcher(config, - crypto_config, - versions, - std::move(helper), - std::move(session_helper), - std::move(alarm_factory)), + : QuicSimpleDispatcher(config, + crypto_config, + version_manager, + std::move(helper), + std::move(session_helper), + std::move(alarm_factory)), session_factory_(nullptr), stream_factory_(nullptr), crypto_stream_factory_(nullptr) {} @@ -99,7 +99,7 @@ class QuicTestDispatcher : public QuicDispatcher { base::AutoLock lock(factory_lock_); if (session_factory_ == nullptr && stream_factory_ == nullptr && crypto_stream_factory_ == nullptr) { - return QuicDispatcher::CreateQuicSession(id, client); + return QuicSimpleDispatcher::CreateQuicSession(id, client); } QuicConnection* connection = new QuicConnection( id, client, helper(), alarm_factory(), CreatePerConnectionWriter(), @@ -149,24 +149,24 @@ class QuicTestDispatcher : public QuicDispatcher { QuicTestServer::CryptoStreamFactory* crypto_stream_factory_; // Not owned. }; -QuicTestServer::QuicTestServer(ProofSource* proof_source) - : QuicServer(proof_source) {} +QuicTestServer::QuicTestServer(std::unique_ptr<ProofSource> proof_source) + : QuicServer(std::move(proof_source)) {} -QuicTestServer::QuicTestServer(ProofSource* proof_source, +QuicTestServer::QuicTestServer(std::unique_ptr<ProofSource> proof_source, const QuicConfig& config, const QuicVersionVector& supported_versions) - : QuicServer(proof_source, + : QuicServer(std::move(proof_source), config, QuicCryptoServerConfig::ConfigOptions(), supported_versions) {} QuicDispatcher* QuicTestServer::CreateQuicDispatcher() { return new QuicTestDispatcher( - config(), &crypto_config(), supported_versions(), + config(), &crypto_config(), version_manager(), std::unique_ptr<QuicEpollConnectionHelper>(new QuicEpollConnectionHelper( epoll_server(), QuicAllocator::BUFFER_POOL)), - std::unique_ptr<QuicServerSessionBase::Helper>( - new QuicSimpleServerSessionHelper(QuicRandom::GetInstance())), + std::unique_ptr<QuicCryptoServerStream::Helper>( + new QuicSimpleCryptoServerStreamHelper(QuicRandom::GetInstance())), std::unique_ptr<QuicEpollAlarmFactory>( new QuicEpollAlarmFactory(epoll_server()))); } @@ -191,7 +191,7 @@ ImmediateGoAwaySession::ImmediateGoAwaySession( const QuicConfig& config, QuicConnection* connection, QuicServerSessionBase::Visitor* visitor, - QuicServerSessionBase::Helper* helper, + QuicCryptoServerStream::Helper* helper, const QuicCryptoServerConfig* crypto_config, QuicCompressedCertsCache* compressed_certs_cache) : QuicSimpleServerSession(config, diff --git a/chromium/net/tools/quic/test_tools/quic_test_server.h b/chromium/net/tools/quic/test_tools/quic_test_server.h index 071466853e5..75084bc0455 100644 --- a/chromium/net/tools/quic/test_tools/quic_test_server.h +++ b/chromium/net/tools/quic/test_tools/quic_test_server.h @@ -9,7 +9,7 @@ #include <string> #include "net/base/ip_endpoint.h" -#include "net/quic/quic_session.h" +#include "net/quic/core/quic_session.h" #include "net/tools/quic/quic_dispatcher.h" #include "net/tools/quic/quic_server.h" #include "net/tools/quic/quic_simple_server_session.h" @@ -35,7 +35,7 @@ class QuicTestServer : public QuicServer { const QuicConfig& config, QuicConnection* connection, QuicServerSessionBase::Visitor* visitor, - QuicServerSessionBase::Helper* helper, + QuicCryptoServerStream::Helper* helper, const QuicCryptoServerConfig* crypto_config, QuicCompressedCertsCache* compressed_certs_cache) = 0; }; @@ -60,8 +60,8 @@ class QuicTestServer : public QuicServer { QuicServerSessionBase* session) = 0; }; - explicit QuicTestServer(ProofSource* proof_source); - QuicTestServer(ProofSource* proof_source, + explicit QuicTestServer(std::unique_ptr<ProofSource> proof_source); + QuicTestServer(std::unique_ptr<ProofSource> proof_source, const QuicConfig& config, const QuicVersionVector& supported_versions); @@ -91,7 +91,7 @@ class ImmediateGoAwaySession : public QuicSimpleServerSession { ImmediateGoAwaySession(const QuicConfig& config, QuicConnection* connection, QuicServerSessionBase::Visitor* visitor, - QuicServerSessionBase::Helper* helper, + QuicCryptoServerStream::Helper* helper, const QuicCryptoServerConfig* crypto_config, QuicCompressedCertsCache* compressed_certs_cache); // Override to send GoAway. diff --git a/chromium/net/tools/quic/test_tools/server_thread.h b/chromium/net/tools/quic/test_tools/server_thread.h index b907fd16010..898679dae4c 100644 --- a/chromium/net/tools/quic/test_tools/server_thread.h +++ b/chromium/net/tools/quic/test_tools/server_thread.h @@ -10,7 +10,7 @@ #include "base/macros.h" #include "base/threading/simple_thread.h" #include "net/base/ip_endpoint.h" -#include "net/quic/quic_config.h" +#include "net/quic/core/quic_config.h" #include "net/tools/quic/quic_server.h" namespace net { diff --git a/chromium/net/tools/quic/test_tools/simple_client.cc b/chromium/net/tools/quic/test_tools/simple_client.cc index 3559da1375e..f6ac39f3c56 100644 --- a/chromium/net/tools/quic/test_tools/simple_client.cc +++ b/chromium/net/tools/quic/test_tools/simple_client.cc @@ -4,6 +4,8 @@ #include "net/tools/quic/test_tools/simple_client.h" +#include "net/tools/balsa/balsa_headers.h" + namespace net { namespace test { @@ -16,6 +18,18 @@ void SimpleClient::WaitForInitialResponse() { WaitForInitialResponseForMs(-1); } +void SimpleClient::WaitForResponseForMs(int timeout_ms) { + WaitUntil(timeout_ms, [this]() { return response_complete(); }); + if (response_complete()) { + VLOG(1) << "Client received response:" << response_headers()->DebugString() + << response_body(); + } +} + +void SimpleClient::WaitForInitialResponseForMs(int timeout_ms) { + WaitUntil(timeout_ms, [this]() { return response_size() != 0; }); +} + int SimpleClient::ResetSocket() { LOG(FATAL) << "SimpleClient::ResetSocket is not implemented"; return 0; diff --git a/chromium/net/tools/quic/test_tools/simple_client.h b/chromium/net/tools/quic/test_tools/simple_client.h index 3c5d02cfd0a..68b5c897d51 100644 --- a/chromium/net/tools/quic/test_tools/simple_client.h +++ b/chromium/net/tools/quic/test_tools/simple_client.h @@ -55,11 +55,13 @@ class SimpleClient { // Returns once a complete response or a connection close has been received // from the server, or once the timeout expires. -1 for no timeout. - virtual void WaitForResponseForMs(int timeout_ms) = 0; + virtual void WaitForResponseForMs(int timeout_ms); // Waits for some data or response from the server, or once the timeout // expires. -1 for no timeout. - virtual void WaitForInitialResponseForMs(int timeout_ms) = 0; + virtual void WaitForInitialResponseForMs(int timeout_ms); + + virtual void WaitUntil(int timeout_ms, std::function<bool()> trigger) = 0; // Clears any outstanding state from the last request. virtual void ClearPerRequestState() = 0; |