summaryrefslogtreecommitdiffstats
path: root/chromium/net/tools/quic/end_to_end_test.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/net/tools/quic/end_to_end_test.cc')
-rw-r--r--chromium/net/tools/quic/end_to_end_test.cc696
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