diff options
author | rch@chromium.org <rch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-11-14 07:28:24 +0000 |
---|---|---|
committer | rch@chromium.org <rch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-11-14 07:28:24 +0000 |
commit | acf2474114d7d07c35553a23f70212b66bcdaab7 (patch) | |
tree | 6c64e793052be8ace63f444ece3f82e82ddb36b8 /net | |
parent | 79fbdab0cd96515427b35140e4fdb342824d2d80 (diff) | |
download | chromium_src-acf2474114d7d07c35553a23f70212b66bcdaab7.zip chromium_src-acf2474114d7d07c35553a23f70212b66bcdaab7.tar.gz chromium_src-acf2474114d7d07c35553a23f70212b66bcdaab7.tar.bz2 |
Implement QuicConnectionHelper::WritePacketToWire()
Review URL: https://chromiumcodereview.appspot.com/11312211
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@167621 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r-- | net/quic/quic_connection.cc | 5 | ||||
-rw-r--r-- | net/quic/quic_connection_helper.cc | 14 | ||||
-rw-r--r-- | net/quic/quic_connection_helper.h | 2 | ||||
-rw-r--r-- | net/quic/quic_connection_helper_test.cc | 260 | ||||
-rw-r--r-- | net/quic/quic_connection_test.cc | 6 |
5 files changed, 200 insertions, 87 deletions
diff --git a/net/quic/quic_connection.cc b/net/quic/quic_connection.cc index 2113ba5..9905fec 100644 --- a/net/quic/quic_connection.cc +++ b/net/quic/quic_connection.cc @@ -487,8 +487,13 @@ bool QuicConnection::SendPacket(QuicPacketSequenceNumber sequence_number, if (rv == -1) { if (error == ERR_IO_PENDING) { write_blocked_ = true; + + // TODO(rch): uncomment when we get non-blocking (and non-retrying) + // UDP sockets. + /* queued_packets_.push_front( QueuedPacket(sequence_number, packet, should_resend, is_retransmit)); + */ return false; } } diff --git a/net/quic/quic_connection_helper.cc b/net/quic/quic_connection_helper.cc index 4623c82..afa3590 100644 --- a/net/quic/quic_connection_helper.cc +++ b/net/quic/quic_connection_helper.cc @@ -8,6 +8,7 @@ #include "base/logging.h" #include "base/task_runner.h" #include "base/time.h" +#include "net/base/io_buffer.h" #include "net/quic/congestion_control/quic_receipt_metrics_collector.h" #include "net/quic/congestion_control/quic_send_scheduler.h" #include "net/quic/quic_utils.h" @@ -45,8 +46,12 @@ int QuicConnectionHelper::WritePacketToWire( return packet.length(); } - // TODO(rch): add udp socket write - return packet.length(); + scoped_refptr<StringIOBuffer> buf( + new StringIOBuffer(std::string(packet.data(), + packet.length()))); + return socket_->Write(buf, packet.length(), + base::Bind(&QuicConnectionHelper::OnWriteComplete, + weak_factory_.GetWeakPtr())); } void QuicConnectionHelper::SetResendAlarm( @@ -105,4 +110,9 @@ void QuicConnectionHelper::OnTimeoutAlarm() { connection_->CheckForTimeout(); } +void QuicConnectionHelper::OnWriteComplete(int result) { + // TODO(rch): Inform the connection about the result. + connection_->OnCanWrite(); +} + } // namespace net diff --git a/net/quic/quic_connection_helper.h b/net/quic/quic_connection_helper.h index b00d091..022f090 100644 --- a/net/quic/quic_connection_helper.h +++ b/net/quic/quic_connection_helper.h @@ -55,6 +55,8 @@ class NET_EXPORT_PRIVATE QuicConnectionHelper void OnSendAlarm(); // An alarm which fires when the connection may have timed out. void OnTimeoutAlarm(); + // A completion callback invoked when a write completes. + void OnWriteComplete(int result); private: friend class QuicConnectionHelperPeer; diff --git a/net/quic/quic_connection_helper_test.cc b/net/quic/quic_connection_helper_test.cc index de72e38..c8d4327 100644 --- a/net/quic/quic_connection_helper_test.cc +++ b/net/quic/quic_connection_helper_test.cc @@ -6,7 +6,6 @@ #include <vector> -#include "base/stl_util.h" #include "net/base/net_errors.h" #include "net/quic/test_tools/mock_clock.h" #include "net/quic/test_tools/quic_test_utils.h" @@ -15,7 +14,6 @@ #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" -using std::map; using testing::_; namespace net { @@ -34,7 +32,7 @@ class QuicConnectionPeer { namespace test { -const char data1[] = "foo"; +const char kData[] = "foo"; class TestConnection : public QuicConnection { public: @@ -51,96 +49,154 @@ class TestConnection : public QuicConnection { void SetScheduler(QuicSendScheduler* scheduler) { QuicConnectionPeer::SetScheduler(this, scheduler); } +}; + +class QuicConnectionHelperTest : public ::testing::Test { + protected: + // Holds a packet to be written to the wire, and the IO mode that should + // be used by the mock socket when performing the write. + struct PacketToWrite { + PacketToWrite(IoMode mode, QuicEncryptedPacket* packet) + : mode(mode), + packet(packet) { + } + IoMode mode; + QuicEncryptedPacket* packet; + }; - bool SendPacket(QuicPacketSequenceNumber sequence_number, - QuicPacket* packet, - bool should_resend, - bool force, - bool is_retransmit) { - return QuicConnection::SendPacket( - sequence_number, packet, should_resend, force, is_retransmit); + QuicConnectionHelperTest() + : guid_(2), + framer_(QuicDecrypter::Create(kNULL), QuicEncrypter::Create(kNULL)), + creator_(guid_, &framer_), + net_log_(BoundNetLog()), + frame_(1, false, 0, kData) { + Initialize(); } -}; -class TestConnectionHelper : public QuicConnectionHelper { - public: - TestConnectionHelper(base::TaskRunner* runner, - QuicClock* clock, - MockUDPClientSocket* socket) - : QuicConnectionHelper(runner, clock, socket) { + ~QuicConnectionHelperTest() { + for (size_t i = 0; i < writes_.size(); i++) { + delete writes_[i].packet; + } } - virtual ~TestConnectionHelper() {} + // Adds a packet to the list of expected writes. + void AddWrite(IoMode mode, QuicEncryptedPacket* packet) { + writes_.push_back(PacketToWrite(mode, packet)); + } - virtual int WritePacketToWire(const QuicEncryptedPacket& packet, - int* error) { - QuicFramer framer(QuicDecrypter::Create(kNULL), - QuicEncrypter::Create(kNULL)); - FramerVisitorCapturingAcks visitor; - framer.set_visitor(&visitor); - EXPECT_TRUE(framer.ProcessPacket(IPEndPoint(), IPEndPoint(), packet)); - header_ = *visitor.header(); - return packet.length(); + // Returns the packet to be written at position |pos|. + QuicEncryptedPacket* GetWrite(size_t pos) { + return writes_[pos].packet; } - QuicPacketHeader* header() { return &header_; } + bool AtEof() { + return socket_data_->at_read_eof() && socket_data_->at_write_eof(); + } - private: - QuicPacketHeader header_; -}; + // Configures the test fixture to use the list of expected writes. + void Initialize() { + mock_writes_.reset(new MockWrite[writes_.size()]); + for (size_t i = 0; i < writes_.size(); i++) { + mock_writes_[i] = MockWrite(writes_[i].mode, + writes_[i].packet->data(), + writes_[i].packet->length()); + }; + + socket_data_.reset(new StaticSocketDataProvider(NULL, 0, mock_writes_.get(), + writes_.size())); + + socket_.reset(new MockUDPClientSocket(socket_data_.get(), + net_log_.net_log())); + socket_->Connect(IPEndPoint()); + runner_ = new TestTaskRunner(&clock_); + helper_ = new QuicConnectionHelper(runner_.get(), &clock_, socket_.get()); + scheduler_ = new MockScheduler(); + EXPECT_CALL(*scheduler_, TimeUntilSend(_)). + WillRepeatedly(testing::Return(QuicTime::Delta())); + connection_.reset(new TestConnection(guid_, IPEndPoint(), helper_)); + connection_->set_visitor(&visitor_); + connection_->SetScheduler(scheduler_); + } -class QuicConnectionHelperTest : public ::testing::Test { - protected: - QuicConnectionHelperTest() - : guid_(0), - framer_(QuicDecrypter::Create(kNULL), QuicEncrypter::Create(kNULL)), - creator_(guid_, &framer_), - net_log_(BoundNetLog()), - scheduler_(new MockScheduler()), - socket_(&empty_data_, net_log_.net_log()), - runner_(new TestTaskRunner(&clock_)), - helper_(new TestConnectionHelper(runner_.get(), &clock_, &socket_)), - connection_(guid_, IPEndPoint(), helper_), - frame1_(1, false, 0, data1) { - connection_.set_visitor(&visitor_); - connection_.SetScheduler(scheduler_); - EXPECT_CALL(*scheduler_, TimeUntilSend(_)).WillRepeatedly(testing::Return( - QuicTime::Delta())); - } - - QuicPacket* ConstructDataPacket(QuicPacketSequenceNumber number, - QuicFecGroupNumber fec_group) { + // Returns a newly created packet to send kData on stream 1. + QuicEncryptedPacket* ConstructDataPacket( + QuicPacketSequenceNumber sequence_number) { + InitializeHeader(sequence_number); + + return ConstructPacket(header_, QuicFrame(&frame_)); + } + + // Returns a newly created packet to send ack data. + QuicEncryptedPacket* ConstructAckPacket( + QuicPacketSequenceNumber sequence_number) { + InitializeHeader(sequence_number); + + QuicAckFrame ack(0, QuicTime(), 0); + ack.congestion_info.type = kFixRate; + ack.congestion_info.fix_rate.bitrate_in_bytes_per_second = 100000; + + return ConstructPacket(header_, QuicFrame(&ack)); + } + + // Returns a newly created packet to send a connection close frame. + QuicEncryptedPacket* ConstructClosePacket( + QuicPacketSequenceNumber sequence_number, + bool with_ack) { + InitializeHeader(sequence_number); + + QuicFrames frames; + QuicAckFrame ack(0, QuicTime(), 0); + if (with_ack) { + ack.congestion_info.type = kFixRate; + ack.congestion_info.fix_rate.bitrate_in_bytes_per_second = 100000; + } else { + ack.congestion_info.type = kNone; + } + QuicConnectionCloseFrame close; + close.error_code = QUIC_CONNECTION_TIMED_OUT; + close.ack_frame = ack; + + return ConstructPacket(header_, QuicFrame(&close)); + } + + MockScheduler* scheduler_; + scoped_refptr<TestTaskRunner> runner_; + QuicConnectionHelper* helper_; + scoped_array<MockWrite> mock_writes_; + MockClock clock_; + scoped_ptr<TestConnection> connection_; + testing::StrictMock<MockConnectionVisitor> visitor_; + + private: + void InitializeHeader(QuicPacketSequenceNumber sequence_number) { header_.guid = guid_; - header_.packet_sequence_number = number; + header_.packet_sequence_number = sequence_number; header_.flags = PACKET_FLAGS_NONE; - header_.fec_group = fec_group; + header_.fec_group = 0; + } + QuicEncryptedPacket* ConstructPacket(const QuicPacketHeader& header, + const QuicFrame& frame) { QuicFrames frames; - QuicFrame frame(&frame1_); frames.push_back(frame); QuicPacket* packet; framer_.ConstructFrameDataPacket(header_, frames, &packet); - return packet; + QuicEncryptedPacket* encrypted = framer_.EncryptPacket(*packet); + delete packet; + return encrypted; } QuicGuid guid_; QuicFramer framer_; QuicPacketCreator creator_; QuicPacketHeader header_; - BoundNetLog net_log_; - StaticSocketDataProvider empty_data_; - MockScheduler* scheduler_; - MockUDPClientSocket socket_; - scoped_refptr<TestTaskRunner> runner_; - MockClock clock_; - TestConnectionHelper* helper_; - TestConnection connection_; - QuicStreamFrame frame1_; - testing::StrictMock<MockConnectionVisitor> visitor_; + QuicStreamFrame frame_; + scoped_ptr<MockUDPClientSocket> socket_; + scoped_ptr<StaticSocketDataProvider> socket_data_; + std::vector<PacketToWrite> writes_; }; - TEST_F(QuicConnectionHelperTest, GetClock) { EXPECT_EQ(&clock_, helper_->GetClock()); } @@ -161,19 +217,27 @@ TEST_F(QuicConnectionHelperTest, UnregisterSendAlarmIfRegistered) { } TEST_F(QuicConnectionHelperTest, TestResend) { - //FLAGS_fake_packet_loss_percentage = 100; + AddWrite(SYNCHRONOUS, ConstructDataPacket(1)); + AddWrite(SYNCHRONOUS, ConstructDataPacket(2)); + Initialize(); + QuicTime::Delta kDefaultResendTime = QuicTime::Delta::FromMilliseconds(500); + QuicTime start = clock_.Now(); - connection_.SendStreamData(1, "foo", 0, false, NULL); - EXPECT_EQ(1u, helper_->header()->packet_sequence_number); + // Send a packet. + connection_->SendStreamData(1, kData, 0, false, NULL); - QuicTime start = clock_.Now(); + // Since no ack was received, the resend alarm will fire and resend it. runner_->RunNextTask(); + EXPECT_EQ(kDefaultResendTime, clock_.Now().Subtract(start)); - EXPECT_EQ(2u, helper_->header()->packet_sequence_number); + EXPECT_TRUE(AtEof()); } TEST_F(QuicConnectionHelperTest, InitialTimeout) { + AddWrite(SYNCHRONOUS, ConstructClosePacket(1, false)); + Initialize(); + // Verify that a single task was posted. EXPECT_EQ(1u, runner_->tasks()->size()); EXPECT_EQ(base::TimeDelta::FromMicroseconds(kDefaultTimeoutUs), @@ -184,11 +248,39 @@ TEST_F(QuicConnectionHelperTest, InitialTimeout) { runner_->RunNextTask(); EXPECT_EQ(QuicTime::FromMicroseconds(kDefaultTimeoutUs), clock_.Now()); - EXPECT_FALSE(connection_.connected()); + EXPECT_FALSE(connection_->connected()); + EXPECT_TRUE(AtEof()); +} + +TEST_F(QuicConnectionHelperTest, WritePacketToWire) { + AddWrite(SYNCHRONOUS, ConstructDataPacket(1)); + Initialize(); + + int len = GetWrite(0)->length(); + int error = 0; + EXPECT_EQ(len, helper_->WritePacketToWire(*GetWrite(0), &error)); + EXPECT_EQ(0, error); + EXPECT_TRUE(AtEof()); +} + +TEST_F(QuicConnectionHelperTest, WritePacketToWireAsync) { + AddWrite(ASYNC, ConstructClosePacket(1, false)); + Initialize(); + + int error = 0; + EXPECT_EQ(ERR_IO_PENDING, + helper_->WritePacketToWire(*GetWrite(0), &error)); + EXPECT_EQ(0, error); + MessageLoop::current()->RunUntilIdle(); + EXPECT_TRUE(AtEof()); } TEST_F(QuicConnectionHelperTest, TimeoutAfterSend) { - EXPECT_TRUE(connection_.connected()); + AddWrite(SYNCHRONOUS, ConstructAckPacket(1)); + AddWrite(SYNCHRONOUS, ConstructClosePacket(2, true)); + Initialize(); + + EXPECT_TRUE(connection_->connected()); EXPECT_EQ(0u, clock_.Now().ToMicroseconds()); // When we send a packet, the timeout will change to 5000 + kDefaultTimeout. @@ -196,39 +288,41 @@ TEST_F(QuicConnectionHelperTest, TimeoutAfterSend) { EXPECT_EQ(5000u, clock_.Now().ToMicroseconds()); // Send an ack so we don't set the resend alarm. - connection_.SendAck(); + connection_->SendAck(); // The original alarm will fire. We should not time out because we had a // network event at t=5000. The alarm will reregister. runner_->RunNextTask(); EXPECT_EQ(QuicTime::FromMicroseconds(kDefaultTimeoutUs), clock_.Now()); + EXPECT_TRUE(connection_->connected()); // This time, we should time out. EXPECT_CALL(visitor_, ConnectionClose(QUIC_CONNECTION_TIMED_OUT, false)); runner_->RunNextTask(); EXPECT_EQ(kDefaultTimeoutUs + 5000, clock_.Now().ToMicroseconds()); - EXPECT_FALSE(connection_.connected()); + EXPECT_FALSE(connection_->connected()); + EXPECT_TRUE(AtEof()); } TEST_F(QuicConnectionHelperTest, SendSchedulerDelayThenSend) { + AddWrite(SYNCHRONOUS, ConstructDataPacket(1)); + Initialize(); + // Test that if we send a packet with a delay, it ends up queued. - scoped_ptr<QuicPacket> packet(ConstructDataPacket(1, 0)); EXPECT_CALL(*scheduler_, TimeUntilSend(true)).WillOnce(testing::Return( QuicTime::Delta::FromMicroseconds(1))); - bool should_resend = true; - bool force = false; - bool is_retransmit = false; - connection_.SendPacket(1, packet.get(), should_resend, force, is_retransmit); - EXPECT_EQ(1u, connection_.NumQueuedPackets()); + connection_->SendStreamData(1, kData, 0, false, NULL); + EXPECT_EQ(1u, connection_->NumQueuedPackets()); // Advance the clock to fire the alarm, and configure the scheduler // to permit the packet to be sent. EXPECT_CALL(*scheduler_, TimeUntilSend(true)).WillOnce(testing::Return( QuicTime::Delta())); runner_->RunNextTask(); - EXPECT_EQ(0u, connection_.NumQueuedPackets()); + EXPECT_EQ(0u, connection_->NumQueuedPackets()); + EXPECT_TRUE(AtEof()); } } // namespace test diff --git a/net/quic/quic_connection_test.cc b/net/quic/quic_connection_test.cc index 753a086..8f9094d 100644 --- a/net/quic/quic_connection_test.cc +++ b/net/quic/quic_connection_test.cc @@ -711,7 +711,8 @@ TEST_F(QuicConnectionTest, TestResend) { EXPECT_EQ(2u, last_header()->packet_sequence_number); } -TEST_F(QuicConnectionTest, TestQueued) { +// TODO(rch): Enable after we get non-blocking sockets. +TEST_F(QuicConnectionTest, DISABLED_TestQueued) { EXPECT_EQ(0u, connection_.NumQueuedPackets()); helper_->set_blocked(true); connection_.SendStreamData(1, "foo", 0, false, NULL); @@ -851,7 +852,8 @@ TEST_F(QuicConnectionTest, SendSchedulerForce) { EXPECT_EQ(0u, connection_.NumQueuedPackets()); } -TEST_F(QuicConnectionTest, SendSchedulerEAGAIN) { +// TODO(rch): Enable after we get non-blocking sockets. +TEST_F(QuicConnectionTest, DISABLED_SendSchedulerEAGAIN) { scoped_ptr<QuicPacket> packet(ConstructDataPacket(1, 0)); helper_->set_blocked(true); EXPECT_CALL(*scheduler_, TimeUntilSend(true)).WillOnce(testing::Return( |