diff options
Diffstat (limited to 'chromium/net/tools/quic/end_to_end_test.cc')
-rw-r--r-- | chromium/net/tools/quic/end_to_end_test.cc | 696 |
1 files changed, 560 insertions, 136 deletions
diff --git a/chromium/net/tools/quic/end_to_end_test.cc b/chromium/net/tools/quic/end_to_end_test.cc index 2b94b0f2fd1..a21dc4082a3 100644 --- a/chromium/net/tools/quic/end_to_end_test.cc +++ b/chromium/net/tools/quic/end_to_end_test.cc @@ -4,30 +4,39 @@ #include <stddef.h> #include <string> +#include <sys/epoll.h> #include <vector> +#include "base/basictypes.h" #include "base/memory/scoped_ptr.h" #include "base/memory/singleton.h" #include "base/strings/string_number_conversions.h" #include "base/synchronization/waitable_event.h" +#include "base/time/time.h" #include "net/base/ip_endpoint.h" #include "net/quic/congestion_control/tcp_cubic_sender.h" #include "net/quic/crypto/aes_128_gcm_12_encrypter.h" #include "net/quic/crypto/null_encrypter.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_sent_packet_manager.h" +#include "net/quic/quic_server_id.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_session_peer.h" -#include "net/quic/test_tools/quic_test_writer.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" +#include "net/tools/epoll_server/epoll_server.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_writer_wrapper.h" #include "net/tools/quic/quic_server.h" #include "net/tools/quic/quic_socket_utils.h" #include "net/tools/quic/quic_spdy_client_stream.h" -#include "net/tools/quic/test_tools/http_message_test_utils.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/quic_client_peer.h" #include "net/tools/quic/test_tools/quic_dispatcher_peer.h" @@ -39,10 +48,14 @@ using base::StringPiece; using base::WaitableEvent; +using net::EpollServer; +using net::test::GenerateBody; using net::test::QuicConnectionPeer; +using net::test::QuicFlowControllerPeer; using net::test::QuicSessionPeer; -using net::test::QuicTestWriter; using net::test::ReliableQuicStreamPeer; +using net::test::ValueRestore; +using net::test::kClientDataStreamId1; using net::tools::test::PacketDroppingTestWriter; using net::tools::test::QuicDispatcherPeer; using net::tools::test::QuicServerPeer; @@ -58,14 +71,6 @@ namespace { const char* kFooResponseBody = "Artichoke hearts make me happy."; const char* kBarResponseBody = "Palm hearts are pretty delicious, also."; -void GenerateBody(string* body, int length) { - body->clear(); - body->reserve(length); - for (int i = 0; i < length; ++i) { - body->append(1, static_cast<char>(32 + i % (126 - 32))); - } -} - // Run all tests with the cross products of all versions. struct TestParams { TestParams(const QuicVersionVector& client_supported_versions, @@ -98,7 +103,6 @@ struct TestParams { vector<TestParams> GetTestParams() { vector<TestParams> params; QuicVersionVector all_supported_versions = QuicSupportedVersions(); - for (int use_pacing = 0; use_pacing < 2; ++use_pacing) { // Add an entry for server and client supporting all versions. params.push_back(TestParams(all_supported_versions, @@ -106,19 +110,6 @@ vector<TestParams> GetTestParams() { all_supported_versions[0], use_pacing != 0)); - // Test client supporting 1 version and server supporting all versions. - // Simulate an old client and exercise version downgrade in the server. - // No 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 < all_supported_versions.size(); ++i) { - QuicVersionVector client_supported_versions; - client_supported_versions.push_back(all_supported_versions[i]); - params.push_back(TestParams(client_supported_versions, - all_supported_versions, - client_supported_versions[0], - use_pacing != 0)); - } - // 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 @@ -126,6 +117,12 @@ vector<TestParams> GetTestParams() { for (size_t i = 1; i < all_supported_versions.size(); ++i) { QuicVersionVector server_supported_versions; server_supported_versions.push_back(all_supported_versions[i]); + if (all_supported_versions[i] >= QUIC_VERSION_17) { + // Until flow control is globally rolled out and we remove + // QUIC_VERSION_16, the server MUST support at least one QUIC version + // that does not use flow control. + server_supported_versions.push_back(QUIC_VERSION_16); + } params.push_back(TestParams(all_supported_versions, server_supported_versions, server_supported_versions[0], @@ -135,6 +132,28 @@ vector<TestParams> GetTestParams() { return params; } +class ServerDelegate : public PacketDroppingTestWriter::Delegate { + public: + explicit ServerDelegate(QuicDispatcher* dispatcher) + : dispatcher_(dispatcher) {} + virtual ~ServerDelegate() {} + virtual void OnCanWrite() OVERRIDE { dispatcher_->OnCanWrite(); } + private: + QuicDispatcher* dispatcher_; +}; + +class ClientDelegate : public PacketDroppingTestWriter::Delegate { + public: + explicit ClientDelegate(QuicClient* client) : client_(client) {} + virtual ~ClientDelegate() {} + virtual void OnCanWrite() OVERRIDE { + EpollEvent event(EPOLLOUT, false); + client_->OnEvent(client_->fd(), &event); + } + private: + QuicClient* client_; +}; + class EndToEndTest : public ::testing::TestWithParam<TestParams> { protected: EndToEndTest() @@ -148,14 +167,32 @@ class EndToEndTest : public ::testing::TestWithParam<TestParams> { client_supported_versions_ = GetParam().client_supported_versions; server_supported_versions_ = GetParam().server_supported_versions; negotiated_version_ = GetParam().negotiated_version; - FLAGS_limit_rto_increase_for_tests = true; FLAGS_enable_quic_pacing = GetParam().use_pacing; - LOG(INFO) << "Using Configuration: " << GetParam(); + + if (negotiated_version_ >= QUIC_VERSION_17) { + FLAGS_enable_quic_stream_flow_control_2 = true; + } + if (negotiated_version_ >= QUIC_VERSION_19) { + FLAGS_enable_quic_connection_flow_control_2 = true; + } + VLOG(1) << "Using Configuration: " << GetParam(); client_config_.SetDefaults(); server_config_.SetDefaults(); - server_config_.set_initial_round_trip_time_us(kMaxInitialRoundTripTimeUs, - 0); + + // Use different flow control windows for client/server. + client_config_.SetInitialFlowControlWindowToSend( + 2 * kInitialSessionFlowControlWindowForTest); + client_config_.SetInitialStreamFlowControlWindowToSend( + 2 * kInitialStreamFlowControlWindowForTest); + client_config_.SetInitialSessionFlowControlWindowToSend( + 2 * kInitialSessionFlowControlWindowForTest); + server_config_.SetInitialFlowControlWindowToSend( + 3 * kInitialSessionFlowControlWindowForTest); + server_config_.SetInitialStreamFlowControlWindowToSend( + 3 * kInitialStreamFlowControlWindowForTest); + server_config_.SetInitialSessionFlowControlWindowToSend( + 3 * kInitialSessionFlowControlWindowForTest); QuicInMemoryCachePeer::ResetForTests(); AddToCache("GET", "https://www.google.com/foo", @@ -170,54 +207,99 @@ class EndToEndTest : public ::testing::TestWithParam<TestParams> { QuicInMemoryCachePeer::ResetForTests(); } - virtual QuicTestClient* CreateQuicClient(QuicTestWriter* writer) { - QuicTestClient* client = new QuicTestClient(server_address_, - server_hostname_, - false, // not secure - client_config_, - client_supported_versions_); + QuicTestClient* CreateQuicClient(QuicPacketWriterWrapper* writer) { + QuicTestClient* client = new QuicTestClient( + server_address_, + server_hostname_, + false, // not secure + client_config_, + client_supported_versions_); client->UseWriter(writer); client->Connect(); return client; } - virtual bool Initialize() { + void set_client_initial_flow_control_receive_window(uint32 window) { + CHECK(client_.get() == NULL); + DVLOG(1) << "Setting client initial flow control window: " << window; + client_config_.SetInitialFlowControlWindowToSend(window); + } + + void set_client_initial_stream_flow_control_receive_window(uint32 window) { + CHECK(client_.get() == NULL); + DLOG(INFO) << "Setting client initial stream flow control window: " + << window; + client_config_.SetInitialStreamFlowControlWindowToSend(window); + } + + void set_client_initial_session_flow_control_receive_window(uint32 window) { + CHECK(client_.get() == NULL); + DLOG(INFO) << "Setting client initial session flow control window: " + << window; + client_config_.SetInitialSessionFlowControlWindowToSend(window); + } + + void set_server_initial_flow_control_receive_window(uint32 window) { + CHECK(server_thread_.get() == NULL); + DVLOG(1) << "Setting server initial flow control window: " << window; + server_config_.SetInitialFlowControlWindowToSend(window); + } + + void set_server_initial_stream_flow_control_receive_window(uint32 window) { + CHECK(server_thread_.get() == NULL); + DLOG(INFO) << "Setting server initial stream flow control window: " + << window; + server_config_.SetInitialStreamFlowControlWindowToSend(window); + } + + void set_server_initial_session_flow_control_receive_window(uint32 window) { + CHECK(server_thread_.get() == NULL); + DLOG(INFO) << "Setting server initial session flow control window: " + << window; + server_config_.SetInitialSessionFlowControlWindowToSend(window); + } + + bool Initialize() { // Start the server first, because CreateQuicClient() attempts // to connect to the server. StartServer(); client_.reset(CreateQuicClient(client_writer_)); - QuicEpollConnectionHelper* helper = + static EpollEvent event(EPOLLOUT, false); + client_writer_->Initialize( reinterpret_cast<QuicEpollConnectionHelper*>( QuicConnectionPeer::GetHelper( - client_->client()->session()->connection())); - client_writer_->SetConnectionHelper(helper); + client_->client()->session()->connection())), + new ClientDelegate(client_->client())); return client_->client()->connected(); } - virtual void SetUp() { - // The ownership of these gets transferred to the QuicTestWriter and - // QuicDispatcher when Initialize() is executed. + virtual void SetUp() OVERRIDE { + // The ownership of these gets transferred to the QuicPacketWriterWrapper + // and QuicDispatcher when Initialize() is executed. client_writer_ = new PacketDroppingTestWriter(); server_writer_ = new PacketDroppingTestWriter(); } - virtual void TearDown() { + virtual void TearDown() OVERRIDE { StopServer(); } void StartServer() { - server_thread_.reset(new ServerThread(server_address_, server_config_, - server_supported_versions_, - strike_register_no_startup_period_)); - server_thread_->Start(); - server_thread_->WaitForServerStartup(); + server_thread_.reset( + new ServerThread( + new QuicServer(server_config_, server_supported_versions_), + server_address_, + strike_register_no_startup_period_)); + server_thread_->Initialize(); server_address_ = IPEndPoint(server_address_.address(), server_thread_->GetPort()); QuicDispatcher* dispatcher = QuicServerPeer::GetDispatcher(server_thread_->server()); - server_writer_->SetConnectionHelper( - QuicDispatcherPeer::GetHelper(dispatcher)); QuicDispatcherPeer::UseWriter(dispatcher, server_writer_); + server_writer_->Initialize( + QuicDispatcherPeer::GetHelper(dispatcher), + new ServerDelegate(dispatcher)); + server_thread_->Start(); server_started_ = true; } @@ -243,8 +325,10 @@ class EndToEndTest : public ::testing::TestWithParam<TestParams> { void SetPacketLossPercentage(int32 loss) { // TODO(rtenneti): enable when we can do random packet loss tests in // chrome's tree. - // client_writer_->set_fake_packet_loss_percentage(loss); - // server_writer_->set_fake_packet_loss_percentage(loss); + if (loss != 0 && loss != 100) + return; + client_writer_->set_fake_packet_loss_percentage(loss); + server_writer_->set_fake_packet_loss_percentage(loss); } void SetPacketSendDelay(QuicTime::Delta delay) { @@ -261,6 +345,36 @@ class EndToEndTest : public ::testing::TestWithParam<TestParams> { // server_writer_->set_fake_reorder_percentage(reorder); } + // Verifies that the client and server connections were both free of packets + // being discarded, based on connection stats. + // Calls server_thread_ Pause() and Resume(), which may only be called once + // per test. + void VerifyCleanConnection(bool had_packet_loss) { + QuicConnectionStats client_stats = + client_->client()->session()->connection()->GetStats(); + if (!had_packet_loss) { + EXPECT_EQ(0u, client_stats.packets_lost); + } + EXPECT_EQ(0u, client_stats.packets_discarded); + EXPECT_EQ(0u, client_stats.packets_dropped); + EXPECT_EQ(client_stats.packets_received, client_stats.packets_processed); + + server_thread_->Pause(); + QuicDispatcher* dispatcher = + QuicServerPeer::GetDispatcher(server_thread_->server()); + ASSERT_EQ(1u, dispatcher->session_map().size()); + QuicSession* session = dispatcher->session_map().begin()->second; + QuicConnectionStats server_stats = session->connection()->GetStats(); + if (!had_packet_loss) { + EXPECT_EQ(0u, server_stats.packets_lost); + } + EXPECT_EQ(0u, server_stats.packets_discarded); + // TODO(ianswett): Restore the check for packets_dropped equals 0. + // The expect for packets received is equal to packets processed fails + // due to version negotiation packets. + server_thread_->Resume(); + } + IPEndPoint server_address_; string server_hostname_; scoped_ptr<ServerThread> server_thread_; @@ -398,12 +512,14 @@ TEST_P(EndToEndTest, PostMissingBytes) { EXPECT_EQ(500u, client_->response_headers()->parsed_response_code()); } -TEST_P(EndToEndTest, LargePostNoPacketLoss) { +// TODO(rtenneti): DISABLED_LargePostNoPacketLoss seems to be flaky. +// http://crbug.com/297040. +TEST_P(EndToEndTest, DISABLED_LargePostNoPacketLoss) { ASSERT_TRUE(Initialize()); client_->client()->WaitForCryptoHandshakeConfirmed(); - // 1 Mb body. + // 1 MB body. string body; GenerateBody(&body, 1024 * 1024); @@ -412,6 +528,25 @@ TEST_P(EndToEndTest, LargePostNoPacketLoss) { request.AddBody(body, true); EXPECT_EQ(kFooResponseBody, client_->SendCustomSynchronousRequest(request)); + VerifyCleanConnection(false); +} + +TEST_P(EndToEndTest, LargePostNoPacketLoss1sRTT) { + ASSERT_TRUE(Initialize()); + SetPacketSendDelay(QuicTime::Delta::FromMilliseconds(1000)); + + client_->client()->WaitForCryptoHandshakeConfirmed(); + + // 100 KB body. + string body; + GenerateBody(&body, 100 * 1024); + + HTTPMessage request(HttpConstants::HTTP_1_1, + HttpConstants::POST, "/foo"); + request.AddBody(body, true); + + EXPECT_EQ(kFooResponseBody, client_->SendCustomSynchronousRequest(request)); + VerifyCleanConnection(false); } TEST_P(EndToEndTest, LargePostWithPacketLoss) { @@ -424,7 +559,7 @@ TEST_P(EndToEndTest, LargePostWithPacketLoss) { client_->client()->WaitForCryptoHandshakeConfirmed(); SetPacketLossPercentage(30); - // 10 Kb body. + // 10 KB body. string body; GenerateBody(&body, 1024 * 10); @@ -433,19 +568,23 @@ TEST_P(EndToEndTest, LargePostWithPacketLoss) { request.AddBody(body, true); EXPECT_EQ(kFooResponseBody, client_->SendCustomSynchronousRequest(request)); + VerifyCleanConnection(true); } -TEST_P(EndToEndTest, LargePostNoPacketLossWithDelayAndReordering) { +TEST_P(EndToEndTest, LargePostWithPacketLossAndBlockedSocket) { + // Connect with lower fake packet loss than we'd like to test. Until + // b/10126687 is fixed, losing handshake packets is pretty brutal. + SetPacketLossPercentage(5); ASSERT_TRUE(Initialize()); + // Wait for the server SHLO before upping the packet loss. client_->client()->WaitForCryptoHandshakeConfirmed(); - // Both of these must be called when the writer is not actively used. - SetPacketSendDelay(QuicTime::Delta::FromMilliseconds(2)); - SetReorderPercentage(30); + SetPacketLossPercentage(10); + client_writer_->set_fake_blocked_socket_percentage(10); - // 1 Mb body. + // 10 KB body. string body; - GenerateBody(&body, 1024 * 1024); + GenerateBody(&body, 1024 * 10); HTTPMessage request(HttpConstants::HTTP_1_1, HttpConstants::POST, "/foo"); @@ -454,20 +593,17 @@ TEST_P(EndToEndTest, LargePostNoPacketLossWithDelayAndReordering) { EXPECT_EQ(kFooResponseBody, client_->SendCustomSynchronousRequest(request)); } -TEST_P(EndToEndTest, LargePostWithPacketLossAndBlocketSocket) { - // Connect with lower fake packet loss than we'd like to test. Until - // b/10126687 is fixed, losing handshake packets is pretty brutal. - SetPacketLossPercentage(5); +TEST_P(EndToEndTest, LargePostNoPacketLossWithDelayAndReordering) { ASSERT_TRUE(Initialize()); - // Wait for the server SHLO before upping the packet loss. client_->client()->WaitForCryptoHandshakeConfirmed(); - SetPacketLossPercentage(30); - client_writer_->set_fake_blocked_socket_percentage(10); + // Both of these must be called when the writer is not actively used. + SetPacketSendDelay(QuicTime::Delta::FromMilliseconds(2)); + SetReorderPercentage(30); - // 10 Kb body. + // 1 MB body. string body; - GenerateBody(&body, 1024 * 10); + GenerateBody(&body, 1024 * 1024); HTTPMessage request(HttpConstants::HTTP_1_1, HttpConstants::POST, "/foo"); @@ -500,6 +636,14 @@ TEST_P(EndToEndTest, DISABLED_LargePostZeroRTTFailure) { // The 0-RTT handshake should succeed. client_->Connect(); + if (client_supported_versions_[0] >= QUIC_VERSION_17 && + negotiated_version_ < QUIC_VERSION_17) { + // If the version negotiation has resulted in a downgrade, then the client + // must wait for the handshake to complete before sending any data. + // Otherwise it may have queued QUIC_VERSION_17 frames which will trigger a + // DFATAL when they are serialized after the downgrade. + client_->client()->WaitForCryptoHandshakeConfirmed(); + } client_->WaitForResponseForMs(-1); ASSERT_TRUE(client_->client()->connected()); EXPECT_EQ(kFooResponseBody, client_->SendCustomSynchronousRequest(request)); @@ -513,16 +657,35 @@ TEST_P(EndToEndTest, DISABLED_LargePostZeroRTTFailure) { StartServer(); client_->Connect(); + if (client_supported_versions_[0] >= QUIC_VERSION_17 && + negotiated_version_ < QUIC_VERSION_17) { + // If the version negotiation has resulted in a downgrade, then the client + // must wait for the handshake to complete before sending any data. + // Otherwise it may have queued QUIC_VERSION_17 frames which will trigger a + // DFATAL when they are serialized after the downgrade. + client_->client()->WaitForCryptoHandshakeConfirmed(); + } ASSERT_TRUE(client_->client()->connected()); EXPECT_EQ(kFooResponseBody, client_->SendCustomSynchronousRequest(request)); EXPECT_EQ(2, client_->client()->session()->GetNumSentClientHellos()); + VerifyCleanConnection(false); } -// TODO(ianswett): Enable once b/9295090 is fixed. -TEST_P(EndToEndTest, DISABLED_LargePostFEC) { - SetPacketLossPercentage(30); +TEST_P(EndToEndTest, LargePostFEC) { + // Connect without packet loss to avoid issues with losing handshake packets, + // and then up the packet loss rate (b/10126687). ASSERT_TRUE(Initialize()); - client_->options()->max_packets_per_fec_group = 6; + + // Wait for the server SHLO before upping the packet loss. + client_->client()->WaitForCryptoHandshakeConfirmed(); + SetPacketLossPercentage(30); + + // Enable FEC protection. + QuicPacketCreator* creator = QuicConnectionPeer::GetPacketCreator( + client_->client()->session()->connection()); + creator->set_max_packets_per_fec_group(3); + // Set FecPolicy to always protect data on all streams. + client_->SetFecPolicy(FEC_PROTECT_ALWAYS); string body; GenerateBody(&body, 10240); @@ -530,21 +693,23 @@ TEST_P(EndToEndTest, DISABLED_LargePostFEC) { HTTPMessage request(HttpConstants::HTTP_1_1, HttpConstants::POST, "/foo"); request.AddBody(body, true); - EXPECT_EQ(kFooResponseBody, client_->SendCustomSynchronousRequest(request)); + VerifyCleanConnection(true); } -TEST_P(EndToEndTest, LargePostLargeBuffer) { +// TODO(shess): This is flaky on ChromiumOS bots. +// http://crbug.com/374871 +TEST_P(EndToEndTest, DISABLED_LargePostSmallBandwidthLargeBuffer) { ASSERT_TRUE(Initialize()); SetPacketSendDelay(QuicTime::Delta::FromMicroseconds(1)); - // 1Mbit per second with a 128k buffer from server to client. Wireless + // 256KB per second with a 256KB buffer from server to client. Wireless // clients commonly have larger buffers, but our max CWND is 200. server_writer_->set_max_bandwidth_and_buffer_size( - QuicBandwidth::FromBytesPerSecond(256 * 1024), 128 * 1024); + QuicBandwidth::FromBytesPerSecond(256 * 1024), 256 * 1024); client_->client()->WaitForCryptoHandshakeConfirmed(); - // 1 Mb body. + // 1 MB body. string body; GenerateBody(&body, 1024 * 1024); @@ -553,6 +718,52 @@ TEST_P(EndToEndTest, LargePostLargeBuffer) { request.AddBody(body, true); EXPECT_EQ(kFooResponseBody, client_->SendCustomSynchronousRequest(request)); + // This connection will not drop packets, because the buffer size is larger + // than the default receive window. + VerifyCleanConnection(false); +} + +TEST_P(EndToEndTest, DoNotSetResumeWriteAlarmIfConnectionFlowControlBlocked) { + // Regression test for b/14677858. + // Test that the resume write alarm is not set in QuicConnection::OnCanWrite + // if currently connection level flow control blocked. If set, this results in + // an infinite loop in the EpollServer, as the alarm fires and is immediately + // rescheduled. + ASSERT_TRUE(Initialize()); + if (negotiated_version_ < QUIC_VERSION_19) { + return; + } + client_->client()->WaitForCryptoHandshakeConfirmed(); + + // Ensure both stream and connection level are flow control blocked by setting + // the send window offset to 0. + const uint64 kFlowControlWindow = + server_config_.GetInitialFlowControlWindowToSend(); + QuicSpdyClientStream* stream = client_->GetOrCreateStream(); + QuicSession* session = client_->client()->session(); + QuicFlowControllerPeer::SetSendWindowOffset(stream->flow_controller(), 0); + QuicFlowControllerPeer::SetSendWindowOffset(session->flow_controller(), 0); + EXPECT_TRUE(stream->flow_controller()->IsBlocked()); + EXPECT_TRUE(session->flow_controller()->IsBlocked()); + + // Make sure that the stream has data pending so that it will be marked as + // write blocked when it receives a stream level WINDOW_UPDATE. + stream->SendBody("hello", false); + + // The stream now attempts to write, fails because it is still connection + // level flow control blocked, and is added to the write blocked list. + QuicWindowUpdateFrame window_update(stream->id(), 2 * kFlowControlWindow); + stream->OnWindowUpdateFrame(window_update); + + // Prior to fixing b/14677858 this call would result in an infinite loop in + // Chromium. As a proxy for detecting this, we now check whether the + // resume_writes_alarm is set after OnCanWrite. It should not be, as the + // connection is still flow control blocked. + session->connection()->OnCanWrite(); + + QuicAlarm* resume_writes_alarm = + QuicConnectionPeer::GetResumeWritesAlarm(session->connection()); + EXPECT_FALSE(resume_writes_alarm->IsSet()); } TEST_P(EndToEndTest, InvalidStream) { @@ -570,7 +781,7 @@ TEST_P(EndToEndTest, InvalidStream) { QuicSessionPeer::SetNextStreamId(client_->client()->session(), 2); client_->SendCustomSynchronousRequest(request); -// EXPECT_EQ(QUIC_STREAM_CONNECTION_ERROR, client_->stream_error()); + // EXPECT_EQ(QUIC_STREAM_CONNECTION_ERROR, client_->stream_error()); EXPECT_EQ(QUIC_PACKET_FOR_NONEXISTENT_STREAM, client_->connection_error()); } @@ -598,23 +809,7 @@ TEST_P(EndToEndTest, DISABLED_MultipleTermination) { ReliableQuicStreamPeer::SetWriteSideClosed( false, client_->GetOrCreateStream()); -#if !defined(WIN32) && defined(GTEST_HAS_DEATH_TEST) -#if !defined(DCHECK_ALWAYS_ON) - EXPECT_DEBUG_DEATH({ - client_->SendData("eep", true); - client_->WaitForResponse(); - EXPECT_EQ(QUIC_MULTIPLE_TERMINATION_OFFSETS, client_->stream_error()); - }, - "Check failed: !fin_buffered_"); -#else - EXPECT_DEATH({ - client_->SendData("eep", true); - client_->WaitForResponse(); - EXPECT_EQ(QUIC_MULTIPLE_TERMINATION_OFFSETS, client_->stream_error()); - }, - "Check failed: !fin_buffered_"); -#endif -#endif + EXPECT_DFATAL(client_->SendData("eep", true), "Fin already buffered"); } TEST_P(EndToEndTest, Timeout) { @@ -629,6 +824,30 @@ TEST_P(EndToEndTest, Timeout) { } } +TEST_P(EndToEndTest, NegotiateMaxOpenStreams) { + // Negotiate 1 max open stream. + client_config_.set_max_streams_per_connection(1, 1); + ASSERT_TRUE(Initialize()); + client_->client()->WaitForCryptoHandshakeConfirmed(); + + // Make the client misbehave after negotiation. + QuicSessionPeer::SetMaxOpenStreams(client_->client()->session(), 10); + + HTTPMessage request(HttpConstants::HTTP_1_1, + HttpConstants::POST, "/foo"); + request.AddHeader("content-length", "3"); + request.set_has_complete_message(false); + + // Open two simultaneous streams. + client_->SendMessage(request); + client_->SendMessage(request); + client_->WaitForResponse(); + + EXPECT_FALSE(client_->connected()); + EXPECT_EQ(QUIC_STREAM_CONNECTION_ERROR, client_->stream_error()); + EXPECT_EQ(QUIC_TOO_MANY_OPEN_STREAMS, client_->connection_error()); +} + TEST_P(EndToEndTest, LimitMaxOpenStreams) { // Server limits the number of max streams to 2. server_config_.set_max_streams_per_connection(2, 2); @@ -644,13 +863,10 @@ TEST_P(EndToEndTest, LimitMaxOpenStreams) { // TODO(rtenneti): DISABLED_LimitCongestionWindowAndRTT seems to be flaky. // http://crbug.com/321870. TEST_P(EndToEndTest, DISABLED_LimitCongestionWindowAndRTT) { - server_config_.set_server_initial_congestion_window(kMaxInitialWindow, - kDefaultInitialWindow); - // Client tries to negotiate twice the server's max and negotiation settles - // on the max. - client_config_.set_server_initial_congestion_window(2 * kMaxInitialWindow, - kDefaultInitialWindow); - client_config_.set_initial_round_trip_time_us(1, 1); + // Client tries to request twice the server's max initial window, and the + // server limits it to the max. + client_config_.SetInitialCongestionWindowToSend(2 * kMaxInitialWindow); + client_config_.SetInitialRoundTripTimeUsToSend(1); ASSERT_TRUE(Initialize()); client_->client()->WaitForCryptoHandshakeConfirmed(); @@ -662,17 +878,11 @@ TEST_P(EndToEndTest, DISABLED_LimitCongestionWindowAndRTT) { QuicServerPeer::GetDispatcher(server_thread_->server()); ASSERT_EQ(1u, dispatcher->session_map().size()); QuicSession* session = dispatcher->session_map().begin()->second; - QuicConfig* client_negotiated_config = client_->client()->session()->config(); - QuicConfig* server_negotiated_config = session->config(); const QuicSentPacketManager& client_sent_packet_manager = client_->client()->session()->connection()->sent_packet_manager(); const QuicSentPacketManager& server_sent_packet_manager = session->connection()->sent_packet_manager(); - EXPECT_EQ(kMaxInitialWindow, - client_negotiated_config->server_initial_congestion_window()); - EXPECT_EQ(kMaxInitialWindow, - server_negotiated_config->server_initial_congestion_window()); // The client shouldn't set it's initial window based on the negotiated value. EXPECT_EQ(kDefaultInitialWindow * kDefaultTCPMSS, client_sent_packet_manager.GetCongestionWindow()); @@ -684,13 +894,14 @@ TEST_P(EndToEndTest, DISABLED_LimitCongestionWindowAndRTT) { EXPECT_EQ(FLAGS_enable_quic_pacing, client_sent_packet_manager.using_pacing()); - EXPECT_EQ(1u, client_negotiated_config->initial_round_trip_time_us()); - EXPECT_EQ(1u, server_negotiated_config->initial_round_trip_time_us()); + EXPECT_EQ(100000u, + client_sent_packet_manager.GetRttStats()->initial_rtt_us()); + EXPECT_EQ(1u, server_sent_packet_manager.GetRttStats()->initial_rtt_us()); // Now use the negotiated limits with packet loss. SetPacketLossPercentage(30); - // 10 Kb body. + // 10 KB body. string body; GenerateBody(&body, 1024 * 10); @@ -703,11 +914,42 @@ TEST_P(EndToEndTest, DISABLED_LimitCongestionWindowAndRTT) { EXPECT_EQ(kFooResponseBody, client_->SendCustomSynchronousRequest(request)); } -TEST_P(EndToEndTest, InitialRTT) { - // Client tries to negotiate twice the server's max and negotiation settles - // on the max. - client_config_.set_initial_round_trip_time_us(2 * kMaxInitialRoundTripTimeUs, - 0); +TEST_P(EndToEndTest, MaxInitialRTT) { + // Client tries to suggest twice the server's max initial rtt and the server + // uses the max. + client_config_.SetInitialRoundTripTimeUsToSend( + 2 * kMaxInitialRoundTripTimeUs); + + ASSERT_TRUE(Initialize()); + client_->client()->WaitForCryptoHandshakeConfirmed(); + server_thread_->WaitForCryptoHandshakeConfirmed(); + + // Pause the server so we can access the server's internals without races. + server_thread_->Pause(); + QuicDispatcher* dispatcher = + QuicServerPeer::GetDispatcher(server_thread_->server()); + ASSERT_EQ(1u, dispatcher->session_map().size()); + QuicSession* session = dispatcher->session_map().begin()->second; + const QuicSentPacketManager& client_sent_packet_manager = + client_->client()->session()->connection()->sent_packet_manager(); + const QuicSentPacketManager& server_sent_packet_manager = + session->connection()->sent_packet_manager(); + + // Now that acks have been exchanged, the RTT estimate has decreased on the + // server and is not infinite on the client. + EXPECT_FALSE( + client_sent_packet_manager.GetRttStats()->SmoothedRtt().IsInfinite()); + EXPECT_EQ(static_cast<int64>(kMaxInitialRoundTripTimeUs), + server_sent_packet_manager.GetRttStats()->initial_rtt_us()); + EXPECT_GE( + static_cast<int64>(kMaxInitialRoundTripTimeUs), + server_sent_packet_manager.GetRttStats()->SmoothedRtt().ToMicroseconds()); + server_thread_->Resume(); +} + +TEST_P(EndToEndTest, MinInitialRTT) { + // Client tries to suggest 0 and the server uses the default. + client_config_.SetInitialRoundTripTimeUsToSend(0); ASSERT_TRUE(Initialize()); client_->client()->WaitForCryptoHandshakeConfirmed(); @@ -719,22 +961,22 @@ TEST_P(EndToEndTest, InitialRTT) { QuicServerPeer::GetDispatcher(server_thread_->server()); ASSERT_EQ(1u, dispatcher->session_map().size()); QuicSession* session = dispatcher->session_map().begin()->second; - QuicConfig* client_negotiated_config = client_->client()->session()->config(); - QuicConfig* server_negotiated_config = session->config(); const QuicSentPacketManager& client_sent_packet_manager = client_->client()->session()->connection()->sent_packet_manager(); const QuicSentPacketManager& server_sent_packet_manager = session->connection()->sent_packet_manager(); - EXPECT_EQ(kMaxInitialRoundTripTimeUs, - client_negotiated_config->initial_round_trip_time_us()); - EXPECT_EQ(kMaxInitialRoundTripTimeUs, - server_negotiated_config->initial_round_trip_time_us()); // Now that acks have been exchanged, the RTT estimate has decreased on the // server and is not infinite on the client. - EXPECT_FALSE(client_sent_packet_manager.SmoothedRtt().IsInfinite()); - EXPECT_GE(static_cast<int64>(kMaxInitialRoundTripTimeUs), - server_sent_packet_manager.SmoothedRtt().ToMicroseconds()); + EXPECT_FALSE( + client_sent_packet_manager.GetRttStats()->SmoothedRtt().IsInfinite()); + // Expect the default rtt of 100ms. + EXPECT_EQ(static_cast<int64>(100 * base::Time::kMicrosecondsPerMillisecond), + server_sent_packet_manager.GetRttStats()->initial_rtt_us()); + // Ensure the bandwidth is valid. + client_sent_packet_manager.BandwidthEstimate(); + server_sent_packet_manager.BandwidthEstimate(); + server_thread_->Resume(); } TEST_P(EndToEndTest, ResetConnection) { @@ -770,7 +1012,35 @@ TEST_P(EndToEndTest, MaxStreamsUberTest) { } } -class WrongAddressWriter : public QuicTestWriter { +TEST_P(EndToEndTest, StreamCancelErrorTest) { + ASSERT_TRUE(Initialize()); + string small_body; + GenerateBody(&small_body, 256); + + AddToCache("GET", "/small_response", "HTTP/1.1", "200", "OK", small_body); + + client_->client()->WaitForCryptoHandshakeConfirmed(); + + QuicSession* session = client_->client()->session(); + // Lose the request. + SetPacketLossPercentage(100); + EXPECT_LT(0, client_->SendRequest("/small_response")); + client_->client()->WaitForEvents(); + // Transmit the cancel, and ensure the connection is torn down properly. + SetPacketLossPercentage(0); + QuicStreamId stream_id = kClientDataStreamId1; + session->SendRstStream(stream_id, QUIC_STREAM_CANCELLED, 0); + + // WaitForEvents waits 50ms and returns true if there are outstanding + // requests. + while (client_->client()->WaitForEvents() == true) { + } + // It should be completely fine to RST a stream before any data has been + // received for that stream. + EXPECT_EQ(QUIC_NO_ERROR, client_->connection_error()); +} + +class WrongAddressWriter : public QuicPacketWriterWrapper { public: WrongAddressWriter() { IPAddressNumber ip; @@ -779,12 +1049,13 @@ class WrongAddressWriter : public QuicTestWriter { } virtual WriteResult WritePacket( - const char* buffer, size_t buf_len, + const char* buffer, + size_t buf_len, const IPAddressNumber& real_self_address, - const IPEndPoint& peer_address, - QuicBlockedWriterInterface* blocked_writer) OVERRIDE { - return writer()->WritePacket(buffer, buf_len, self_address_.address(), - peer_address, blocked_writer); + const IPEndPoint& peer_address) OVERRIDE { + // Use wrong address! + return QuicPacketWriterWrapper::WritePacket( + buffer, buf_len, self_address_.address(), peer_address); } virtual bool IsWriteBlockedDataBuffered() const OVERRIDE { @@ -794,7 +1065,10 @@ class WrongAddressWriter : public QuicTestWriter { IPEndPoint self_address_; }; -TEST_P(EndToEndTest, ConnectionMigration) { +TEST_P(EndToEndTest, ConnectionMigrationClientIPChanged) { + // Tests that the client's IP can not change during an established QUIC + // connection. If it changes, the connection is closed by the server as we do + // not yet support IP migration. ASSERT_TRUE(Initialize()); EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); @@ -802,8 +1076,7 @@ TEST_P(EndToEndTest, ConnectionMigration) { scoped_ptr<WrongAddressWriter> writer(new WrongAddressWriter()); - writer->set_writer(new QuicDefaultPacketWriter( - QuicClientPeer::GetFd(client_->client()))); + writer->set_writer(new QuicDefaultPacketWriter(client_->client()->fd())); QuicConnectionPeer::SetWriter(client_->client()->session()->connection(), writer.get()); @@ -813,6 +1086,157 @@ TEST_P(EndToEndTest, ConnectionMigration) { EXPECT_EQ(QUIC_ERROR_MIGRATING_ADDRESS, client_->connection_error()); } +TEST_P(EndToEndTest, ConnectionMigrationClientPortChanged) { + // Tests that the client's port can change during an established QUIC + // connection, and that doing so does not result in the connection being + // closed by the server. + FLAGS_quic_allow_port_migration = true; + + ASSERT_TRUE(Initialize()); + + EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); + EXPECT_EQ(200u, client_->response_headers()->parsed_response_code()); + + // Store the client address which was used to send the first request. + IPEndPoint old_address = client_->client()->client_address(); + + // Stop listening on the old FD. + EpollServer* eps = client_->epoll_server(); + int old_fd = client_->client()->fd(); + eps->UnregisterFD(old_fd); + // Create a new socket before closing the old one, which will result in a new + // ephemeral port. + QuicClientPeer::CreateUDPSocket(client_->client()); + close(old_fd); + + // The packet writer needs to be updated to use the new FD. + client_->client()->CreateQuicPacketWriter(); + + // Change the internal state of the client and connection to use the new port, + // this is done because in a real NAT rebinding the client wouldn't see any + // port change, and so expects no change to incoming port. + // This is kind of ugly, but needed as we are simply swapping out the client + // FD rather than any more complex NAT rebinding simulation. + int new_port = client_->client()->client_address().port(); + QuicClientPeer::SetClientPort(client_->client(), new_port); + QuicConnectionPeer::SetSelfAddress( + client_->client()->session()->connection(), + IPEndPoint( + client_->client()->session()->connection()->self_address().address(), + new_port)); + + // Register the new FD for epoll events. + int new_fd = client_->client()->fd(); + eps->RegisterFD(new_fd, client_->client(), EPOLLIN | EPOLLOUT | EPOLLET); + + // Send a second request, using the new FD. + EXPECT_EQ(kBarResponseBody, client_->SendSynchronousRequest("/bar")); + EXPECT_EQ(200u, client_->response_headers()->parsed_response_code()); + + // Verify that the client's ephemeral port is different. + IPEndPoint new_address = client_->client()->client_address(); + EXPECT_EQ(old_address.address(), new_address.address()); + EXPECT_NE(old_address.port(), new_address.port()); +} + + +TEST_P(EndToEndTest, DifferentFlowControlWindowsQ019) { + // TODO(rjshade): Remove this test when removing QUIC_VERSION_19. + // Client and server can set different initial flow control receive windows. + // These are sent in CHLO/SHLO. Tests that these values are exchanged properly + // in the crypto handshake. + + const uint32 kClientIFCW = 123456; + set_client_initial_flow_control_receive_window(kClientIFCW); + + const uint32 kServerIFCW = 654321; + set_server_initial_flow_control_receive_window(kServerIFCW); + + ASSERT_TRUE(Initialize()); + if (negotiated_version_ > QUIC_VERSION_19) { + return; + } + + // Values are exchanged during crypto handshake, so wait for that to finish. + client_->client()->WaitForCryptoHandshakeConfirmed(); + server_thread_->WaitForCryptoHandshakeConfirmed(); + + // Client should have the right value for server's receive window. + EXPECT_EQ(kServerIFCW, client_->client() + ->session() + ->config() + ->ReceivedInitialFlowControlWindowBytes()); + + // Server should have the right value for client's receive window. + server_thread_->Pause(); + QuicDispatcher* dispatcher = + QuicServerPeer::GetDispatcher(server_thread_->server()); + QuicSession* session = dispatcher->session_map().begin()->second; + EXPECT_EQ(kClientIFCW, + session->config()->ReceivedInitialFlowControlWindowBytes()); + server_thread_->Resume(); +} + +TEST_P(EndToEndTest, DifferentFlowControlWindowsQ020) { + // TODO(rjshade): Rename to DifferentFlowControlWindows when removing + // QUIC_VERSION_19. + // Client and server can set different initial flow control receive windows. + // These are sent in CHLO/SHLO. Tests that these values are exchanged properly + // in the crypto handshake. + const uint32 kClientStreamIFCW = 123456; + const uint32 kClientSessionIFCW = 234567; + set_client_initial_stream_flow_control_receive_window(kClientStreamIFCW); + set_client_initial_session_flow_control_receive_window(kClientSessionIFCW); + + const uint32 kServerStreamIFCW = 654321; + const uint32 kServerSessionIFCW = 765432; + set_server_initial_stream_flow_control_receive_window(kServerStreamIFCW); + set_server_initial_session_flow_control_receive_window(kServerSessionIFCW); + + ASSERT_TRUE(Initialize()); + if (negotiated_version_ <= QUIC_VERSION_19) { + return; + } + + // Values are exchanged during crypto handshake, so wait for that to finish. + client_->client()->WaitForCryptoHandshakeConfirmed(); + server_thread_->WaitForCryptoHandshakeConfirmed(); + + // Open a data stream to make sure the stream level flow control is updated. + QuicSpdyClientStream* stream = client_->GetOrCreateStream(); + stream->SendBody("hello", false); + + // Client should have the right values for server's receive window. + EXPECT_EQ(kServerStreamIFCW, + client_->client() + ->session() + ->config() + ->ReceivedInitialStreamFlowControlWindowBytes()); + EXPECT_EQ(kServerSessionIFCW, + client_->client() + ->session() + ->config() + ->ReceivedInitialSessionFlowControlWindowBytes()); + EXPECT_EQ(kServerStreamIFCW, QuicFlowControllerPeer::SendWindowOffset( + stream->flow_controller())); + EXPECT_EQ(kServerSessionIFCW, + QuicFlowControllerPeer::SendWindowOffset( + client_->client()->session()->flow_controller())); + + // Server should have the right values for client's receive window. + server_thread_->Pause(); + QuicDispatcher* dispatcher = + QuicServerPeer::GetDispatcher(server_thread_->server()); + QuicSession* session = dispatcher->session_map().begin()->second; + EXPECT_EQ(kClientStreamIFCW, + session->config()->ReceivedInitialStreamFlowControlWindowBytes()); + EXPECT_EQ(kClientSessionIFCW, + session->config()->ReceivedInitialSessionFlowControlWindowBytes()); + EXPECT_EQ(kClientSessionIFCW, QuicFlowControllerPeer::SendWindowOffset( + session->flow_controller())); + server_thread_->Resume(); +} + } // namespace } // namespace test } // namespace tools |