| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "net/quic/quic_connection.h" |
| |
| #include "net/base/net_errors.h" |
| #include "net/quic/congestion_control/quic_receipt_metrics_collector.h" |
| #include "net/quic/congestion_control/quic_send_scheduler.h" |
| #include "net/quic/crypto/null_encrypter.h" |
| #include "net/quic/crypto/quic_decrypter.h" |
| #include "net/quic/crypto/quic_encrypter.h" |
| #include "net/quic/test_tools/mock_clock.h" |
| #include "net/quic/test_tools/quic_test_utils.h" |
| #include "net/quic/quic_utils.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| using base::StringPiece; |
| using std::map; |
| using testing::_; |
| using testing::ContainerEq; |
| using testing::Return; |
| using testing::StrictMock; |
| |
| namespace net { |
| |
| // Peer to make public a number of otherwise private QuicConnection methods. |
| class QuicConnectionPeer { |
| public: |
| static void SendAck(QuicConnection* connection) { |
| connection->SendAck(); |
| } |
| |
| static void SetCollector(QuicConnection* connection, |
| QuicReceiptMetricsCollector* collector) { |
| connection->collector_.reset(collector); |
| } |
| |
| static void SetScheduler(QuicConnection* connection, |
| QuicSendScheduler* scheduler) { |
| connection->scheduler_.reset(scheduler); |
| } |
| static QuicAckFrame* GetOutgoingAck(QuicConnection* connection) { |
| return &connection->outgoing_ack_; |
| } |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(QuicConnectionPeer); |
| }; |
| |
| namespace test { |
| namespace { |
| |
| const char data1[] = "foo"; |
| const char data2[] = "bar"; |
| |
| class TestCollector : public QuicReceiptMetricsCollector { |
| public: |
| explicit TestCollector(QuicCongestionFeedbackFrame* feedback) |
| : QuicReceiptMetricsCollector(&clock_, kFixRate), |
| feedback_(feedback) { |
| } |
| |
| bool GenerateCongestionFeedback( |
| QuicCongestionFeedbackFrame* congestion_feedback) { |
| if (feedback_ == NULL) { |
| return false; |
| } |
| *congestion_feedback = *feedback_; |
| return true; |
| } |
| |
| MOCK_METHOD4(RecordIncomingPacket, |
| void(size_t, QuicPacketSequenceNumber, QuicTime, bool)); |
| |
| private: |
| MockClock clock_; |
| QuicCongestionFeedbackFrame* feedback_; |
| |
| DISALLOW_COPY_AND_ASSIGN(TestCollector); |
| }; |
| |
| class TestConnectionHelper : public QuicConnectionHelperInterface { |
| public: |
| explicit TestConnectionHelper(MockClock* clock) |
| : clock_(clock), |
| blocked_(false) { |
| } |
| |
| // QuicConnectionHelperInterface |
| virtual void SetConnection(QuicConnection* connection) {} |
| |
| virtual const QuicClock* GetClock() const { |
| return clock_; |
| } |
| |
| 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(); |
| if (visitor.ack()) { |
| ack_.reset(new QuicAckFrame(*visitor.ack())); |
| } |
| if (visitor.feedback()) { |
| feedback_.reset(new QuicCongestionFeedbackFrame(*visitor.feedback())); |
| } |
| if (blocked_) { |
| *error = ERR_IO_PENDING; |
| return -1; |
| } |
| return packet.length(); |
| } |
| |
| virtual void SetResendAlarm(QuicPacketSequenceNumber sequence_number, |
| QuicTime::Delta delay) { |
| resend_alarms_[sequence_number] = clock_->Now().Add(delay); |
| } |
| |
| virtual void SetSendAlarm(QuicTime::Delta delay) { |
| send_alarm_ = clock_->Now().Add(delay); |
| } |
| |
| virtual void SetTimeoutAlarm(QuicTime::Delta delay) { |
| timeout_alarm_ = clock_->Now().Add(delay); |
| } |
| |
| virtual bool IsSendAlarmSet() { |
| return send_alarm_ > clock_->Now(); |
| } |
| |
| virtual void UnregisterSendAlarmIfRegistered() { |
| send_alarm_ = QuicTime(); |
| } |
| |
| const map<QuicPacketSequenceNumber, QuicTime>& resend_alarms() const { |
| return resend_alarms_; |
| } |
| |
| QuicTime timeout_alarm() const { return timeout_alarm_; } |
| |
| QuicPacketHeader* header() { return &header_; } |
| |
| QuicAckFrame* ack() { return ack_.get(); } |
| |
| QuicCongestionFeedbackFrame* feedback() { return feedback_.get(); } |
| |
| void set_blocked(bool blocked) { blocked_ = blocked; } |
| |
| private: |
| MockClock* clock_; |
| map<QuicPacketSequenceNumber, QuicTime> resend_alarms_; |
| QuicTime send_alarm_; |
| QuicTime timeout_alarm_; |
| QuicPacketHeader header_; |
| scoped_ptr<QuicAckFrame> ack_; |
| scoped_ptr<QuicCongestionFeedbackFrame> feedback_; |
| bool blocked_; |
| |
| DISALLOW_COPY_AND_ASSIGN(TestConnectionHelper); |
| }; |
| |
| class TestConnection : public QuicConnection { |
| public: |
| TestConnection(QuicGuid guid, |
| IPEndPoint address, |
| TestConnectionHelper* helper) |
| : QuicConnection(guid, address, helper) { |
| } |
| |
| void SendAck() { |
| QuicConnectionPeer::SendAck(this); |
| } |
| |
| void SetCollector(QuicReceiptMetricsCollector* collector) { |
| QuicConnectionPeer::SetCollector(this, collector); |
| } |
| |
| void SetScheduler(QuicSendScheduler* scheduler) { |
| QuicConnectionPeer::SetScheduler(this, scheduler); |
| } |
| |
| 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); |
| } |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(TestConnection); |
| }; |
| |
| class QuicConnectionTest : public ::testing::Test { |
| protected: |
| QuicConnectionTest() |
| : guid_(42), |
| framer_(QuicDecrypter::Create(kNULL), QuicEncrypter::Create(kNULL)), |
| creator_(guid_, &framer_), |
| scheduler_(new StrictMock<MockScheduler>), |
| helper_(new TestConnectionHelper(&clock_)), |
| connection_(guid_, IPEndPoint(), helper_.get()), |
| frame1_(1, false, 0, data1), |
| frame2_(1, false, 3, data2), |
| accept_packet_(true) { |
| connection_.set_visitor(&visitor_); |
| connection_.SetScheduler(scheduler_); |
| // Simplify tests by not sending feedback unless specifically configured. |
| SetFeedback(NULL); |
| EXPECT_CALL(*scheduler_, TimeUntilSend(_)).WillRepeatedly(Return( |
| QuicTime::Delta())); |
| } |
| |
| QuicAckFrame* last_ack() { |
| return helper_->ack(); |
| } |
| |
| QuicCongestionFeedbackFrame* last_feedback() { |
| return helper_->feedback(); |
| } |
| |
| QuicPacketHeader* last_header() { |
| return helper_->header(); |
| } |
| |
| void ProcessPacket(QuicPacketSequenceNumber number) { |
| EXPECT_CALL(visitor_, OnPacket(_, _, _, _)) |
| .WillOnce(Return(accept_packet_)); |
| EXPECT_CALL(*scheduler_, SentPacket(_, _, _)); |
| ProcessDataPacket(number, 0); |
| } |
| |
| void ProcessFecProtectedPacket(QuicPacketSequenceNumber number, |
| bool expect_revival) { |
| if (expect_revival) { |
| EXPECT_CALL(visitor_, OnPacket(_, _, _, _)).Times(2).WillRepeatedly( |
| Return(accept_packet_)); |
| EXPECT_CALL(*scheduler_, SentPacket(_, _, _)).Times(2); |
| } else { |
| EXPECT_CALL(visitor_, OnPacket(_, _, _, _)).WillOnce( |
| Return(accept_packet_)); |
| EXPECT_CALL(*scheduler_, SentPacket(_, _, _)); |
| } |
| ProcessDataPacket(number, 1); |
| } |
| |
| void ProcessDataPacket(QuicPacketSequenceNumber number, |
| QuicFecGroupNumber fec_group) { |
| scoped_ptr<QuicPacket> packet(ConstructDataPacket(number, fec_group)); |
| scoped_ptr<QuicEncryptedPacket> encrypted(framer_.EncryptPacket(*packet)); |
| connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted); |
| } |
| |
| // Sends an FEC packet that covers the packets that would have been sent. |
| void ProcessFecPacket(QuicPacketSequenceNumber number, |
| QuicPacketSequenceNumber min_protected_packet, |
| bool expect_revival) { |
| if (expect_revival) { |
| EXPECT_CALL(visitor_, OnPacket(_, _, _, _)).WillOnce( |
| Return(accept_packet_)); |
| EXPECT_CALL(*scheduler_, SentPacket(_, _, _)); |
| } |
| |
| // Construct the decrypted data packet so we can compute the correct |
| // redundancy. |
| scoped_ptr<QuicPacket> data_packet(ConstructDataPacket(number, 1)); |
| |
| header_.guid = guid_; |
| header_.packet_sequence_number = number; |
| header_.flags = PACKET_FLAGS_FEC; |
| header_.fec_group = 1; |
| QuicFecData fec_data; |
| fec_data.min_protected_packet_sequence_number = min_protected_packet; |
| fec_data.fec_group = 1; |
| // Since all data packets in this test have the same payload, the |
| // redundancy is either equal to that payload or the xor of that payload |
| // with itself, depending on the number of packets. |
| if (((number - min_protected_packet) % 2) == 0) { |
| for (size_t i = kStartOfFecProtectedData; i < data_packet->length(); |
| ++i) { |
| data_packet->mutable_data()[i] ^= data_packet->data()[i]; |
| } |
| } |
| fec_data.redundancy = data_packet->FecProtectedData(); |
| scoped_ptr<QuicPacket> fec_packet( |
| framer_.ConstructFecPacket(header_, fec_data)); |
| scoped_ptr<QuicEncryptedPacket> encrypted( |
| framer_.EncryptPacket(*fec_packet)); |
| |
| connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted); |
| } |
| |
| void SendStreamDataToPeer(QuicStreamId id, StringPiece data, |
| QuicStreamOffset offset, bool fin, |
| QuicPacketSequenceNumber* last_packet) { |
| EXPECT_CALL(*scheduler_, SentPacket(_, _, _)); |
| connection_.SendStreamData(id, data, offset, fin, last_packet); |
| } |
| |
| void SendAckPacketToPeer() { |
| EXPECT_CALL(*scheduler_, SentPacket(_, _, _)).Times(num_packets_per_ack_); |
| connection_.SendAck(); |
| } |
| |
| void ProcessAckPacket(QuicAckFrame* frame, bool expect_success = true) { |
| if (expect_success) { |
| EXPECT_CALL(*scheduler_, OnIncomingAckFrame(_)); |
| } |
| scoped_ptr<QuicPacket> packet(creator_.AckPacket(frame).second); |
| scoped_ptr<QuicEncryptedPacket> encrypted(framer_.EncryptPacket(*packet)); |
| connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted); |
| } |
| |
| bool IsMissing(QuicPacketSequenceNumber number) { |
| return last_ack()->received_info.IsAwaitingPacket(number); |
| } |
| |
| QuicPacket* ConstructDataPacket(QuicPacketSequenceNumber number, |
| QuicFecGroupNumber fec_group) { |
| header_.guid = guid_; |
| header_.packet_sequence_number = number; |
| header_.flags = PACKET_FLAGS_NONE; |
| header_.fec_group = fec_group; |
| |
| QuicFrames frames; |
| QuicFrame frame(&frame1_); |
| frames.push_back(frame); |
| QuicPacket* packet = framer_.ConstructFrameDataPacket(header_, frames); |
| EXPECT_TRUE(packet != NULL); |
| return packet; |
| } |
| |
| void SetFeedback(QuicCongestionFeedbackFrame* feedback) { |
| num_packets_per_ack_ = feedback != NULL ? 2 : 1; |
| collector_ = new TestCollector(feedback); |
| connection_.SetCollector(collector_); |
| } |
| |
| QuicGuid guid_; |
| QuicFramer framer_; |
| QuicPacketCreator creator_; |
| |
| MockScheduler* scheduler_; |
| TestCollector* collector_; |
| MockClock clock_; |
| scoped_ptr<TestConnectionHelper> helper_; |
| TestConnection connection_; |
| testing::StrictMock<MockConnectionVisitor> visitor_; |
| |
| QuicPacketHeader header_; |
| QuicStreamFrame frame1_; |
| QuicStreamFrame frame2_; |
| bool accept_packet_; |
| size_t num_packets_per_ack_; |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(QuicConnectionTest); |
| }; |
| |
| TEST_F(QuicConnectionTest, PacketsInOrder) { |
| ProcessPacket(1); |
| EXPECT_EQ(1u, last_ack()->received_info.largest_received); |
| EXPECT_EQ(0u, last_ack()->received_info.missing_packets.size()); |
| |
| ProcessPacket(2); |
| EXPECT_EQ(2u, last_ack()->received_info.largest_received); |
| EXPECT_EQ(0u, last_ack()->received_info.missing_packets.size()); |
| |
| ProcessPacket(3); |
| EXPECT_EQ(3u, last_ack()->received_info.largest_received); |
| EXPECT_EQ(0u, last_ack()->received_info.missing_packets.size()); |
| } |
| |
| TEST_F(QuicConnectionTest, PacketsRejected) { |
| ProcessPacket(1); |
| EXPECT_EQ(1u, last_ack()->received_info.largest_received); |
| EXPECT_EQ(0u, last_ack()->received_info.missing_packets.size()); |
| |
| accept_packet_ = false; |
| ProcessPacket(2); |
| // We should not have an ack for two. |
| EXPECT_EQ(1u, last_ack()->received_info.largest_received); |
| EXPECT_EQ(0u, last_ack()->received_info.missing_packets.size()); |
| } |
| |
| TEST_F(QuicConnectionTest, PacketsOutOfOrder) { |
| ProcessPacket(3); |
| EXPECT_EQ(3u, last_ack()->received_info.largest_received); |
| EXPECT_TRUE(IsMissing(2)); |
| EXPECT_TRUE(IsMissing(1)); |
| |
| ProcessPacket(2); |
| EXPECT_EQ(3u, last_ack()->received_info.largest_received); |
| EXPECT_FALSE(IsMissing(2)); |
| EXPECT_TRUE(IsMissing(1)); |
| |
| ProcessPacket(1); |
| EXPECT_EQ(3u, last_ack()->received_info.largest_received); |
| EXPECT_FALSE(IsMissing(2)); |
| EXPECT_FALSE(IsMissing(1)); |
| } |
| |
| TEST_F(QuicConnectionTest, DuplicatePacket) { |
| ProcessPacket(3); |
| EXPECT_EQ(3u, last_ack()->received_info.largest_received); |
| EXPECT_TRUE(IsMissing(2)); |
| EXPECT_TRUE(IsMissing(1)); |
| |
| // Send packet 3 again, but do not set the expectation that |
| // the visitor OnPacket() will be called. |
| ProcessDataPacket(3, 0); |
| EXPECT_EQ(3u, last_ack()->received_info.largest_received); |
| EXPECT_TRUE(IsMissing(2)); |
| EXPECT_TRUE(IsMissing(1)); |
| } |
| |
| TEST_F(QuicConnectionTest, PacketsOutOfOrderWithAdditionsAndLeastAwaiting) { |
| ProcessPacket(3); |
| EXPECT_EQ(3u, last_ack()->received_info.largest_received); |
| EXPECT_TRUE(IsMissing(2)); |
| EXPECT_TRUE(IsMissing(1)); |
| |
| ProcessPacket(2); |
| EXPECT_EQ(3u, last_ack()->received_info.largest_received); |
| EXPECT_TRUE(IsMissing(1)); |
| |
| ProcessPacket(5); |
| EXPECT_EQ(5u, last_ack()->received_info.largest_received); |
| EXPECT_TRUE(IsMissing(1)); |
| EXPECT_TRUE(IsMissing(4)); |
| |
| // Pretend at this point the client has gotten acks for 2 and 3 and 1 is a |
| // packet the peer will not retransmit. It indicates this by sending 'least |
| // awaiting' is 4. The connection should then realize 1 will not be |
| // retransmitted, and will remove it from the missing list. |
| creator_.set_sequence_number(5); |
| QuicAckFrame frame(0, 4); |
| ProcessAckPacket(&frame); |
| |
| // Force an ack to be sent. |
| SendAckPacketToPeer(); |
| EXPECT_TRUE(IsMissing(4)); |
| } |
| |
| TEST_F(QuicConnectionTest, RejectPacketTooFarOut) { |
| // Call ProcessDataPacket rather than ProcessPacket, as we should not get a |
| // packet call to the visitor. |
| ProcessDataPacket(6000, 0); |
| |
| SendAckPacketToPeer(); // Packet 2 |
| EXPECT_EQ(0u, last_ack()->received_info.largest_received); |
| } |
| |
| TEST_F(QuicConnectionTest, LeastUnackedLower) { |
| SendStreamDataToPeer(1, "foo", 0, false, NULL); |
| SendStreamDataToPeer(1, "bar", 3, false, NULL); |
| SendStreamDataToPeer(1, "eep", 6, false, NULL); |
| |
| // Start out saying the least unacked is 2 |
| creator_.set_sequence_number(5); |
| QuicAckFrame frame(0, 2); |
| ProcessAckPacket(&frame); |
| |
| // Change it to 1, but lower the sequence number to fake out-of-order packets. |
| // This should be fine. |
| creator_.set_sequence_number(1); |
| QuicAckFrame frame2(0, 1); |
| // The scheduler will not process out of order acks. |
| ProcessAckPacket(&frame2, false); |
| |
| // Now claim it's one, but set the ordering so it was sent "after" the first |
| // one. This should cause a connection error. |
| EXPECT_CALL(visitor_, ConnectionClose(QUIC_INVALID_ACK_DATA, false)); |
| EXPECT_CALL(*scheduler_, SentPacket(_, _, _)); |
| creator_.set_sequence_number(7); |
| ProcessAckPacket(&frame2, false); |
| } |
| |
| TEST_F(QuicConnectionTest, LeastUnackedGreaterThanPacketSequenceNumber) { |
| EXPECT_CALL(visitor_, ConnectionClose(QUIC_INVALID_ACK_DATA, false)); |
| EXPECT_CALL(*scheduler_, SentPacket(_, _, _)); |
| // Create an ack with least_unacked is 2 in packet number 1. |
| creator_.set_sequence_number(0); |
| QuicAckFrame frame(0, 2); |
| ProcessAckPacket(&frame, false); |
| } |
| |
| TEST_F(QuicConnectionTest, |
| DISABLED_NackSequenceNumberGreaterThanLargestReceived) { |
| SendStreamDataToPeer(1, "foo", 0, false, NULL); |
| SendStreamDataToPeer(1, "bar", 3, false, NULL); |
| SendStreamDataToPeer(1, "eep", 6, false, NULL); |
| |
| EXPECT_CALL(visitor_, ConnectionClose(QUIC_INVALID_ACK_DATA, false)); |
| EXPECT_CALL(*scheduler_, SentPacket(_, _, _)); |
| QuicAckFrame frame(0, 1); |
| frame.received_info.missing_packets.insert(3); |
| ProcessAckPacket(&frame, false); |
| } |
| |
| TEST_F(QuicConnectionTest, AckUnsentData) { |
| // Ack a packet which has not been sent. |
| EXPECT_CALL(visitor_, ConnectionClose(QUIC_INVALID_ACK_DATA, false)); |
| EXPECT_CALL(*scheduler_, SentPacket(_, _, _)); |
| QuicAckFrame frame(1, 0); |
| ProcessAckPacket(&frame, false); |
| } |
| |
| TEST_F(QuicConnectionTest, AckAll) { |
| ProcessPacket(1); |
| |
| creator_.set_sequence_number(1); |
| QuicAckFrame frame1(1, 1); |
| ProcessAckPacket(&frame1); |
| } |
| |
| TEST_F(QuicConnectionTest, BasicSending) { |
| QuicPacketSequenceNumber last_packet; |
| SendStreamDataToPeer(1, "foo", 0, false, &last_packet); // Packet 1 |
| EXPECT_EQ(1u, last_packet); |
| SendAckPacketToPeer(); // Packet 2 |
| |
| EXPECT_EQ(1u, last_ack()->sent_info.least_unacked); |
| |
| SendAckPacketToPeer(); // Packet 3 |
| EXPECT_EQ(1u, last_ack()->sent_info.least_unacked); |
| |
| SendStreamDataToPeer(1u, "bar", 3, false, &last_packet); // Packet 4 |
| EXPECT_EQ(4u, last_packet); |
| SendAckPacketToPeer(); // Packet 5 |
| EXPECT_EQ(1u, last_ack()->sent_info.least_unacked); |
| |
| QuicConnectionVisitorInterface::AckedPackets expected_acks; |
| expected_acks.insert(1); |
| |
| // Client acks up to packet 3 |
| EXPECT_CALL(visitor_, OnAck(ContainerEq(expected_acks))); |
| QuicAckFrame frame(3, 0); |
| ProcessAckPacket(&frame); |
| SendAckPacketToPeer(); // Packet 6 |
| |
| // As soon as we've acked one, we skip ack packets 2 and 3 and note lack of |
| // ack for 4. |
| EXPECT_EQ(4u, last_ack()->sent_info.least_unacked); |
| |
| expected_acks.clear(); |
| expected_acks.insert(4); |
| |
| // Client acks up to packet 4, the last packet |
| EXPECT_CALL(visitor_, OnAck(ContainerEq(expected_acks))); |
| QuicAckFrame frame2(6, 0); |
| ProcessAckPacket(&frame2); |
| SendAckPacketToPeer(); // Packet 7 |
| |
| // The least packet awaiting ack should now be 7 |
| EXPECT_EQ(7u, last_ack()->sent_info.least_unacked); |
| |
| // If we force an ack, we shouldn't change our retransmit state. |
| SendAckPacketToPeer(); // Packet 8 |
| EXPECT_EQ(8u, last_ack()->sent_info.least_unacked); |
| |
| // But if we send more data it should. |
| SendStreamDataToPeer(1, "eep", 6, false, &last_packet); // Packet 9 |
| EXPECT_EQ(9u, last_packet); |
| SendAckPacketToPeer(); // Packet10 |
| EXPECT_EQ(9u, last_ack()->sent_info.least_unacked); |
| } |
| |
| TEST_F(QuicConnectionTest, ResendOnNack) { |
| QuicPacketSequenceNumber last_packet; |
| SendStreamDataToPeer(1, "foo", 0, false, &last_packet); // Packet 1 |
| SendStreamDataToPeer(1, "foos", 3, false, &last_packet); // Packet 2 |
| SendStreamDataToPeer(1, "fooos", 7, false, &last_packet); // Packet 3 |
| |
| QuicConnectionVisitorInterface::AckedPackets expected_acks; |
| expected_acks.insert(1); |
| EXPECT_CALL(visitor_, OnAck(ContainerEq(expected_acks))); |
| |
| // Client acks one but not two or three. Right now we only resend on explicit |
| // nack, so it should not trigger resend. |
| QuicAckFrame ack_one(1, 0); |
| ProcessAckPacket(&ack_one); |
| ProcessAckPacket(&ack_one); |
| ProcessAckPacket(&ack_one); |
| |
| expected_acks.clear(); |
| expected_acks.insert(3); |
| EXPECT_CALL(visitor_, OnAck(ContainerEq(expected_acks))); |
| |
| // Client acks up to 3 with two explicitly missing. Two nacks should cause no |
| // change. |
| QuicAckFrame nack_two(3, 0); |
| nack_two.received_info.missing_packets.insert(2); |
| ProcessAckPacket(&nack_two); |
| ProcessAckPacket(&nack_two); |
| |
| // The third nack should trigger resend. |
| EXPECT_CALL(*scheduler_, SentPacket(4, 37, true)).Times(1); |
| ProcessAckPacket(&nack_two); |
| } |
| |
| TEST_F(QuicConnectionTest, LimitPacketsPerNack) { |
| int offset = 0; |
| // Send packets 1 to 12 |
| for (int i = 0; i < 12; ++i) { |
| SendStreamDataToPeer(1, "foo", offset, false, NULL); |
| offset += 3; |
| } |
| |
| // Ack 12, nack 1-11 |
| QuicAckFrame nack(12, 0); |
| for (int i = 1; i < 12; ++i) { |
| nack.received_info.missing_packets.insert(i); |
| } |
| |
| QuicConnectionVisitorInterface::AckedPackets expected_acks; |
| expected_acks.insert(12); |
| EXPECT_CALL(visitor_, OnAck(ContainerEq(expected_acks))); |
| |
| // Nack three times. |
| ProcessAckPacket(&nack); |
| ProcessAckPacket(&nack); |
| // The third call should trigger resending 10 packets and an updated ack. |
| EXPECT_CALL(*scheduler_, SentPacket(_, _, _)).Times(11); |
| ProcessAckPacket(&nack); |
| |
| // The fourth call should triggre resending the 11th packet. |
| EXPECT_CALL(*scheduler_, SentPacket(_, _, _)).Times(1); |
| ProcessAckPacket(&nack); |
| } |
| |
| // Test sending multiple acks from the connection to the session. |
| TEST_F(QuicConnectionTest, MultipleAcks) { |
| QuicPacketSequenceNumber last_packet; |
| SendStreamDataToPeer(1u, "foo", 0, false, &last_packet); // Packet 1 |
| EXPECT_EQ(1u, last_packet); |
| SendStreamDataToPeer(3u, "foo", 0, false, &last_packet); // Packet 2 |
| EXPECT_EQ(2u, last_packet); |
| SendAckPacketToPeer(); // Packet 3 |
| SendStreamDataToPeer(5u, "foo", 0, false, &last_packet); // Packet 4 |
| EXPECT_EQ(4u, last_packet); |
| SendStreamDataToPeer(1u, "foo", 3, false, &last_packet); // Packet 5 |
| EXPECT_EQ(5u, last_packet); |
| SendStreamDataToPeer(3u, "foo", 3, false, &last_packet); // Packet 6 |
| EXPECT_EQ(6u, last_packet); |
| |
| // Client will ack packets 1, [!2], 3, 4, 5 |
| QuicAckFrame frame1(5, 0); |
| frame1.received_info.missing_packets.insert(2); |
| |
| // The connection should pass up acks for 1, 4, 5. 2 is not acked, and 3 was |
| // an ackframe so should not be passed up. |
| QuicConnectionVisitorInterface::AckedPackets expected_acks; |
| expected_acks.insert(1); |
| expected_acks.insert(4); |
| expected_acks.insert(5); |
| |
| EXPECT_CALL(visitor_, OnAck(ContainerEq(expected_acks))); |
| ProcessAckPacket(&frame1); |
| |
| // Now the client implicitly acks 2, and explicitly acks 6 |
| QuicAckFrame frame2(6, 0); |
| expected_acks.clear(); |
| // Both acks should be passed up. |
| expected_acks.insert(2); |
| expected_acks.insert(6); |
| |
| EXPECT_CALL(visitor_, OnAck(ContainerEq(expected_acks))); |
| ProcessAckPacket(&frame2); |
| } |
| |
| TEST_F(QuicConnectionTest, DontLatchUnackedPacket) { |
| SendStreamDataToPeer(1, "foo", 0, false, NULL); // Packet 1; |
| SendAckPacketToPeer(); // Packet 2 |
| |
| // This sets least unacked to 2, the ack packet. |
| QuicConnectionVisitorInterface::AckedPackets expected_acks; |
| expected_acks.insert(1); |
| // Client acks packet 1 |
| EXPECT_CALL(visitor_, OnAck(ContainerEq(expected_acks))); |
| QuicAckFrame frame(1, 0); |
| ProcessAckPacket(&frame); |
| |
| // Verify that our internal state has least-unacked as 2. |
| QuicAckFrame* outgoing_ack = QuicConnectionPeer::GetOutgoingAck(&connection_); |
| EXPECT_EQ(2u, outgoing_ack->sent_info.least_unacked); |
| |
| // When we send an ack, we make sure our least-unacked makes sense. In this |
| // case since we're not waiting on an ack for 2 and all packets are acked, we |
| // set it to 3. |
| SendAckPacketToPeer(); // Packet 3 |
| EXPECT_EQ(3u, outgoing_ack->sent_info.least_unacked); |
| EXPECT_EQ(3u, last_ack()->sent_info.least_unacked); |
| } |
| |
| TEST_F(QuicConnectionTest, ReviveMissingPacketAfterFecPacket) { |
| // Don't send missing packet 1. |
| ProcessFecPacket(2, 1, true); |
| } |
| |
| TEST_F(QuicConnectionTest, ReviveMissingPacketAfterDataPacketThenFecPacket) { |
| ProcessFecProtectedPacket(1, false); |
| // Don't send missing packet 2. |
| ProcessFecPacket(3, 1, true); |
| } |
| |
| TEST_F(QuicConnectionTest, ReviveMissingPacketAfterDataPacketsThenFecPacket) { |
| ProcessFecProtectedPacket(1, false); |
| // Don't send missing packet 2. |
| ProcessFecProtectedPacket(3, false); |
| ProcessFecPacket(4, 1, true); |
| } |
| |
| TEST_F(QuicConnectionTest, ReviveMissingPacketAfterDataPacket) { |
| // Don't send missing packet 1. |
| ProcessFecPacket(3, 1, false); // out of order |
| ProcessFecProtectedPacket(2, true); |
| } |
| |
| TEST_F(QuicConnectionTest, ReviveMissingPacketAfterDataPackets) { |
| ProcessFecProtectedPacket(1, false); |
| // Don't send missing packet 2. |
| ProcessFecPacket(6, 1, false); |
| ProcessFecProtectedPacket(3, false); |
| ProcessFecProtectedPacket(4, false); |
| ProcessFecProtectedPacket(5, true); |
| } |
| |
| TEST_F(QuicConnectionTest, TestResend) { |
| // TODO(rch): make this work |
| // FLAGS_fake_packet_loss_percentage = 100; |
| const QuicTime::Delta kDefaultResendTime = |
| QuicTime::Delta::FromMilliseconds(500); |
| |
| QuicTime default_resend_time = clock_.Now().Add(kDefaultResendTime); |
| |
| QuicAckFrame* outgoing_ack = QuicConnectionPeer::GetOutgoingAck(&connection_); |
| SendStreamDataToPeer(1, "foo", 0, false, NULL); |
| EXPECT_EQ(1u, outgoing_ack->sent_info.least_unacked); |
| |
| EXPECT_EQ(1u, last_header()->packet_sequence_number); |
| EXPECT_EQ(1u, helper_->resend_alarms().size()); |
| EXPECT_EQ(default_resend_time, |
| helper_->resend_alarms().find(1)->second); |
| // Simulate the resend alarm firing |
| clock_.AdvanceTime(kDefaultResendTime); |
| EXPECT_CALL(*scheduler_, SentPacket(_, _, _)); |
| connection_.MaybeResendPacket(1); |
| EXPECT_EQ(2u, last_header()->packet_sequence_number); |
| EXPECT_EQ(2u, outgoing_ack->sent_info.least_unacked); |
| } |
| |
| // TODO(rch): Enable after we get non-blocking sockets. |
| TEST_F(QuicConnectionTest, DISABLED_TestQueued) { |
| EXPECT_EQ(0u, connection_.NumQueuedPackets()); |
| helper_->set_blocked(true); |
| SendStreamDataToPeer(1, "foo", 0, false, NULL); |
| EXPECT_EQ(1u, connection_.NumQueuedPackets()); |
| |
| // Attempt to send all packets, but since we're actually still |
| // blocked, they should all remain queued. |
| EXPECT_FALSE(connection_.OnCanWrite()); |
| EXPECT_EQ(1u, connection_.NumQueuedPackets()); |
| |
| // Unblock the writes and actually send. |
| helper_->set_blocked(false); |
| EXPECT_CALL(visitor_, OnCanWrite()); |
| EXPECT_TRUE(connection_.OnCanWrite()); |
| EXPECT_EQ(0u, connection_.NumQueuedPackets()); |
| } |
| |
| TEST_F(QuicConnectionTest, CloseFecGroup) { |
| // Don't send missing packet 1 |
| // Don't send missing packet 2 |
| ProcessFecProtectedPacket(3, false); |
| // Don't send missing FEC packet 3 |
| ASSERT_EQ(1u, connection_.NumFecGroups()); |
| |
| // Now send non-fec protected ack packet and close the group |
| QuicAckFrame frame(0, 5); |
| creator_.set_sequence_number(4); |
| ProcessAckPacket(&frame); |
| ASSERT_EQ(0u, connection_.NumFecGroups()); |
| } |
| |
| TEST_F(QuicConnectionTest, NoQuicCongestionFeedbackFrame) { |
| SendAckPacketToPeer(); |
| EXPECT_TRUE(last_feedback() == NULL); |
| } |
| |
| TEST_F(QuicConnectionTest, WithQuicCongestionFeedbackFrame) { |
| QuicCongestionFeedbackFrame info; |
| info.type = kFixRate; |
| info.fix_rate.bitrate_in_bytes_per_second = 123; |
| SetFeedback(&info); |
| |
| SendAckPacketToPeer(); |
| EXPECT_EQ(kFixRate, last_feedback()->type); |
| EXPECT_EQ(info.fix_rate.bitrate_in_bytes_per_second, |
| last_feedback()->fix_rate.bitrate_in_bytes_per_second); |
| } |
| |
| TEST_F(QuicConnectionTest, UpdateQuicCongestionFeedbackFrame) { |
| SendAckPacketToPeer(); |
| EXPECT_CALL(*collector_, RecordIncomingPacket(_, _, _, _)); |
| ProcessPacket(1); |
| } |
| |
| TEST_F(QuicConnectionTest, DontUpdateQuicCongestionFeedbackFrameForRevived) { |
| SendAckPacketToPeer(); |
| // Process an FEC packet, and revive the missing data packet |
| // but only contact the collector once. |
| EXPECT_CALL(*collector_, RecordIncomingPacket(_, _, _, _)); |
| ProcessFecPacket(2, 1, true); |
| } |
| |
| TEST_F(QuicConnectionTest, InitialTimeout) { |
| EXPECT_TRUE(connection_.connected()); |
| EXPECT_CALL(visitor_, ConnectionClose(QUIC_CONNECTION_TIMED_OUT, false)); |
| EXPECT_CALL(*scheduler_, SentPacket(_, _, _)); |
| |
| QuicTime default_timeout = clock_.Now().Add( |
| QuicTime::Delta::FromMicroseconds(kDefaultTimeoutUs)); |
| EXPECT_EQ(default_timeout, helper_->timeout_alarm()); |
| |
| // Simulate the timeout alarm firing |
| clock_.AdvanceTime(QuicTime::Delta::FromMicroseconds(kDefaultTimeoutUs)); |
| EXPECT_TRUE(connection_.CheckForTimeout()); |
| EXPECT_FALSE(connection_.connected()); |
| } |
| |
| TEST_F(QuicConnectionTest, TimeoutAfterSend) { |
| EXPECT_TRUE(connection_.connected()); |
| |
| QuicTime default_timeout = clock_.Now().Add( |
| QuicTime::Delta::FromMicroseconds(kDefaultTimeoutUs)); |
| |
| // When we send a packet, the timeout will change to 5000 + kDefaultTimeout. |
| clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); |
| |
| // Send an ack so we don't set the resend alarm. |
| SendAckPacketToPeer(); |
| EXPECT_EQ(default_timeout, helper_->timeout_alarm()); |
| |
| // The original alarm will fire. We should not time out because we had a |
| // network event at t=5000. The alarm will reregister. |
| clock_.AdvanceTime(QuicTime::Delta::FromMicroseconds( |
| kDefaultTimeoutUs - 5000)); |
| EXPECT_EQ(default_timeout, clock_.Now()); |
| EXPECT_FALSE(connection_.CheckForTimeout()); |
| EXPECT_TRUE(connection_.connected()); |
| EXPECT_EQ(default_timeout.Add(QuicTime::Delta::FromMilliseconds(5)), |
| helper_->timeout_alarm()); |
| |
| // This time, we should time out. |
| EXPECT_CALL(visitor_, ConnectionClose(QUIC_CONNECTION_TIMED_OUT, false)); |
| EXPECT_CALL(*scheduler_, SentPacket(_, _, _)); |
| clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); |
| EXPECT_EQ(default_timeout.Add(QuicTime::Delta::FromMilliseconds(5)), |
| clock_.Now()); |
| EXPECT_TRUE(connection_.CheckForTimeout()); |
| EXPECT_FALSE(connection_.connected()); |
| } |
| |
| // TODO(ianswett): Add scheduler tests when resend is false. |
| TEST_F(QuicConnectionTest, SendScheduler) { |
| // Test that if we send a packet without delay, it is not queued. |
| scoped_ptr<QuicPacket> packet(ConstructDataPacket(1, 0)); |
| EXPECT_CALL(*scheduler_, TimeUntilSend(true)).WillOnce(testing::Return( |
| QuicTime::Delta())); |
| EXPECT_CALL(*scheduler_, SentPacket(_, _, _)); |
| connection_.SendPacket(1, packet.get(), true, false, false); |
| EXPECT_EQ(0u, connection_.NumQueuedPackets()); |
| } |
| |
| TEST_F(QuicConnectionTest, SendSchedulerDelay) { |
| // 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))); |
| EXPECT_CALL(*scheduler_, SentPacket(1, _, _)).Times(0); |
| connection_.SendPacket(1, packet.get(), true, false, false); |
| EXPECT_EQ(1u, connection_.NumQueuedPackets()); |
| } |
| |
| TEST_F(QuicConnectionTest, SendSchedulerForce) { |
| // Test that if we force send a packet, it is not queued. |
| scoped_ptr<QuicPacket> packet(ConstructDataPacket(1, 0)); |
| EXPECT_CALL(*scheduler_, TimeUntilSend(true)).Times(0); |
| EXPECT_CALL(*scheduler_, SentPacket(_, _, _)); |
| connection_.SendPacket(1, packet.get(), true, true, false); |
| EXPECT_EQ(0u, connection_.NumQueuedPackets()); |
| } |
| |
| // 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( |
| QuicTime::Delta())); |
| EXPECT_CALL(*scheduler_, SentPacket(1, _, _)).Times(0); |
| connection_.SendPacket(1, packet.get(), true, false, false); |
| EXPECT_EQ(1u, connection_.NumQueuedPackets()); |
| } |
| |
| TEST_F(QuicConnectionTest, SendSchedulerDelayThenSend) { |
| // 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))); |
| connection_.SendPacket(1, packet.get(), true, false, false); |
| 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())); |
| clock_.AdvanceTime(QuicTime::Delta::FromMicroseconds(1)); |
| EXPECT_CALL(*scheduler_, SentPacket(_, _, _)); |
| EXPECT_CALL(visitor_, OnCanWrite()); |
| connection_.OnCanWrite(); |
| EXPECT_EQ(0u, connection_.NumQueuedPackets()); |
| } |
| |
| TEST_F(QuicConnectionTest, SendSchedulerDelayThenRetransmit) { |
| // Test that if we send a retransmit 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))); |
| connection_.SendPacket(1, packet.get(), true, false, true); |
| 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())); |
| |
| // Ensure the scheduler is notified this is a retransmit. |
| EXPECT_CALL(*scheduler_, SentPacket(1, _, true)); |
| clock_.AdvanceTime(QuicTime::Delta::FromMicroseconds(1)); |
| EXPECT_CALL(visitor_, OnCanWrite()); |
| connection_.OnCanWrite(); |
| EXPECT_EQ(0u, connection_.NumQueuedPackets()); |
| } |
| |
| TEST_F(QuicConnectionTest, SendSchedulerDelayAndQueue) { |
| scoped_ptr<QuicPacket> packet(ConstructDataPacket(1, 0)); |
| EXPECT_CALL(*scheduler_, TimeUntilSend(true)).WillOnce(testing::Return( |
| QuicTime::Delta::FromMicroseconds(1))); |
| connection_.SendPacket(1, packet.get(), true, false, false); |
| EXPECT_EQ(1u, connection_.NumQueuedPackets()); |
| |
| // Attempt to send another packet and make sure that it gets queued. |
| connection_.SendPacket(2, packet.get(), true, false, false); |
| EXPECT_EQ(2u, connection_.NumQueuedPackets()); |
| } |
| |
| TEST_F(QuicConnectionTest, SendSchedulerDelayThenAckAndSend) { |
| scoped_ptr<QuicPacket> packet(ConstructDataPacket(1, 0)); |
| EXPECT_CALL(*scheduler_, TimeUntilSend(true)).WillOnce(testing::Return( |
| QuicTime::Delta::FromMicroseconds(10))); |
| connection_.SendPacket(1, packet.get(), true, false, false); |
| EXPECT_EQ(1u, connection_.NumQueuedPackets()); |
| |
| // Now send non-retransmitting information, that we're not going to resend 3. |
| // The far end should stop waiting for it. |
| QuicAckFrame frame(0, 1); |
| EXPECT_CALL(*scheduler_, TimeUntilSend(true)).WillRepeatedly(testing::Return( |
| QuicTime::Delta())); |
| EXPECT_CALL(*scheduler_, SentPacket(_, _, _)); |
| EXPECT_CALL(visitor_, OnCanWrite()); |
| ProcessAckPacket(&frame); |
| |
| EXPECT_EQ(0u, connection_.NumQueuedPackets()); |
| // Ensure alarm is not set |
| EXPECT_FALSE(helper_->IsSendAlarmSet()); |
| } |
| |
| TEST_F(QuicConnectionTest, SendSchedulerDelayThenAckAndHold) { |
| scoped_ptr<QuicPacket> packet(ConstructDataPacket(1, 0)); |
| EXPECT_CALL(*scheduler_, TimeUntilSend(true)).WillOnce(testing::Return( |
| QuicTime::Delta::FromMicroseconds(10))); |
| connection_.SendPacket(1, packet.get(), true, false, false); |
| EXPECT_EQ(1u, connection_.NumQueuedPackets()); |
| |
| // Now send non-resending information, that we're not going to resend 3. |
| // The far end should stop waiting for it. |
| QuicAckFrame frame(0, 1); |
| EXPECT_CALL(*scheduler_, TimeUntilSend(true)).WillOnce(testing::Return( |
| QuicTime::Delta::FromMicroseconds(1))); |
| EXPECT_CALL(visitor_, OnCanWrite()); |
| ProcessAckPacket(&frame); |
| |
| EXPECT_EQ(1u, connection_.NumQueuedPackets()); |
| } |
| |
| TEST_F(QuicConnectionTest, SendSchedulerDelayThenOnCanWrite) { |
| scoped_ptr<QuicPacket> packet(ConstructDataPacket(1, 0)); |
| EXPECT_CALL(*scheduler_, TimeUntilSend(true)).WillOnce(testing::Return( |
| QuicTime::Delta::FromMicroseconds(10))); |
| connection_.SendPacket(1, packet.get(), true, false, false); |
| EXPECT_EQ(1u, connection_.NumQueuedPackets()); |
| |
| // OnCanWrite should not send the packet (because of the delay) |
| // but should still return true. |
| EXPECT_CALL(visitor_, OnCanWrite()); |
| EXPECT_TRUE(connection_.OnCanWrite()); |
| EXPECT_EQ(1u, connection_.NumQueuedPackets()); |
| } |
| |
| TEST_F(QuicConnectionTest, TestQueueLimitsOnSendStreamData) { |
| // Limit to one byte per packet. |
| size_t ciphertext_size = NullEncrypter().GetCiphertextSize(1); |
| connection_.options()->max_packet_length = |
| ciphertext_size + QuicUtils::StreamFramePacketOverhead(1); |
| |
| // Queue the first packet. |
| EXPECT_CALL(*scheduler_, TimeUntilSend(true)).WillOnce(testing::Return( |
| QuicTime::Delta::FromMicroseconds(10))); |
| EXPECT_EQ(6u, |
| connection_.SendStreamData(1, "EnoughDataToQueue", 0, false, NULL)); |
| EXPECT_EQ(6u, connection_.NumQueuedPackets()); |
| } |
| |
| TEST_F(QuicConnectionTest, LoopThroughSendingPackets) { |
| // Limit to one byte per packet. |
| size_t ciphertext_size = NullEncrypter().GetCiphertextSize(1); |
| connection_.options()->max_packet_length = |
| ciphertext_size + QuicUtils::StreamFramePacketOverhead(1); |
| |
| // Queue the first packet. |
| EXPECT_CALL(*scheduler_, SentPacket(_, _, _)).Times(17); |
| EXPECT_EQ(17u, |
| connection_.SendStreamData(1, "EnoughDataToQueue", 0, false, NULL)); |
| } |
| |
| } // namespace |
| } // namespace test |
| } // namespace net |