summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorrch@chromium.org <rch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-11-14 07:28:24 +0000
committerrch@chromium.org <rch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-11-14 07:28:24 +0000
commitacf2474114d7d07c35553a23f70212b66bcdaab7 (patch)
tree6c64e793052be8ace63f444ece3f82e82ddb36b8 /net
parent79fbdab0cd96515427b35140e4fdb342824d2d80 (diff)
downloadchromium_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.cc5
-rw-r--r--net/quic/quic_connection_helper.cc14
-rw-r--r--net/quic/quic_connection_helper.h2
-rw-r--r--net/quic/quic_connection_helper_test.cc260
-rw-r--r--net/quic/quic_connection_test.cc6
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(