| // 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/third_party/quiche/src/quic/core/quic_connection.h" |
| |
| #include <errno.h> |
| |
| #include <memory> |
| #include <ostream> |
| #include <string> |
| #include <utility> |
| |
| #include "net/third_party/quiche/src/quic/core/congestion_control/loss_detection_interface.h" |
| #include "net/third_party/quiche/src/quic/core/congestion_control/send_algorithm_interface.h" |
| #include "net/third_party/quiche/src/quic/core/crypto/null_decrypter.h" |
| #include "net/third_party/quiche/src/quic/core/crypto/null_encrypter.h" |
| #include "net/third_party/quiche/src/quic/core/crypto/quic_decrypter.h" |
| #include "net/third_party/quiche/src/quic/core/crypto/quic_encrypter.h" |
| #include "net/third_party/quiche/src/quic/core/quic_connection_id.h" |
| #include "net/third_party/quiche/src/quic/core/quic_packets.h" |
| #include "net/third_party/quiche/src/quic/core/quic_simple_buffer_allocator.h" |
| #include "net/third_party/quiche/src/quic/core/quic_types.h" |
| #include "net/third_party/quiche/src/quic/core/quic_utils.h" |
| #include "net/third_party/quiche/src/quic/platform/api/quic_error_code_wrappers.h" |
| #include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h" |
| #include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" |
| #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" |
| #include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" |
| #include "net/third_party/quiche/src/quic/platform/api/quic_reference_counted.h" |
| #include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h" |
| #include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" |
| #include "net/third_party/quiche/src/quic/platform/api/quic_test.h" |
| #include "net/third_party/quiche/src/quic/test_tools/mock_clock.h" |
| #include "net/third_party/quiche/src/quic/test_tools/mock_random.h" |
| #include "net/third_party/quiche/src/quic/test_tools/quic_config_peer.h" |
| #include "net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h" |
| #include "net/third_party/quiche/src/quic/test_tools/quic_framer_peer.h" |
| #include "net/third_party/quiche/src/quic/test_tools/quic_packet_creator_peer.h" |
| #include "net/third_party/quiche/src/quic/test_tools/quic_packet_generator_peer.h" |
| #include "net/third_party/quiche/src/quic/test_tools/quic_sent_packet_manager_peer.h" |
| #include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" |
| #include "net/third_party/quiche/src/quic/test_tools/simple_data_producer.h" |
| #include "net/third_party/quiche/src/quic/test_tools/simple_quic_framer.h" |
| #include "net/third_party/quiche/src/quic/test_tools/simple_session_notifier.h" |
| |
| using testing::_; |
| using testing::AnyNumber; |
| using testing::AtLeast; |
| using testing::DoAll; |
| using testing::Exactly; |
| using testing::Ge; |
| using testing::IgnoreResult; |
| using testing::InSequence; |
| using testing::Invoke; |
| using testing::InvokeWithoutArgs; |
| using testing::Lt; |
| using testing::Ref; |
| using testing::Return; |
| using testing::SaveArg; |
| using testing::SetArgPointee; |
| using testing::StrictMock; |
| |
| namespace quic { |
| namespace test { |
| namespace { |
| |
| const char data1[] = "foo data"; |
| const char data2[] = "bar data"; |
| |
| const bool kHasStopWaiting = true; |
| |
| const int kDefaultRetransmissionTimeMs = 500; |
| |
| DiversificationNonce kTestDiversificationNonce = { |
| 'a', 'b', 'a', 'b', 'a', 'b', 'a', 'b', 'a', 'b', 'a', |
| 'b', 'a', 'b', 'a', 'b', 'a', 'b', 'a', 'b', 'a', 'b', |
| 'a', 'b', 'a', 'b', 'a', 'b', 'a', 'b', 'a', 'b', |
| }; |
| |
| const QuicSocketAddress kPeerAddress = |
| QuicSocketAddress(QuicIpAddress::Loopback6(), |
| /*port=*/12345); |
| const QuicSocketAddress kSelfAddress = |
| QuicSocketAddress(QuicIpAddress::Loopback6(), |
| /*port=*/443); |
| |
| Perspective InvertPerspective(Perspective perspective) { |
| return perspective == Perspective::IS_CLIENT ? Perspective::IS_SERVER |
| : Perspective::IS_CLIENT; |
| } |
| |
| QuicStreamId GetNthClientInitiatedStreamId(int n, |
| QuicTransportVersion version) { |
| return QuicUtils::GetHeadersStreamId(version) + n * 2; |
| } |
| |
| QuicLongHeaderType EncryptionlevelToLongHeaderType(EncryptionLevel level) { |
| switch (level) { |
| case ENCRYPTION_INITIAL: |
| return INITIAL; |
| case ENCRYPTION_HANDSHAKE: |
| return HANDSHAKE; |
| case ENCRYPTION_ZERO_RTT: |
| return ZERO_RTT_PROTECTED; |
| case ENCRYPTION_FORWARD_SECURE: |
| DCHECK(false); |
| return INVALID_PACKET_TYPE; |
| default: |
| DCHECK(false); |
| return INVALID_PACKET_TYPE; |
| } |
| } |
| |
| // TaggingEncrypter appends kTagSize bytes of |tag| to the end of each message. |
| class TaggingEncrypter : public QuicEncrypter { |
| public: |
| explicit TaggingEncrypter(uint8_t tag) : tag_(tag) {} |
| TaggingEncrypter(const TaggingEncrypter&) = delete; |
| TaggingEncrypter& operator=(const TaggingEncrypter&) = delete; |
| |
| ~TaggingEncrypter() override {} |
| |
| // QuicEncrypter interface. |
| bool SetKey(QuicStringPiece key) override { return true; } |
| |
| bool SetNoncePrefix(QuicStringPiece nonce_prefix) override { return true; } |
| |
| bool SetIV(QuicStringPiece iv) override { return true; } |
| |
| bool SetHeaderProtectionKey(QuicStringPiece key) override { return true; } |
| |
| bool EncryptPacket(uint64_t packet_number, |
| QuicStringPiece associated_data, |
| QuicStringPiece plaintext, |
| char* output, |
| size_t* output_length, |
| size_t max_output_length) override { |
| const size_t len = plaintext.size() + kTagSize; |
| if (max_output_length < len) { |
| return false; |
| } |
| // Memmove is safe for inplace encryption. |
| memmove(output, plaintext.data(), plaintext.size()); |
| output += plaintext.size(); |
| memset(output, tag_, kTagSize); |
| *output_length = len; |
| return true; |
| } |
| |
| std::string GenerateHeaderProtectionMask(QuicStringPiece sample) override { |
| return std::string(5, 0); |
| } |
| |
| size_t GetKeySize() const override { return 0; } |
| size_t GetNoncePrefixSize() const override { return 0; } |
| size_t GetIVSize() const override { return 0; } |
| |
| size_t GetMaxPlaintextSize(size_t ciphertext_size) const override { |
| return ciphertext_size - kTagSize; |
| } |
| |
| size_t GetCiphertextSize(size_t plaintext_size) const override { |
| return plaintext_size + kTagSize; |
| } |
| |
| QuicStringPiece GetKey() const override { return QuicStringPiece(); } |
| |
| QuicStringPiece GetNoncePrefix() const override { return QuicStringPiece(); } |
| |
| private: |
| enum { |
| kTagSize = 12, |
| }; |
| |
| const uint8_t tag_; |
| }; |
| |
| // TaggingDecrypter ensures that the final kTagSize bytes of the message all |
| // have the same value and then removes them. |
| class TaggingDecrypter : public QuicDecrypter { |
| public: |
| ~TaggingDecrypter() override {} |
| |
| // QuicDecrypter interface |
| bool SetKey(QuicStringPiece key) override { return true; } |
| |
| bool SetNoncePrefix(QuicStringPiece nonce_prefix) override { return true; } |
| |
| bool SetIV(QuicStringPiece iv) override { return true; } |
| |
| bool SetHeaderProtectionKey(QuicStringPiece key) override { return true; } |
| |
| bool SetPreliminaryKey(QuicStringPiece key) override { |
| QUIC_BUG << "should not be called"; |
| return false; |
| } |
| |
| bool SetDiversificationNonce(const DiversificationNonce& key) override { |
| return true; |
| } |
| |
| bool DecryptPacket(uint64_t packet_number, |
| QuicStringPiece associated_data, |
| QuicStringPiece ciphertext, |
| char* output, |
| size_t* output_length, |
| size_t max_output_length) override { |
| if (ciphertext.size() < kTagSize) { |
| return false; |
| } |
| if (!CheckTag(ciphertext, GetTag(ciphertext))) { |
| return false; |
| } |
| *output_length = ciphertext.size() - kTagSize; |
| memcpy(output, ciphertext.data(), *output_length); |
| return true; |
| } |
| |
| std::string GenerateHeaderProtectionMask( |
| QuicDataReader* sample_reader) override { |
| return std::string(5, 0); |
| } |
| |
| size_t GetKeySize() const override { return 0; } |
| size_t GetIVSize() const override { return 0; } |
| QuicStringPiece GetKey() const override { return QuicStringPiece(); } |
| QuicStringPiece GetNoncePrefix() const override { return QuicStringPiece(); } |
| // Use a distinct value starting with 0xFFFFFF, which is never used by TLS. |
| uint32_t cipher_id() const override { return 0xFFFFFFF0; } |
| |
| protected: |
| virtual uint8_t GetTag(QuicStringPiece ciphertext) { |
| return ciphertext.data()[ciphertext.size() - 1]; |
| } |
| |
| private: |
| enum { |
| kTagSize = 12, |
| }; |
| |
| bool CheckTag(QuicStringPiece ciphertext, uint8_t tag) { |
| for (size_t i = ciphertext.size() - kTagSize; i < ciphertext.size(); i++) { |
| if (ciphertext.data()[i] != tag) { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| }; |
| |
| // StringTaggingDecrypter ensures that the final kTagSize bytes of the message |
| // match the expected value. |
| class StrictTaggingDecrypter : public TaggingDecrypter { |
| public: |
| explicit StrictTaggingDecrypter(uint8_t tag) : tag_(tag) {} |
| ~StrictTaggingDecrypter() override {} |
| |
| // TaggingQuicDecrypter |
| uint8_t GetTag(QuicStringPiece ciphertext) override { return tag_; } |
| |
| // Use a distinct value starting with 0xFFFFFF, which is never used by TLS. |
| uint32_t cipher_id() const override { return 0xFFFFFFF1; } |
| |
| private: |
| const uint8_t tag_; |
| }; |
| |
| class TestConnectionHelper : public QuicConnectionHelperInterface { |
| public: |
| TestConnectionHelper(MockClock* clock, MockRandom* random_generator) |
| : clock_(clock), random_generator_(random_generator) { |
| clock_->AdvanceTime(QuicTime::Delta::FromSeconds(1)); |
| } |
| TestConnectionHelper(const TestConnectionHelper&) = delete; |
| TestConnectionHelper& operator=(const TestConnectionHelper&) = delete; |
| |
| // QuicConnectionHelperInterface |
| const QuicClock* GetClock() const override { return clock_; } |
| |
| QuicRandom* GetRandomGenerator() override { return random_generator_; } |
| |
| QuicBufferAllocator* GetStreamSendBufferAllocator() override { |
| return &buffer_allocator_; |
| } |
| |
| private: |
| MockClock* clock_; |
| MockRandom* random_generator_; |
| SimpleBufferAllocator buffer_allocator_; |
| }; |
| |
| class TestAlarmFactory : public QuicAlarmFactory { |
| public: |
| class TestAlarm : public QuicAlarm { |
| public: |
| explicit TestAlarm(QuicArenaScopedPtr<QuicAlarm::Delegate> delegate) |
| : QuicAlarm(std::move(delegate)) {} |
| |
| void SetImpl() override {} |
| void CancelImpl() override {} |
| using QuicAlarm::Fire; |
| }; |
| |
| TestAlarmFactory() {} |
| TestAlarmFactory(const TestAlarmFactory&) = delete; |
| TestAlarmFactory& operator=(const TestAlarmFactory&) = delete; |
| |
| QuicAlarm* CreateAlarm(QuicAlarm::Delegate* delegate) override { |
| return new TestAlarm(QuicArenaScopedPtr<QuicAlarm::Delegate>(delegate)); |
| } |
| |
| QuicArenaScopedPtr<QuicAlarm> CreateAlarm( |
| QuicArenaScopedPtr<QuicAlarm::Delegate> delegate, |
| QuicConnectionArena* arena) override { |
| return arena->New<TestAlarm>(std::move(delegate)); |
| } |
| }; |
| |
| class TestPacketWriter : public QuicPacketWriter { |
| public: |
| TestPacketWriter(ParsedQuicVersion version, MockClock* clock) |
| : version_(version), |
| framer_(SupportedVersions(version_), Perspective::IS_SERVER), |
| last_packet_size_(0), |
| write_blocked_(false), |
| write_should_fail_(false), |
| block_on_next_flush_(false), |
| block_on_next_write_(false), |
| next_packet_too_large_(false), |
| always_get_packet_too_large_(false), |
| is_write_blocked_data_buffered_(false), |
| is_batch_mode_(false), |
| final_bytes_of_last_packet_(0), |
| final_bytes_of_previous_packet_(0), |
| use_tagging_decrypter_(false), |
| packets_write_attempts_(0), |
| clock_(clock), |
| write_pause_time_delta_(QuicTime::Delta::Zero()), |
| max_packet_size_(kMaxOutgoingPacketSize), |
| supports_release_time_(false) {} |
| TestPacketWriter(const TestPacketWriter&) = delete; |
| TestPacketWriter& operator=(const TestPacketWriter&) = delete; |
| |
| // QuicPacketWriter interface |
| WriteResult WritePacket(const char* buffer, |
| size_t buf_len, |
| const QuicIpAddress& self_address, |
| const QuicSocketAddress& peer_address, |
| PerPacketOptions* options) override { |
| QuicEncryptedPacket packet(buffer, buf_len); |
| ++packets_write_attempts_; |
| |
| if (packet.length() >= sizeof(final_bytes_of_last_packet_)) { |
| final_bytes_of_previous_packet_ = final_bytes_of_last_packet_; |
| memcpy(&final_bytes_of_last_packet_, packet.data() + packet.length() - 4, |
| sizeof(final_bytes_of_last_packet_)); |
| } |
| |
| if (use_tagging_decrypter_) { |
| if (framer_.framer()->version().KnowsWhichDecrypterToUse()) { |
| framer_.framer()->InstallDecrypter(ENCRYPTION_INITIAL, |
| QuicMakeUnique<TaggingDecrypter>()); |
| framer_.framer()->InstallDecrypter(ENCRYPTION_ZERO_RTT, |
| QuicMakeUnique<TaggingDecrypter>()); |
| framer_.framer()->InstallDecrypter(ENCRYPTION_FORWARD_SECURE, |
| QuicMakeUnique<TaggingDecrypter>()); |
| } else { |
| framer_.framer()->SetDecrypter(ENCRYPTION_INITIAL, |
| QuicMakeUnique<TaggingDecrypter>()); |
| } |
| } else if (framer_.framer()->version().KnowsWhichDecrypterToUse()) { |
| framer_.framer()->InstallDecrypter( |
| ENCRYPTION_FORWARD_SECURE, |
| QuicMakeUnique<NullDecrypter>(Perspective::IS_SERVER)); |
| } |
| EXPECT_TRUE(framer_.ProcessPacket(packet)); |
| if (block_on_next_write_) { |
| write_blocked_ = true; |
| block_on_next_write_ = false; |
| } |
| if (next_packet_too_large_) { |
| next_packet_too_large_ = false; |
| return WriteResult(WRITE_STATUS_ERROR, QUIC_EMSGSIZE); |
| } |
| if (always_get_packet_too_large_) { |
| return WriteResult(WRITE_STATUS_ERROR, QUIC_EMSGSIZE); |
| } |
| if (IsWriteBlocked()) { |
| return WriteResult(is_write_blocked_data_buffered_ |
| ? WRITE_STATUS_BLOCKED_DATA_BUFFERED |
| : WRITE_STATUS_BLOCKED, |
| 0); |
| } |
| |
| if (ShouldWriteFail()) { |
| return WriteResult(WRITE_STATUS_ERROR, 0); |
| } |
| |
| last_packet_size_ = packet.length(); |
| last_packet_header_ = framer_.header(); |
| |
| if (!write_pause_time_delta_.IsZero()) { |
| clock_->AdvanceTime(write_pause_time_delta_); |
| } |
| return WriteResult(WRITE_STATUS_OK, last_packet_size_); |
| } |
| |
| bool ShouldWriteFail() { return write_should_fail_; } |
| |
| bool IsWriteBlocked() const override { return write_blocked_; } |
| |
| void SetWriteBlocked() { write_blocked_ = true; } |
| |
| void SetWritable() override { write_blocked_ = false; } |
| |
| void SetShouldWriteFail() { write_should_fail_ = true; } |
| |
| QuicByteCount GetMaxPacketSize( |
| const QuicSocketAddress& /*peer_address*/) const override { |
| return max_packet_size_; |
| } |
| |
| bool SupportsReleaseTime() const { return supports_release_time_; } |
| |
| bool IsBatchMode() const override { return is_batch_mode_; } |
| |
| char* GetNextWriteLocation(const QuicIpAddress& self_address, |
| const QuicSocketAddress& peer_address) override { |
| return nullptr; |
| } |
| |
| WriteResult Flush() override { |
| if (block_on_next_flush_) { |
| block_on_next_flush_ = false; |
| SetWriteBlocked(); |
| return WriteResult(WRITE_STATUS_BLOCKED, /*errno*/ -1); |
| } |
| return WriteResult(WRITE_STATUS_OK, 0); |
| } |
| |
| void BlockOnNextFlush() { block_on_next_flush_ = true; } |
| |
| void BlockOnNextWrite() { block_on_next_write_ = true; } |
| |
| void SimulateNextPacketTooLarge() { next_packet_too_large_ = true; } |
| |
| void AlwaysGetPacketTooLarge() { always_get_packet_too_large_ = true; } |
| |
| // Sets the amount of time that the writer should before the actual write. |
| void SetWritePauseTimeDelta(QuicTime::Delta delta) { |
| write_pause_time_delta_ = delta; |
| } |
| |
| void SetBatchMode(bool new_value) { is_batch_mode_ = new_value; } |
| |
| const QuicPacketHeader& header() { return framer_.header(); } |
| |
| size_t frame_count() const { return framer_.num_frames(); } |
| |
| const std::vector<QuicAckFrame>& ack_frames() const { |
| return framer_.ack_frames(); |
| } |
| |
| const std::vector<QuicStopWaitingFrame>& stop_waiting_frames() const { |
| return framer_.stop_waiting_frames(); |
| } |
| |
| const std::vector<QuicConnectionCloseFrame>& connection_close_frames() const { |
| return framer_.connection_close_frames(); |
| } |
| |
| const std::vector<QuicRstStreamFrame>& rst_stream_frames() const { |
| return framer_.rst_stream_frames(); |
| } |
| |
| const std::vector<std::unique_ptr<QuicStreamFrame>>& stream_frames() const { |
| return framer_.stream_frames(); |
| } |
| |
| const std::vector<std::unique_ptr<QuicCryptoFrame>>& crypto_frames() const { |
| return framer_.crypto_frames(); |
| } |
| |
| const std::vector<QuicPingFrame>& ping_frames() const { |
| return framer_.ping_frames(); |
| } |
| |
| const std::vector<QuicMessageFrame>& message_frames() const { |
| return framer_.message_frames(); |
| } |
| |
| const std::vector<QuicWindowUpdateFrame>& window_update_frames() const { |
| return framer_.window_update_frames(); |
| } |
| |
| const std::vector<QuicPaddingFrame>& padding_frames() const { |
| return framer_.padding_frames(); |
| } |
| |
| const std::vector<QuicPathChallengeFrame>& path_challenge_frames() const { |
| return framer_.path_challenge_frames(); |
| } |
| |
| const std::vector<QuicPathResponseFrame>& path_response_frames() const { |
| return framer_.path_response_frames(); |
| } |
| |
| size_t last_packet_size() { return last_packet_size_; } |
| |
| const QuicPacketHeader& last_packet_header() const { |
| return last_packet_header_; |
| } |
| |
| const QuicVersionNegotiationPacket* version_negotiation_packet() { |
| return framer_.version_negotiation_packet(); |
| } |
| |
| void set_is_write_blocked_data_buffered(bool buffered) { |
| is_write_blocked_data_buffered_ = buffered; |
| } |
| |
| void set_perspective(Perspective perspective) { |
| // We invert perspective here, because the framer needs to parse packets |
| // we send. |
| QuicFramerPeer::SetPerspective(framer_.framer(), |
| InvertPerspective(perspective)); |
| } |
| |
| // final_bytes_of_last_packet_ returns the last four bytes of the previous |
| // packet as a little-endian, uint32_t. This is intended to be used with a |
| // TaggingEncrypter so that tests can determine which encrypter was used for |
| // a given packet. |
| uint32_t final_bytes_of_last_packet() { return final_bytes_of_last_packet_; } |
| |
| // Returns the final bytes of the second to last packet. |
| uint32_t final_bytes_of_previous_packet() { |
| return final_bytes_of_previous_packet_; |
| } |
| |
| void use_tagging_decrypter() { use_tagging_decrypter_ = true; } |
| |
| uint32_t packets_write_attempts() { return packets_write_attempts_; } |
| |
| void Reset() { framer_.Reset(); } |
| |
| void SetSupportedVersions(const ParsedQuicVersionVector& versions) { |
| framer_.SetSupportedVersions(versions); |
| } |
| |
| void set_max_packet_size(QuicByteCount max_packet_size) { |
| max_packet_size_ = max_packet_size; |
| } |
| |
| void set_supports_release_time(bool supports_release_time) { |
| supports_release_time_ = supports_release_time; |
| } |
| |
| SimpleQuicFramer* framer() { return &framer_; } |
| |
| private: |
| ParsedQuicVersion version_; |
| SimpleQuicFramer framer_; |
| size_t last_packet_size_; |
| QuicPacketHeader last_packet_header_; |
| bool write_blocked_; |
| bool write_should_fail_; |
| bool block_on_next_flush_; |
| bool block_on_next_write_; |
| bool next_packet_too_large_; |
| bool always_get_packet_too_large_; |
| bool is_write_blocked_data_buffered_; |
| bool is_batch_mode_; |
| uint32_t final_bytes_of_last_packet_; |
| uint32_t final_bytes_of_previous_packet_; |
| bool use_tagging_decrypter_; |
| uint32_t packets_write_attempts_; |
| MockClock* clock_; |
| // If non-zero, the clock will pause during WritePacket for this amount of |
| // time. |
| QuicTime::Delta write_pause_time_delta_; |
| QuicByteCount max_packet_size_; |
| bool supports_release_time_; |
| }; |
| |
| class TestConnection : public QuicConnection { |
| public: |
| TestConnection(QuicConnectionId connection_id, |
| QuicSocketAddress address, |
| TestConnectionHelper* helper, |
| TestAlarmFactory* alarm_factory, |
| TestPacketWriter* writer, |
| Perspective perspective, |
| ParsedQuicVersion version) |
| : QuicConnection(connection_id, |
| address, |
| helper, |
| alarm_factory, |
| writer, |
| /* owns_writer= */ false, |
| perspective, |
| SupportedVersions(version)), |
| notifier_(nullptr) { |
| writer->set_perspective(perspective); |
| SetEncrypter(ENCRYPTION_FORWARD_SECURE, |
| QuicMakeUnique<NullEncrypter>(perspective)); |
| SetDataProducer(&producer_); |
| } |
| TestConnection(const TestConnection&) = delete; |
| TestConnection& operator=(const TestConnection&) = delete; |
| |
| void SendAck() { QuicConnectionPeer::SendAck(this); } |
| |
| void SetSendAlgorithm(SendAlgorithmInterface* send_algorithm) { |
| QuicConnectionPeer::SetSendAlgorithm(this, send_algorithm); |
| } |
| |
| void SetLossAlgorithm(LossDetectionInterface* loss_algorithm) { |
| QuicConnectionPeer::SetLossAlgorithm(this, loss_algorithm); |
| } |
| |
| void SendPacket(EncryptionLevel level, |
| uint64_t packet_number, |
| std::unique_ptr<QuicPacket> packet, |
| HasRetransmittableData retransmittable, |
| bool has_ack, |
| bool has_pending_frames) { |
| char buffer[kMaxOutgoingPacketSize]; |
| size_t encrypted_length = |
| QuicConnectionPeer::GetFramer(this)->EncryptPayload( |
| ENCRYPTION_INITIAL, QuicPacketNumber(packet_number), *packet, |
| buffer, kMaxOutgoingPacketSize); |
| SerializedPacket serialized_packet( |
| QuicPacketNumber(packet_number), PACKET_4BYTE_PACKET_NUMBER, buffer, |
| encrypted_length, has_ack, has_pending_frames); |
| if (retransmittable == HAS_RETRANSMITTABLE_DATA) { |
| serialized_packet.retransmittable_frames.push_back( |
| QuicFrame(QuicStreamFrame())); |
| } |
| OnSerializedPacket(&serialized_packet); |
| } |
| |
| QuicConsumedData SaveAndSendStreamData(QuicStreamId id, |
| const struct iovec* iov, |
| int iov_count, |
| size_t total_length, |
| QuicStreamOffset offset, |
| StreamSendingState state) { |
| ScopedPacketFlusher flusher(this, NO_ACK); |
| producer_.SaveStreamData(id, iov, iov_count, 0u, total_length); |
| if (notifier_ != nullptr) { |
| return notifier_->WriteOrBufferData(id, total_length, state); |
| } |
| return QuicConnection::SendStreamData(id, total_length, offset, state); |
| } |
| |
| QuicConsumedData SendStreamDataWithString(QuicStreamId id, |
| QuicStringPiece data, |
| QuicStreamOffset offset, |
| StreamSendingState state) { |
| ScopedPacketFlusher flusher(this, NO_ACK); |
| if (!QuicUtils::IsCryptoStreamId(transport_version(), id) && |
| this->encryption_level() == ENCRYPTION_INITIAL) { |
| this->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); |
| } |
| struct iovec iov; |
| MakeIOVector(data, &iov); |
| return SaveAndSendStreamData(id, &iov, 1, data.length(), offset, state); |
| } |
| |
| QuicConsumedData SendApplicationDataAtLevel(EncryptionLevel encryption_level, |
| QuicStreamId id, |
| QuicStringPiece data, |
| QuicStreamOffset offset, |
| StreamSendingState state) { |
| ScopedPacketFlusher flusher(this, NO_ACK); |
| DCHECK(encryption_level >= ENCRYPTION_ZERO_RTT); |
| SetEncrypter(encryption_level, QuicMakeUnique<TaggingEncrypter>(0x01)); |
| SetDefaultEncryptionLevel(encryption_level); |
| struct iovec iov; |
| MakeIOVector(data, &iov); |
| return SaveAndSendStreamData(id, &iov, 1, data.length(), offset, state); |
| } |
| |
| QuicConsumedData SendStreamData3() { |
| return SendStreamDataWithString( |
| GetNthClientInitiatedStreamId(1, transport_version()), "food", 0, |
| NO_FIN); |
| } |
| |
| QuicConsumedData SendStreamData5() { |
| return SendStreamDataWithString( |
| GetNthClientInitiatedStreamId(2, transport_version()), "food2", 0, |
| NO_FIN); |
| } |
| |
| // Ensures the connection can write stream data before writing. |
| QuicConsumedData EnsureWritableAndSendStreamData5() { |
| EXPECT_TRUE(CanWriteStreamData()); |
| return SendStreamData5(); |
| } |
| |
| // The crypto stream has special semantics so that it is not blocked by a |
| // congestion window limitation, and also so that it gets put into a separate |
| // packet (so that it is easier to reason about a crypto frame not being |
| // split needlessly across packet boundaries). As a result, we have separate |
| // tests for some cases for this stream. |
| QuicConsumedData SendCryptoStreamData() { |
| QuicStreamOffset offset = 0; |
| QuicStringPiece data("chlo"); |
| return SendCryptoDataWithString(data, offset); |
| } |
| |
| QuicConsumedData SendCryptoDataWithString(QuicStringPiece data, |
| QuicStreamOffset offset) { |
| if (!QuicVersionUsesCryptoFrames(transport_version())) { |
| return SendStreamDataWithString( |
| QuicUtils::GetCryptoStreamId(transport_version()), data, offset, |
| NO_FIN); |
| } |
| producer_.SaveCryptoData(ENCRYPTION_INITIAL, offset, data); |
| size_t bytes_written; |
| if (notifier_) { |
| bytes_written = |
| notifier_->WriteCryptoData(ENCRYPTION_INITIAL, data.length(), offset); |
| } else { |
| bytes_written = QuicConnection::SendCryptoData(ENCRYPTION_INITIAL, |
| data.length(), offset); |
| } |
| return QuicConsumedData(bytes_written, /*fin_consumed*/ false); |
| } |
| |
| void set_version(ParsedQuicVersion version) { |
| QuicConnectionPeer::GetFramer(this)->set_version(version); |
| } |
| |
| void SetSupportedVersions(const ParsedQuicVersionVector& versions) { |
| QuicConnectionPeer::GetFramer(this)->SetSupportedVersions(versions); |
| QuicConnectionPeer::SetNoVersionNegotiation(this, versions.size() == 1); |
| writer()->SetSupportedVersions(versions); |
| } |
| |
| void set_perspective(Perspective perspective) { |
| writer()->set_perspective(perspective); |
| QuicConnectionPeer::SetPerspective(this, perspective); |
| } |
| |
| // Enable path MTU discovery. Assumes that the test is performed from the |
| // client perspective and the higher value of MTU target is used. |
| void EnablePathMtuDiscovery(MockSendAlgorithm* send_algorithm) { |
| ASSERT_EQ(Perspective::IS_CLIENT, perspective()); |
| |
| QuicConfig config; |
| QuicTagVector connection_options; |
| connection_options.push_back(kMTUH); |
| config.SetConnectionOptionsToSend(connection_options); |
| EXPECT_CALL(*send_algorithm, SetFromConfig(_, _)); |
| SetFromConfig(config); |
| |
| // Normally, the pacing would be disabled in the test, but calling |
| // SetFromConfig enables it. Set nearly-infinite bandwidth to make the |
| // pacing algorithm work. |
| EXPECT_CALL(*send_algorithm, PacingRate(_)) |
| .WillRepeatedly(Return(QuicBandwidth::Infinite())); |
| } |
| |
| TestAlarmFactory::TestAlarm* GetAckAlarm() { |
| return reinterpret_cast<TestAlarmFactory::TestAlarm*>( |
| QuicConnectionPeer::GetAckAlarm(this)); |
| } |
| |
| TestAlarmFactory::TestAlarm* GetPingAlarm() { |
| return reinterpret_cast<TestAlarmFactory::TestAlarm*>( |
| QuicConnectionPeer::GetPingAlarm(this)); |
| } |
| |
| TestAlarmFactory::TestAlarm* GetRetransmissionAlarm() { |
| return reinterpret_cast<TestAlarmFactory::TestAlarm*>( |
| QuicConnectionPeer::GetRetransmissionAlarm(this)); |
| } |
| |
| TestAlarmFactory::TestAlarm* GetSendAlarm() { |
| return reinterpret_cast<TestAlarmFactory::TestAlarm*>( |
| QuicConnectionPeer::GetSendAlarm(this)); |
| } |
| |
| TestAlarmFactory::TestAlarm* GetTimeoutAlarm() { |
| return reinterpret_cast<TestAlarmFactory::TestAlarm*>( |
| QuicConnectionPeer::GetTimeoutAlarm(this)); |
| } |
| |
| TestAlarmFactory::TestAlarm* GetMtuDiscoveryAlarm() { |
| return reinterpret_cast<TestAlarmFactory::TestAlarm*>( |
| QuicConnectionPeer::GetMtuDiscoveryAlarm(this)); |
| } |
| |
| TestAlarmFactory::TestAlarm* GetPathDegradingAlarm() { |
| return reinterpret_cast<TestAlarmFactory::TestAlarm*>( |
| QuicConnectionPeer::GetPathDegradingAlarm(this)); |
| } |
| |
| TestAlarmFactory::TestAlarm* GetProcessUndecryptablePacketsAlarm() { |
| return reinterpret_cast<TestAlarmFactory::TestAlarm*>( |
| QuicConnectionPeer::GetProcessUndecryptablePacketsAlarm(this)); |
| } |
| |
| void SetMaxTailLossProbes(size_t max_tail_loss_probes) { |
| QuicSentPacketManagerPeer::SetMaxTailLossProbes( |
| QuicConnectionPeer::GetSentPacketManager(this), max_tail_loss_probes); |
| } |
| |
| QuicByteCount GetBytesInFlight() { |
| return QuicConnectionPeer::GetSentPacketManager(this)->GetBytesInFlight(); |
| } |
| |
| void set_notifier(SimpleSessionNotifier* notifier) { notifier_ = notifier; } |
| |
| void ReturnEffectivePeerAddressForNextPacket(const QuicSocketAddress& addr) { |
| next_effective_peer_addr_ = QuicMakeUnique<QuicSocketAddress>(addr); |
| } |
| |
| SimpleDataProducer* producer() { return &producer_; } |
| |
| using QuicConnection::active_effective_peer_migration_type; |
| using QuicConnection::IsCurrentPacketConnectivityProbing; |
| using QuicConnection::SelectMutualVersion; |
| using QuicConnection::SendProbingRetransmissions; |
| using QuicConnection::set_defer_send_in_response_to_packets; |
| |
| protected: |
| QuicSocketAddress GetEffectivePeerAddressFromCurrentPacket() const override { |
| if (next_effective_peer_addr_) { |
| return *std::move(next_effective_peer_addr_); |
| } |
| return QuicConnection::GetEffectivePeerAddressFromCurrentPacket(); |
| } |
| |
| private: |
| TestPacketWriter* writer() { |
| return static_cast<TestPacketWriter*>(QuicConnection::writer()); |
| } |
| |
| SimpleDataProducer producer_; |
| |
| SimpleSessionNotifier* notifier_; |
| |
| std::unique_ptr<QuicSocketAddress> next_effective_peer_addr_; |
| }; |
| |
| enum class AckResponse { kDefer, kImmediate }; |
| |
| // Run tests with combinations of {ParsedQuicVersion, AckResponse}. |
| struct TestParams { |
| TestParams(ParsedQuicVersion version, |
| AckResponse ack_response, |
| bool no_stop_waiting) |
| : version(version), |
| ack_response(ack_response), |
| no_stop_waiting(no_stop_waiting) {} |
| |
| friend std::ostream& operator<<(std::ostream& os, const TestParams& p) { |
| os << "{ client_version: " << ParsedQuicVersionToString(p.version) |
| << " ack_response: " |
| << (p.ack_response == AckResponse::kDefer ? "defer" : "immediate") |
| << " no_stop_waiting: " << p.no_stop_waiting << " }"; |
| return os; |
| } |
| |
| ParsedQuicVersion version; |
| AckResponse ack_response; |
| bool no_stop_waiting; |
| }; |
| |
| // Constructs various test permutations. |
| std::vector<TestParams> GetTestParams() { |
| QuicFlagSaver flags; |
| SetQuicFlag(FLAGS_quic_supports_tls_handshake, true); |
| std::vector<TestParams> params; |
| ParsedQuicVersionVector all_supported_versions = AllSupportedVersions(); |
| for (size_t i = 0; i < all_supported_versions.size(); ++i) { |
| for (AckResponse ack_response : |
| {AckResponse::kDefer, AckResponse::kImmediate}) { |
| for (bool no_stop_waiting : {true, false}) { |
| // After version 43, never use STOP_WAITING. |
| if (all_supported_versions[i].transport_version <= QUIC_VERSION_43 || |
| no_stop_waiting) { |
| params.push_back(TestParams(all_supported_versions[i], ack_response, |
| no_stop_waiting)); |
| } |
| } |
| } |
| } |
| return params; |
| } |
| |
| class QuicConnectionTest : public QuicTestWithParam<TestParams> { |
| protected: |
| QuicConnectionTest() |
| : connection_id_(TestConnectionId()), |
| framer_(SupportedVersions(version()), |
| QuicTime::Zero(), |
| Perspective::IS_CLIENT, |
| connection_id_.length()), |
| send_algorithm_(new StrictMock<MockSendAlgorithm>), |
| loss_algorithm_(new MockLossAlgorithm()), |
| helper_(new TestConnectionHelper(&clock_, &random_generator_)), |
| alarm_factory_(new TestAlarmFactory()), |
| peer_framer_(SupportedVersions(version()), |
| QuicTime::Zero(), |
| Perspective::IS_SERVER, |
| connection_id_.length()), |
| peer_creator_(connection_id_, |
| &peer_framer_, |
| /*delegate=*/nullptr), |
| writer_(new TestPacketWriter(version(), &clock_)), |
| connection_(connection_id_, |
| kPeerAddress, |
| helper_.get(), |
| alarm_factory_.get(), |
| writer_.get(), |
| Perspective::IS_CLIENT, |
| version()), |
| creator_(QuicConnectionPeer::GetPacketCreator(&connection_)), |
| generator_(QuicConnectionPeer::GetPacketGenerator(&connection_)), |
| manager_(QuicConnectionPeer::GetSentPacketManager(&connection_)), |
| frame1_(0, false, 0, QuicStringPiece(data1)), |
| frame2_(0, false, 3, QuicStringPiece(data2)), |
| crypto_frame_(ENCRYPTION_INITIAL, 0, QuicStringPiece(data1)), |
| packet_number_length_(PACKET_4BYTE_PACKET_NUMBER), |
| connection_id_included_(CONNECTION_ID_PRESENT), |
| notifier_(&connection_) { |
| SetQuicFlag(FLAGS_quic_supports_tls_handshake, true); |
| connection_.set_defer_send_in_response_to_packets(GetParam().ack_response == |
| AckResponse::kDefer); |
| for (EncryptionLevel level : |
| {ENCRYPTION_ZERO_RTT, ENCRYPTION_FORWARD_SECURE}) { |
| peer_creator_.SetEncrypter( |
| level, QuicMakeUnique<NullEncrypter>(peer_framer_.perspective())); |
| } |
| QuicFramerPeer::SetLastSerializedServerConnectionId( |
| QuicConnectionPeer::GetFramer(&connection_), connection_id_); |
| if (version().transport_version > QUIC_VERSION_43) { |
| EXPECT_TRUE(QuicConnectionPeer::GetNoStopWaitingFrames(&connection_)); |
| } else { |
| QuicConnectionPeer::SetNoStopWaitingFrames(&connection_, |
| GetParam().no_stop_waiting); |
| } |
| QuicStreamId stream_id; |
| if (QuicVersionUsesCryptoFrames(version().transport_version)) { |
| stream_id = QuicUtils::GetFirstBidirectionalStreamId( |
| version().transport_version, Perspective::IS_CLIENT); |
| } else { |
| stream_id = QuicUtils::GetCryptoStreamId(version().transport_version); |
| } |
| frame1_.stream_id = stream_id; |
| frame2_.stream_id = stream_id; |
| connection_.set_visitor(&visitor_); |
| if (connection_.session_decides_what_to_write()) { |
| connection_.SetSessionNotifier(¬ifier_); |
| connection_.set_notifier(¬ifier_); |
| } |
| connection_.SetSendAlgorithm(send_algorithm_); |
| connection_.SetLossAlgorithm(loss_algorithm_.get()); |
| EXPECT_CALL(*send_algorithm_, CanSend(_)).WillRepeatedly(Return(true)); |
| EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)) |
| .Times(AnyNumber()); |
| EXPECT_CALL(*send_algorithm_, GetCongestionWindow()) |
| .WillRepeatedly(Return(kDefaultTCPMSS)); |
| EXPECT_CALL(*send_algorithm_, PacingRate(_)) |
| .WillRepeatedly(Return(QuicBandwidth::Zero())); |
| EXPECT_CALL(*send_algorithm_, HasReliableBandwidthEstimate()) |
| .Times(AnyNumber()); |
| EXPECT_CALL(*send_algorithm_, BandwidthEstimate()) |
| .Times(AnyNumber()) |
| .WillRepeatedly(Return(QuicBandwidth::Zero())); |
| EXPECT_CALL(*send_algorithm_, InSlowStart()).Times(AnyNumber()); |
| EXPECT_CALL(*send_algorithm_, InRecovery()).Times(AnyNumber()); |
| EXPECT_CALL(*send_algorithm_, OnApplicationLimited(_)).Times(AnyNumber()); |
| EXPECT_CALL(visitor_, WillingAndAbleToWrite()).Times(AnyNumber()); |
| EXPECT_CALL(visitor_, HasPendingHandshake()).Times(AnyNumber()); |
| if (connection_.session_decides_what_to_write()) { |
| EXPECT_CALL(visitor_, OnCanWrite()) |
| .WillRepeatedly( |
| Invoke(¬ifier_, &SimpleSessionNotifier::OnCanWrite)); |
| } else { |
| EXPECT_CALL(visitor_, OnCanWrite()).Times(AnyNumber()); |
| } |
| EXPECT_CALL(visitor_, ShouldKeepConnectionAlive()) |
| .WillRepeatedly(Return(false)); |
| EXPECT_CALL(visitor_, OnCongestionWindowChange(_)).Times(AnyNumber()); |
| EXPECT_CALL(visitor_, OnConnectivityProbeReceived(_, _)).Times(AnyNumber()); |
| EXPECT_CALL(visitor_, OnForwardProgressConfirmed()).Times(AnyNumber()); |
| |
| EXPECT_CALL(*loss_algorithm_, GetLossTimeout()) |
| .WillRepeatedly(Return(QuicTime::Zero())); |
| EXPECT_CALL(*loss_algorithm_, DetectLosses(_, _, _, _, _, _)) |
| .Times(AnyNumber()); |
| |
| if (connection_.version().KnowsWhichDecrypterToUse()) { |
| connection_.InstallDecrypter( |
| ENCRYPTION_FORWARD_SECURE, |
| QuicMakeUnique<NullDecrypter>(Perspective::IS_CLIENT)); |
| } |
| } |
| |
| QuicConnectionTest(const QuicConnectionTest&) = delete; |
| QuicConnectionTest& operator=(const QuicConnectionTest&) = delete; |
| |
| ParsedQuicVersion version() { return GetParam().version; } |
| |
| QuicAckFrame* outgoing_ack() { |
| QuicFrame ack_frame = QuicConnectionPeer::GetUpdatedAckFrame(&connection_); |
| ack_ = *ack_frame.ack_frame; |
| return &ack_; |
| } |
| |
| QuicStopWaitingFrame* stop_waiting() { |
| QuicConnectionPeer::PopulateStopWaitingFrame(&connection_, &stop_waiting_); |
| return &stop_waiting_; |
| } |
| |
| QuicPacketNumber least_unacked() { |
| if (writer_->stop_waiting_frames().empty()) { |
| return QuicPacketNumber(); |
| } |
| return writer_->stop_waiting_frames()[0].least_unacked; |
| } |
| |
| void use_tagging_decrypter() { writer_->use_tagging_decrypter(); } |
| |
| void SetDecrypter(EncryptionLevel level, |
| std::unique_ptr<QuicDecrypter> decrypter) { |
| if (connection_.version().KnowsWhichDecrypterToUse()) { |
| connection_.InstallDecrypter(level, std::move(decrypter)); |
| connection_.RemoveDecrypter(ENCRYPTION_INITIAL); |
| } else { |
| connection_.SetDecrypter(level, std::move(decrypter)); |
| } |
| } |
| |
| void ProcessPacket(uint64_t number) { |
| EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); |
| ProcessDataPacket(number); |
| if (connection_.GetSendAlarm()->IsSet()) { |
| connection_.GetSendAlarm()->Fire(); |
| } |
| } |
| |
| void ProcessReceivedPacket(const QuicSocketAddress& self_address, |
| const QuicSocketAddress& peer_address, |
| const QuicReceivedPacket& packet) { |
| connection_.ProcessUdpPacket(self_address, peer_address, packet); |
| if (connection_.GetSendAlarm()->IsSet()) { |
| connection_.GetSendAlarm()->Fire(); |
| } |
| } |
| |
| void ProcessFramePacket(QuicFrame frame) { |
| ProcessFramePacketWithAddresses(frame, kSelfAddress, kPeerAddress); |
| } |
| |
| void ProcessFramePacketWithAddresses(QuicFrame frame, |
| QuicSocketAddress self_address, |
| QuicSocketAddress peer_address) { |
| QuicFrames frames; |
| frames.push_back(QuicFrame(frame)); |
| QuicPacketCreatorPeer::SetSendVersionInPacket( |
| &peer_creator_, connection_.perspective() == Perspective::IS_SERVER); |
| |
| char buffer[kMaxOutgoingPacketSize]; |
| SerializedPacket serialized_packet = |
| QuicPacketCreatorPeer::SerializeAllFrames( |
| &peer_creator_, frames, buffer, kMaxOutgoingPacketSize); |
| connection_.ProcessUdpPacket( |
| self_address, peer_address, |
| QuicReceivedPacket(serialized_packet.encrypted_buffer, |
| serialized_packet.encrypted_length, clock_.Now())); |
| if (connection_.GetSendAlarm()->IsSet()) { |
| connection_.GetSendAlarm()->Fire(); |
| } |
| } |
| |
| // Bypassing the packet creator is unrealistic, but allows us to process |
| // packets the QuicPacketCreator won't allow us to create. |
| void ForceProcessFramePacket(QuicFrame frame) { |
| QuicFrames frames; |
| frames.push_back(QuicFrame(frame)); |
| bool send_version = connection_.perspective() == Perspective::IS_SERVER; |
| if (connection_.version().KnowsWhichDecrypterToUse()) { |
| send_version = true; |
| } |
| QuicPacketCreatorPeer::SetSendVersionInPacket(&peer_creator_, send_version); |
| QuicPacketHeader header; |
| QuicPacketCreatorPeer::FillPacketHeader(&peer_creator_, &header); |
| char encrypted_buffer[kMaxOutgoingPacketSize]; |
| size_t length = peer_framer_.BuildDataPacket( |
| header, frames, encrypted_buffer, kMaxOutgoingPacketSize, |
| ENCRYPTION_INITIAL); |
| DCHECK_GT(length, 0u); |
| |
| const size_t encrypted_length = peer_framer_.EncryptInPlace( |
| ENCRYPTION_INITIAL, header.packet_number, |
| GetStartOfEncryptedData(peer_framer_.version().transport_version, |
| header), |
| length, kMaxOutgoingPacketSize, encrypted_buffer); |
| DCHECK_GT(encrypted_length, 0u); |
| |
| connection_.ProcessUdpPacket( |
| kSelfAddress, kPeerAddress, |
| QuicReceivedPacket(encrypted_buffer, encrypted_length, clock_.Now())); |
| } |
| |
| size_t ProcessFramePacketAtLevel(uint64_t number, |
| QuicFrame frame, |
| EncryptionLevel level) { |
| QuicPacketHeader header; |
| header.destination_connection_id = connection_id_; |
| header.packet_number_length = packet_number_length_; |
| header.destination_connection_id_included = connection_id_included_; |
| if ((peer_framer_.transport_version() > QUIC_VERSION_43 || |
| GetQuicRestartFlag(quic_do_not_override_connection_id)) && |
| peer_framer_.perspective() == Perspective::IS_SERVER) { |
| header.destination_connection_id_included = CONNECTION_ID_ABSENT; |
| } |
| if (level == ENCRYPTION_INITIAL && |
| peer_framer_.version().KnowsWhichDecrypterToUse()) { |
| header.version_flag = true; |
| header.retry_token_length_length = VARIABLE_LENGTH_INTEGER_LENGTH_1; |
| header.length_length = VARIABLE_LENGTH_INTEGER_LENGTH_2; |
| } |
| if ((GetQuicRestartFlag(quic_do_not_override_connection_id) || |
| (level == ENCRYPTION_INITIAL && |
| peer_framer_.version().KnowsWhichDecrypterToUse())) && |
| header.version_flag && |
| peer_framer_.perspective() == Perspective::IS_SERVER) { |
| header.source_connection_id = connection_id_; |
| header.source_connection_id_included = CONNECTION_ID_PRESENT; |
| } |
| header.packet_number = QuicPacketNumber(number); |
| QuicFrames frames; |
| frames.push_back(frame); |
| std::unique_ptr<QuicPacket> packet(ConstructPacket(header, frames)); |
| // Set the correct encryption level and encrypter on peer_creator and |
| // peer_framer, respectively. |
| peer_creator_.set_encryption_level(level); |
| if (QuicPacketCreatorPeer::GetEncryptionLevel(&peer_creator_) > |
| ENCRYPTION_INITIAL) { |
| peer_framer_.SetEncrypter( |
| QuicPacketCreatorPeer::GetEncryptionLevel(&peer_creator_), |
| QuicMakeUnique<TaggingEncrypter>(0x01)); |
| // Set the corresponding decrypter. |
| if (connection_.version().KnowsWhichDecrypterToUse()) { |
| connection_.InstallDecrypter( |
| QuicPacketCreatorPeer::GetEncryptionLevel(&peer_creator_), |
| QuicMakeUnique<StrictTaggingDecrypter>(0x01)); |
| connection_.RemoveDecrypter(ENCRYPTION_INITIAL); |
| } else { |
| connection_.SetDecrypter( |
| QuicPacketCreatorPeer::GetEncryptionLevel(&peer_creator_), |
| QuicMakeUnique<StrictTaggingDecrypter>(0x01)); |
| } |
| } |
| |
| char buffer[kMaxOutgoingPacketSize]; |
| size_t encrypted_length = |
| peer_framer_.EncryptPayload(level, QuicPacketNumber(number), *packet, |
| buffer, kMaxOutgoingPacketSize); |
| connection_.ProcessUdpPacket( |
| kSelfAddress, kPeerAddress, |
| QuicReceivedPacket(buffer, encrypted_length, clock_.Now(), false)); |
| if (connection_.GetSendAlarm()->IsSet()) { |
| connection_.GetSendAlarm()->Fire(); |
| } |
| return encrypted_length; |
| } |
| |
| size_t ProcessDataPacket(uint64_t number) { |
| return ProcessDataPacketAtLevel(number, false, ENCRYPTION_FORWARD_SECURE); |
| } |
| |
| size_t ProcessDataPacket(QuicPacketNumber packet_number) { |
| return ProcessDataPacketAtLevel(packet_number, false, |
| ENCRYPTION_FORWARD_SECURE); |
| } |
| |
| size_t ProcessDataPacketAtLevel(QuicPacketNumber packet_number, |
| bool has_stop_waiting, |
| EncryptionLevel level) { |
| return ProcessDataPacketAtLevel(packet_number.ToUint64(), has_stop_waiting, |
| level); |
| } |
| |
| size_t ProcessCryptoPacketAtLevel(uint64_t number, EncryptionLevel level) { |
| QuicPacketHeader header = ConstructPacketHeader(1000, ENCRYPTION_INITIAL); |
| QuicFrames frames; |
| if (QuicVersionUsesCryptoFrames(connection_.transport_version())) { |
| frames.push_back(QuicFrame(&crypto_frame_)); |
| } else { |
| frames.push_back(QuicFrame(frame1_)); |
| } |
| std::unique_ptr<QuicPacket> packet = ConstructPacket(header, frames); |
| char buffer[kMaxOutgoingPacketSize]; |
| peer_creator_.set_encryption_level(ENCRYPTION_INITIAL); |
| size_t encrypted_length = |
| peer_framer_.EncryptPayload(ENCRYPTION_INITIAL, QuicPacketNumber(1000), |
| *packet, buffer, kMaxOutgoingPacketSize); |
| connection_.ProcessUdpPacket( |
| kSelfAddress, kPeerAddress, |
| QuicReceivedPacket(buffer, encrypted_length, clock_.Now(), false)); |
| if (connection_.GetSendAlarm()->IsSet()) { |
| connection_.GetSendAlarm()->Fire(); |
| } |
| return encrypted_length; |
| } |
| |
| size_t ProcessDataPacketAtLevel(uint64_t number, |
| bool has_stop_waiting, |
| EncryptionLevel level) { |
| std::unique_ptr<QuicPacket> packet( |
| ConstructDataPacket(number, has_stop_waiting, level)); |
| char buffer[kMaxOutgoingPacketSize]; |
| peer_creator_.set_encryption_level(level); |
| size_t encrypted_length = |
| peer_framer_.EncryptPayload(level, QuicPacketNumber(number), *packet, |
| buffer, kMaxOutgoingPacketSize); |
| connection_.ProcessUdpPacket( |
| kSelfAddress, kPeerAddress, |
| QuicReceivedPacket(buffer, encrypted_length, clock_.Now(), false)); |
| if (connection_.GetSendAlarm()->IsSet()) { |
| connection_.GetSendAlarm()->Fire(); |
| } |
| return encrypted_length; |
| } |
| |
| void ProcessClosePacket(uint64_t number) { |
| std::unique_ptr<QuicPacket> packet(ConstructClosePacket(number)); |
| char buffer[kMaxOutgoingPacketSize]; |
| size_t encrypted_length = peer_framer_.EncryptPayload( |
| ENCRYPTION_INITIAL, QuicPacketNumber(number), *packet, buffer, |
| kMaxOutgoingPacketSize); |
| connection_.ProcessUdpPacket( |
| kSelfAddress, kPeerAddress, |
| QuicReceivedPacket(buffer, encrypted_length, QuicTime::Zero(), false)); |
| } |
| |
| QuicByteCount SendStreamDataToPeer(QuicStreamId id, |
| QuicStringPiece data, |
| QuicStreamOffset offset, |
| StreamSendingState state, |
| QuicPacketNumber* last_packet) { |
| QuicByteCount packet_size; |
| EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)) |
| .WillOnce(SaveArg<3>(&packet_size)); |
| connection_.SendStreamDataWithString(id, data, offset, state); |
| if (last_packet != nullptr) { |
| *last_packet = creator_->packet_number(); |
| } |
| EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)) |
| .Times(AnyNumber()); |
| return packet_size; |
| } |
| |
| void SendAckPacketToPeer() { |
| EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); |
| { |
| QuicConnection::ScopedPacketFlusher flusher(&connection_, |
| QuicConnection::NO_ACK); |
| connection_.SendAck(); |
| } |
| EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)) |
| .Times(AnyNumber()); |
| } |
| |
| void SendRstStream(QuicStreamId id, |
| QuicRstStreamErrorCode error, |
| QuicStreamOffset bytes_written) { |
| if (connection_.session_decides_what_to_write()) { |
| notifier_.WriteOrBufferRstStream(id, error, bytes_written); |
| connection_.OnStreamReset(id, error); |
| return; |
| } |
| std::unique_ptr<QuicRstStreamFrame> rst_stream = |
| QuicMakeUnique<QuicRstStreamFrame>(1, id, error, bytes_written); |
| if (connection_.SendControlFrame(QuicFrame(rst_stream.get()))) { |
| rst_stream.release(); |
| } |
| connection_.OnStreamReset(id, error); |
| } |
| |
| void SendPing() { |
| if (connection_.session_decides_what_to_write()) { |
| notifier_.WriteOrBufferPing(); |
| } else { |
| connection_.SendControlFrame(QuicFrame(QuicPingFrame(1))); |
| } |
| } |
| |
| void ProcessAckPacket(uint64_t packet_number, QuicAckFrame* frame) { |
| if (packet_number > 1) { |
| QuicPacketCreatorPeer::SetPacketNumber(&peer_creator_, packet_number - 1); |
| } else { |
| QuicPacketCreatorPeer::ClearPacketNumber(&peer_creator_); |
| } |
| ProcessFramePacket(QuicFrame(frame)); |
| } |
| |
| void ProcessAckPacket(QuicAckFrame* frame) { |
| ProcessFramePacket(QuicFrame(frame)); |
| } |
| |
| void ProcessStopWaitingPacket(QuicStopWaitingFrame frame) { |
| ProcessFramePacket(QuicFrame(frame)); |
| } |
| |
| size_t ProcessStopWaitingPacketAtLevel(uint64_t number, |
| QuicStopWaitingFrame frame, |
| EncryptionLevel level) { |
| return ProcessFramePacketAtLevel(number, QuicFrame(frame), |
| ENCRYPTION_ZERO_RTT); |
| } |
| |
| void ProcessGoAwayPacket(QuicGoAwayFrame* frame) { |
| ProcessFramePacket(QuicFrame(frame)); |
| } |
| |
| bool IsMissing(uint64_t number) { |
| return IsAwaitingPacket(*outgoing_ack(), QuicPacketNumber(number), |
| QuicPacketNumber()); |
| } |
| |
| std::unique_ptr<QuicPacket> ConstructPacket(const QuicPacketHeader& header, |
| const QuicFrames& frames) { |
| auto packet = BuildUnsizedDataPacket(&peer_framer_, header, frames); |
| EXPECT_NE(nullptr, packet.get()); |
| return packet; |
| } |
| |
| QuicPacketHeader ConstructPacketHeader(uint64_t number, |
| EncryptionLevel level) { |
| QuicPacketHeader header; |
| if (peer_framer_.transport_version() > QUIC_VERSION_43 && |
| level < ENCRYPTION_FORWARD_SECURE) { |
| // Set long header type accordingly. |
| header.version_flag = true; |
| header.long_packet_type = EncryptionlevelToLongHeaderType(level); |
| if (QuicVersionHasLongHeaderLengths( |
| peer_framer_.version().transport_version)) { |
| header.length_length = VARIABLE_LENGTH_INTEGER_LENGTH_2; |
| if (header.long_packet_type == INITIAL) { |
| header.retry_token_length_length = VARIABLE_LENGTH_INTEGER_LENGTH_1; |
| } |
| } |
| } |
| // Set connection_id to peer's in memory representation as this data packet |
| // is created by peer_framer. |
| if (GetQuicRestartFlag(quic_do_not_override_connection_id) && |
| peer_framer_.perspective() == Perspective::IS_SERVER) { |
| header.source_connection_id = connection_id_; |
| header.source_connection_id_included = connection_id_included_; |
| header.destination_connection_id_included = CONNECTION_ID_ABSENT; |
| } else { |
| header.destination_connection_id = connection_id_; |
| header.destination_connection_id_included = connection_id_included_; |
| } |
| if (peer_framer_.transport_version() > QUIC_VERSION_43 && |
| peer_framer_.perspective() == Perspective::IS_SERVER) { |
| header.destination_connection_id_included = CONNECTION_ID_ABSENT; |
| if (header.version_flag) { |
| header.source_connection_id = connection_id_; |
| header.source_connection_id_included = CONNECTION_ID_PRESENT; |
| if (GetParam().version.handshake_protocol == PROTOCOL_QUIC_CRYPTO && |
| header.long_packet_type == ZERO_RTT_PROTECTED) { |
| header.nonce = &kTestDiversificationNonce; |
| } |
| } |
| } |
| header.packet_number_length = packet_number_length_; |
| header.packet_number = QuicPacketNumber(number); |
| return header; |
| } |
| |
| std::unique_ptr<QuicPacket> ConstructDataPacket(uint64_t number, |
| bool has_stop_waiting, |
| EncryptionLevel level) { |
| QuicPacketHeader header = ConstructPacketHeader(number, level); |
| QuicFrames frames; |
| frames.push_back(QuicFrame(frame1_)); |
| if (has_stop_waiting) { |
| frames.push_back(QuicFrame(stop_waiting_)); |
| } |
| return ConstructPacket(header, frames); |
| } |
| |
| OwningSerializedPacketPointer ConstructProbingPacket() { |
| if (version().transport_version == QUIC_VERSION_99) { |
| QuicPathFrameBuffer payload = { |
| {0xde, 0xad, 0xbe, 0xef, 0xba, 0xdc, 0x0f, 0xfe}}; |
| return QuicPacketCreatorPeer:: |
| SerializePathChallengeConnectivityProbingPacket(&peer_creator_, |
| &payload); |
| } |
| return QuicPacketCreatorPeer::SerializeConnectivityProbingPacket( |
| &peer_creator_); |
| } |
| |
| std::unique_ptr<QuicPacket> ConstructClosePacket(uint64_t number) { |
| QuicPacketHeader header; |
| // Set connection_id to peer's in memory representation as this connection |
| // close packet is created by peer_framer. |
| if (GetQuicRestartFlag(quic_do_not_override_connection_id) && |
| peer_framer_.perspective() == Perspective::IS_SERVER) { |
| header.source_connection_id = connection_id_; |
| header.destination_connection_id_included = CONNECTION_ID_ABSENT; |
| if (peer_framer_.transport_version() <= QUIC_VERSION_43) { |
| header.source_connection_id_included = CONNECTION_ID_PRESENT; |
| } |
| } else { |
| header.destination_connection_id = connection_id_; |
| if (peer_framer_.transport_version() > QUIC_VERSION_43) { |
| header.destination_connection_id_included = CONNECTION_ID_ABSENT; |
| } |
| } |
| |
| header.packet_number = QuicPacketNumber(number); |
| |
| QuicConnectionCloseFrame qccf(QUIC_PEER_GOING_AWAY); |
| if (peer_framer_.transport_version() == QUIC_VERSION_99) { |
| // Default close-type is Google QUIC. If doing IETF/V99 then |
| // set close type to be IETF CC/T. |
| qccf.close_type = IETF_QUIC_TRANSPORT_CONNECTION_CLOSE; |
| } |
| |
| QuicFrames frames; |
| frames.push_back(QuicFrame(&qccf)); |
| return ConstructPacket(header, frames); |
| } |
| |
| QuicTime::Delta DefaultRetransmissionTime() { |
| return QuicTime::Delta::FromMilliseconds(kDefaultRetransmissionTimeMs); |
| } |
| |
| QuicTime::Delta DefaultDelayedAckTime() { |
| return QuicTime::Delta::FromMilliseconds(kDefaultDelayedAckTimeMs); |
| } |
| |
| const QuicStopWaitingFrame InitStopWaitingFrame(uint64_t least_unacked) { |
| QuicStopWaitingFrame frame; |
| frame.least_unacked = QuicPacketNumber(least_unacked); |
| return frame; |
| } |
| |
| // Construct a ack_frame that acks all packet numbers between 1 and |
| // |largest_acked|, except |missing|. |
| // REQUIRES: 1 <= |missing| < |largest_acked| |
| QuicAckFrame ConstructAckFrame(uint64_t largest_acked, uint64_t missing) { |
| return ConstructAckFrame(QuicPacketNumber(largest_acked), |
| QuicPacketNumber(missing)); |
| } |
| |
| QuicAckFrame ConstructAckFrame(QuicPacketNumber largest_acked, |
| QuicPacketNumber missing) { |
| if (missing == QuicPacketNumber(1)) { |
| return InitAckFrame({{missing + 1, largest_acked + 1}}); |
| } |
| return InitAckFrame( |
| {{QuicPacketNumber(1), missing}, {missing + 1, largest_acked + 1}}); |
| } |
| |
| // Undo nacking a packet within the frame. |
| void AckPacket(QuicPacketNumber arrived, QuicAckFrame* frame) { |
| EXPECT_FALSE(frame->packets.Contains(arrived)); |
| frame->packets.Add(arrived); |
| } |
| |
| void TriggerConnectionClose() { |
| // Send an erroneous packet to close the connection. |
| EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_INVALID_ACK_DATA, _, |
| ConnectionCloseSource::FROM_SELF)); |
| EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); |
| // Triggers a connection by receiving ACK of unsent packet. |
| QuicAckFrame frame = InitAckFrame(10000); |
| ProcessAckPacket(1, &frame); |
| |
| EXPECT_FALSE(QuicConnectionPeer::GetConnectionClosePacket(&connection_) == |
| nullptr); |
| } |
| |
| void BlockOnNextWrite() { |
| writer_->BlockOnNextWrite(); |
| EXPECT_CALL(visitor_, OnWriteBlocked()).Times(AtLeast(1)); |
| } |
| |
| void SimulateNextPacketTooLarge() { writer_->SimulateNextPacketTooLarge(); } |
| |
| void AlwaysGetPacketTooLarge() { writer_->AlwaysGetPacketTooLarge(); } |
| |
| void SetWritePauseTimeDelta(QuicTime::Delta delta) { |
| writer_->SetWritePauseTimeDelta(delta); |
| } |
| |
| void CongestionBlockWrites() { |
| EXPECT_CALL(*send_algorithm_, CanSend(_)) |
| .WillRepeatedly(testing::Return(false)); |
| } |
| |
| void CongestionUnblockWrites() { |
| EXPECT_CALL(*send_algorithm_, CanSend(_)) |
| .WillRepeatedly(testing::Return(true)); |
| } |
| |
| void set_perspective(Perspective perspective) { |
| connection_.set_perspective(perspective); |
| if (perspective == Perspective::IS_SERVER) { |
| connection_.set_can_truncate_connection_ids(true); |
| } |
| QuicFramerPeer::SetPerspective(&peer_framer_, |
| InvertPerspective(perspective)); |
| } |
| |
| void set_packets_between_probes_base( |
| const QuicPacketCount packets_between_probes_base) { |
| QuicConnectionPeer::SetPacketsBetweenMtuProbes(&connection_, |
| packets_between_probes_base); |
| QuicConnectionPeer::SetNextMtuProbeAt( |
| &connection_, QuicPacketNumber(packets_between_probes_base)); |
| } |
| |
| bool IsDefaultTestConfiguration() { |
| TestParams p = GetParam(); |
| return p.ack_response == AckResponse::kImmediate && |
| p.version == AllSupportedVersions()[0] && p.no_stop_waiting; |
| } |
| |
| QuicConnectionId connection_id_; |
| QuicFramer framer_; |
| |
| MockSendAlgorithm* send_algorithm_; |
| std::unique_ptr<MockLossAlgorithm> loss_algorithm_; |
| MockClock clock_; |
| MockRandom random_generator_; |
| SimpleBufferAllocator buffer_allocator_; |
| std::unique_ptr<TestConnectionHelper> helper_; |
| std::unique_ptr<TestAlarmFactory> alarm_factory_; |
| QuicFramer peer_framer_; |
| QuicPacketCreator peer_creator_; |
| std::unique_ptr<TestPacketWriter> writer_; |
| TestConnection connection_; |
| QuicPacketCreator* creator_; |
| QuicPacketGenerator* generator_; |
| QuicSentPacketManager* manager_; |
| StrictMock<MockQuicConnectionVisitor> visitor_; |
| |
| QuicStreamFrame frame1_; |
| QuicStreamFrame frame2_; |
| QuicCryptoFrame crypto_frame_; |
| QuicAckFrame ack_; |
| QuicStopWaitingFrame stop_waiting_; |
| QuicPacketNumberLength packet_number_length_; |
| QuicConnectionIdIncluded connection_id_included_; |
| |
| SimpleSessionNotifier notifier_; |
| }; |
| |
| // Run all end to end tests with all supported versions. |
| INSTANTIATE_TEST_SUITE_P(SupportedVersion, |
| QuicConnectionTest, |
| ::testing::ValuesIn(GetTestParams())); |
| |
| TEST_P(QuicConnectionTest, SelfAddressChangeAtClient) { |
| EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); |
| |
| EXPECT_EQ(Perspective::IS_CLIENT, connection_.perspective()); |
| EXPECT_TRUE(connection_.connected()); |
| |
| QuicFrame frame; |
| if (QuicVersionUsesCryptoFrames(connection_.transport_version())) { |
| frame = QuicFrame(&crypto_frame_); |
| EXPECT_CALL(visitor_, OnCryptoFrame(_)); |
| } else { |
| frame = QuicFrame(QuicStreamFrame( |
| QuicUtils::GetCryptoStreamId(connection_.transport_version()), false, |
| 0u, QuicStringPiece())); |
| EXPECT_CALL(visitor_, OnStreamFrame(_)); |
| } |
| ProcessFramePacketWithAddresses(frame, kSelfAddress, kPeerAddress); |
| // Cause change in self_address. |
| QuicIpAddress host; |
| host.FromString("1.1.1.1"); |
| QuicSocketAddress self_address(host, 123); |
| if (QuicVersionUsesCryptoFrames(connection_.transport_version())) { |
| EXPECT_CALL(visitor_, OnCryptoFrame(_)); |
| } else { |
| EXPECT_CALL(visitor_, OnStreamFrame(_)); |
| } |
| ProcessFramePacketWithAddresses(frame, self_address, kPeerAddress); |
| EXPECT_TRUE(connection_.connected()); |
| } |
| |
| TEST_P(QuicConnectionTest, SelfAddressChangeAtServer) { |
| EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); |
| |
| set_perspective(Perspective::IS_SERVER); |
| QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false); |
| |
| EXPECT_EQ(Perspective::IS_SERVER, connection_.perspective()); |
| EXPECT_TRUE(connection_.connected()); |
| |
| QuicFrame frame; |
| if (QuicVersionUsesCryptoFrames(connection_.transport_version())) { |
| frame = QuicFrame(&crypto_frame_); |
| EXPECT_CALL(visitor_, OnCryptoFrame(_)); |
| } else { |
| frame = QuicFrame(QuicStreamFrame( |
| QuicUtils::GetCryptoStreamId(connection_.transport_version()), false, |
| 0u, QuicStringPiece())); |
| EXPECT_CALL(visitor_, OnStreamFrame(_)); |
| } |
| ProcessFramePacketWithAddresses(frame, kSelfAddress, kPeerAddress); |
| // Cause change in self_address. |
| QuicIpAddress host; |
| host.FromString("1.1.1.1"); |
| QuicSocketAddress self_address(host, 123); |
| EXPECT_CALL(visitor_, AllowSelfAddressChange()).WillOnce(Return(false)); |
| EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_ERROR_MIGRATING_ADDRESS, _, _)); |
| ProcessFramePacketWithAddresses(frame, self_address, kPeerAddress); |
| EXPECT_FALSE(connection_.connected()); |
| } |
| |
| TEST_P(QuicConnectionTest, AllowSelfAddressChangeToMappedIpv4AddressAtServer) { |
| EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); |
| |
| set_perspective(Perspective::IS_SERVER); |
| QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false); |
| |
| EXPECT_EQ(Perspective::IS_SERVER, connection_.perspective()); |
| EXPECT_TRUE(connection_.connected()); |
| |
| QuicFrame frame; |
| if (QuicVersionUsesCryptoFrames(connection_.transport_version())) { |
| frame = QuicFrame(&crypto_frame_); |
| EXPECT_CALL(visitor_, OnCryptoFrame(_)).Times(3); |
| } else { |
| frame = QuicFrame(QuicStreamFrame( |
| QuicUtils::GetCryptoStreamId(connection_.transport_version()), false, |
| 0u, QuicStringPiece())); |
| EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(3); |
| } |
| QuicIpAddress host; |
| host.FromString("1.1.1.1"); |
| QuicSocketAddress self_address1(host, 443); |
| ProcessFramePacketWithAddresses(frame, self_address1, kPeerAddress); |
| // Cause self_address change to mapped Ipv4 address. |
| QuicIpAddress host2; |
| host2.FromString( |
| QuicStrCat("::ffff:", connection_.self_address().host().ToString())); |
| QuicSocketAddress self_address2(host2, connection_.self_address().port()); |
| ProcessFramePacketWithAddresses(frame, self_address2, kPeerAddress); |
| EXPECT_TRUE(connection_.connected()); |
| // self_address change back to Ipv4 address. |
| ProcessFramePacketWithAddresses(frame, self_address1, kPeerAddress); |
| EXPECT_TRUE(connection_.connected()); |
| } |
| |
| TEST_P(QuicConnectionTest, ClientAddressChangeAndPacketReordered) { |
| EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); |
| set_perspective(Perspective::IS_SERVER); |
| QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false); |
| |
| // Clear direct_peer_address. |
| QuicConnectionPeer::SetDirectPeerAddress(&connection_, QuicSocketAddress()); |
| // Clear effective_peer_address, it is the same as direct_peer_address for |
| // this test. |
| QuicConnectionPeer::SetEffectivePeerAddress(&connection_, |
| QuicSocketAddress()); |
| |
| QuicFrame frame; |
| if (QuicVersionUsesCryptoFrames(connection_.transport_version())) { |
| frame = QuicFrame(&crypto_frame_); |
| EXPECT_CALL(visitor_, OnCryptoFrame(_)).Times(AnyNumber()); |
| } else { |
| frame = QuicFrame(QuicStreamFrame( |
| QuicUtils::GetCryptoStreamId(connection_.transport_version()), false, |
| 0u, QuicStringPiece())); |
| EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(AnyNumber()); |
| } |
| QuicPacketCreatorPeer::SetPacketNumber(&peer_creator_, 5); |
| const QuicSocketAddress kNewPeerAddress = |
| QuicSocketAddress(QuicIpAddress::Loopback6(), |
| /*port=*/23456); |
| ProcessFramePacketWithAddresses(frame, kSelfAddress, kNewPeerAddress); |
| EXPECT_EQ(kNewPeerAddress, connection_.peer_address()); |
| EXPECT_EQ(kNewPeerAddress, connection_.effective_peer_address()); |
| |
| // Decrease packet number to simulate out-of-order packets. |
| QuicPacketCreatorPeer::SetPacketNumber(&peer_creator_, 4); |
| // This is an old packet, do not migrate. |
| EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(0); |
| ProcessFramePacketWithAddresses(frame, kSelfAddress, kPeerAddress); |
| EXPECT_EQ(kNewPeerAddress, connection_.peer_address()); |
| EXPECT_EQ(kNewPeerAddress, connection_.effective_peer_address()); |
| } |
| |
| TEST_P(QuicConnectionTest, PeerAddressChangeAtServer) { |
| EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); |
| set_perspective(Perspective::IS_SERVER); |
| QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false); |
| EXPECT_EQ(Perspective::IS_SERVER, connection_.perspective()); |
| |
| // Clear direct_peer_address. |
| QuicConnectionPeer::SetDirectPeerAddress(&connection_, QuicSocketAddress()); |
| // Clear effective_peer_address, it is the same as direct_peer_address for |
| // this test. |
| QuicConnectionPeer::SetEffectivePeerAddress(&connection_, |
| QuicSocketAddress()); |
| EXPECT_FALSE(connection_.effective_peer_address().IsInitialized()); |
| |
| QuicFrame frame; |
| if (QuicVersionUsesCryptoFrames(connection_.transport_version())) { |
| frame = QuicFrame(&crypto_frame_); |
| EXPECT_CALL(visitor_, OnCryptoFrame(_)).Times(AnyNumber()); |
| } else { |
| frame = QuicFrame(QuicStreamFrame( |
| QuicUtils::GetCryptoStreamId(connection_.transport_version()), false, |
| 0u, QuicStringPiece())); |
| EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(AnyNumber()); |
| } |
| ProcessFramePacketWithAddresses(frame, kSelfAddress, kPeerAddress); |
| EXPECT_EQ(kPeerAddress, connection_.peer_address()); |
| EXPECT_EQ(kPeerAddress, connection_.effective_peer_address()); |
| |
| // Process another packet with a different peer address on server side will |
| // start connection migration. |
| const QuicSocketAddress kNewPeerAddress = |
| QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/23456); |
| EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(1); |
| ProcessFramePacketWithAddresses(frame, kSelfAddress, kNewPeerAddress); |
| EXPECT_EQ(kNewPeerAddress, connection_.peer_address()); |
| EXPECT_EQ(kNewPeerAddress, connection_.effective_peer_address()); |
| } |
| |
| TEST_P(QuicConnectionTest, EffectivePeerAddressChangeAtServer) { |
| EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); |
| set_perspective(Perspective::IS_SERVER); |
| QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false); |
| EXPECT_EQ(Perspective::IS_SERVER, connection_.perspective()); |
| |
| // Clear direct_peer_address. |
| QuicConnectionPeer::SetDirectPeerAddress(&connection_, QuicSocketAddress()); |
| // Clear effective_peer_address, it is different from direct_peer_address for |
| // this test. |
| QuicConnectionPeer::SetEffectivePeerAddress(&connection_, |
| QuicSocketAddress()); |
| const QuicSocketAddress kEffectivePeerAddress = |
| QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/43210); |
| connection_.ReturnEffectivePeerAddressForNextPacket(kEffectivePeerAddress); |
| |
| QuicFrame frame; |
| if (QuicVersionUsesCryptoFrames(connection_.transport_version())) { |
| frame = QuicFrame(&crypto_frame_); |
| EXPECT_CALL(visitor_, OnCryptoFrame(_)).Times(AnyNumber()); |
| } else { |
| frame = QuicFrame(QuicStreamFrame( |
| QuicUtils::GetCryptoStreamId(connection_.transport_version()), false, |
| 0u, QuicStringPiece())); |
| EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(AnyNumber()); |
| } |
| ProcessFramePacketWithAddresses(frame, kSelfAddress, kPeerAddress); |
| EXPECT_EQ(kPeerAddress, connection_.peer_address()); |
| EXPECT_EQ(kEffectivePeerAddress, connection_.effective_peer_address()); |
| |
| // Process another packet with the same direct peer address and different |
| // effective peer address on server side will start connection migration. |
| const QuicSocketAddress kNewEffectivePeerAddress = |
| QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/54321); |
| connection_.ReturnEffectivePeerAddressForNextPacket(kNewEffectivePeerAddress); |
| EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(1); |
| ProcessFramePacketWithAddresses(frame, kSelfAddress, kPeerAddress); |
| EXPECT_EQ(kPeerAddress, connection_.peer_address()); |
| EXPECT_EQ(kNewEffectivePeerAddress, connection_.effective_peer_address()); |
| |
| // Process another packet with a different direct peer address and the same |
| // effective peer address on server side will not start connection migration. |
| const QuicSocketAddress kNewPeerAddress = |
| QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/23456); |
| connection_.ReturnEffectivePeerAddressForNextPacket(kNewEffectivePeerAddress); |
| EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(0); |
| // ack_frame is used to complete the migration started by the last packet, we |
| // need to make sure a new migration does not start after the previous one is |
| // completed. |
| QuicAckFrame ack_frame = InitAckFrame(1); |
| EXPECT_CALL(*send_algorithm_, OnCongestionEvent(_, _, _, _, _)); |
| ProcessFramePacketWithAddresses(QuicFrame(&ack_frame), kSelfAddress, |
| kNewPeerAddress); |
| EXPECT_EQ(kNewPeerAddress, connection_.peer_address()); |
| EXPECT_EQ(kNewEffectivePeerAddress, connection_.effective_peer_address()); |
| |
| // Process another packet with different direct peer address and different |
| // effective peer address on server side will start connection migration. |
| const QuicSocketAddress kNewerEffectivePeerAddress = |
| QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/65432); |
| const QuicSocketAddress kFinalPeerAddress = |
| QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/34567); |
| connection_.ReturnEffectivePeerAddressForNextPacket( |
| kNewerEffectivePeerAddress); |
| EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(1); |
| ProcessFramePacketWithAddresses(frame, kSelfAddress, kFinalPeerAddress); |
| EXPECT_EQ(kFinalPeerAddress, connection_.peer_address()); |
| EXPECT_EQ(kNewerEffectivePeerAddress, connection_.effective_peer_address()); |
| EXPECT_EQ(PORT_CHANGE, connection_.active_effective_peer_migration_type()); |
| |
| // While the previous migration is ongoing, process another packet with the |
| // same direct peer address and different effective peer address on server |
| // side will start a new connection migration. |
| const QuicSocketAddress kNewestEffectivePeerAddress = |
| QuicSocketAddress(QuicIpAddress::Loopback4(), /*port=*/65430); |
| connection_.ReturnEffectivePeerAddressForNextPacket( |
| kNewestEffectivePeerAddress); |
| EXPECT_CALL(visitor_, OnConnectionMigration(IPV6_TO_IPV4_CHANGE)).Times(1); |
| EXPECT_CALL(*send_algorithm_, OnConnectionMigration()).Times(1); |
| ProcessFramePacketWithAddresses(frame, kSelfAddress, kFinalPeerAddress); |
| EXPECT_EQ(kFinalPeerAddress, connection_.peer_address()); |
| EXPECT_EQ(kNewestEffectivePeerAddress, connection_.effective_peer_address()); |
| EXPECT_EQ(IPV6_TO_IPV4_CHANGE, |
| connection_.active_effective_peer_migration_type()); |
| } |
| |
| TEST_P(QuicConnectionTest, ReceivePaddedPingAtServer) { |
| EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); |
| set_perspective(Perspective::IS_SERVER); |
| QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false); |
| EXPECT_EQ(Perspective::IS_SERVER, connection_.perspective()); |
| |
| // Clear direct_peer_address. |
| QuicConnectionPeer::SetDirectPeerAddress(&connection_, QuicSocketAddress()); |
| // Clear effective_peer_address, it is the same as direct_peer_address for |
| // this test. |
| QuicConnectionPeer::SetEffectivePeerAddress(&connection_, |
| QuicSocketAddress()); |
| EXPECT_FALSE(connection_.effective_peer_address().IsInitialized()); |
| |
| QuicFrame frame; |
| if (QuicVersionUsesCryptoFrames(connection_.transport_version())) { |
| frame = QuicFrame(&crypto_frame_); |
| EXPECT_CALL(visitor_, OnCryptoFrame(_)).Times(AnyNumber()); |
| } else { |
| frame = QuicFrame(QuicStreamFrame( |
| QuicUtils::GetCryptoStreamId(connection_.transport_version()), false, |
| 0u, QuicStringPiece())); |
| EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(AnyNumber()); |
| } |
| ProcessFramePacketWithAddresses(frame, kSelfAddress, kPeerAddress); |
| EXPECT_EQ(kPeerAddress, connection_.peer_address()); |
| EXPECT_EQ(kPeerAddress, connection_.effective_peer_address()); |
| |
| EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(0); |
| EXPECT_CALL(visitor_, OnConnectivityProbeReceived(_, _)).Times(0); |
| |
| // Process a padded PING or PATH CHALLENGE packet with no peer address change |
| // on server side will be ignored. |
| OwningSerializedPacketPointer probing_packet; |
| if (version().transport_version == QUIC_VERSION_99) { |
| QuicPathFrameBuffer payload = { |
| {0xde, 0xad, 0xbe, 0xef, 0xba, 0xdc, 0x0f, 0xfe}}; |
| probing_packet = |
| QuicPacketCreatorPeer::SerializePathChallengeConnectivityProbingPacket( |
| &peer_creator_, &payload); |
| } else { |
| probing_packet = QuicPacketCreatorPeer::SerializeConnectivityProbingPacket( |
| &peer_creator_); |
| } |
| std::unique_ptr<QuicReceivedPacket> received(ConstructReceivedPacket( |
| QuicEncryptedPacket(probing_packet->encrypted_buffer, |
| probing_packet->encrypted_length), |
| clock_.Now())); |
| |
| uint64_t num_probing_received = |
| connection_.GetStats().num_connectivity_probing_received; |
| ProcessReceivedPacket(kSelfAddress, kPeerAddress, *received); |
| |
| EXPECT_EQ(num_probing_received, |
| connection_.GetStats().num_connectivity_probing_received); |
| EXPECT_EQ(kPeerAddress, connection_.peer_address()); |
| EXPECT_EQ(kPeerAddress, connection_.effective_peer_address()); |
| } |
| |
| TEST_P(QuicConnectionTest, WriteOutOfOrderQueuedPackets) { |
| // EXPECT_QUIC_BUG tests are expensive so only run one instance of them. |
| if (!IsDefaultTestConfiguration() || |
| connection_.SupportsMultiplePacketNumberSpaces()) { |
| return; |
| } |
| |
| set_perspective(Perspective::IS_CLIENT); |
| |
| BlockOnNextWrite(); |
| |
| QuicStreamId stream_id = 2; |
| connection_.SendStreamDataWithString(stream_id, "foo", 0, NO_FIN); |
| |
| EXPECT_EQ(1u, connection_.NumQueuedPackets()); |
| |
| writer_->SetWritable(); |
| connection_.SendConnectivityProbingPacket(writer_.get(), |
| connection_.peer_address()); |
| |
| EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_INTERNAL_ERROR, |
| "Packet written out of order.", |
| ConnectionCloseSource::FROM_SELF)); |
| EXPECT_QUIC_BUG(connection_.OnCanWrite(), |
| "Attempt to write packet:1 after:2"); |
| EXPECT_FALSE(connection_.connected()); |
| } |
| |
| TEST_P(QuicConnectionTest, DiscardQueuedPacketsAfterConnectionClose) { |
| // Regression test for b/74073386. |
| { |
| InSequence seq; |
| EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); |
| EXPECT_CALL(visitor_, OnConnectionClosed(_, _, _)).Times(1); |
| } |
| |
| set_perspective(Perspective::IS_CLIENT); |
| |
| writer_->SimulateNextPacketTooLarge(); |
| |
| // This packet write should fail, which should cause the connection to close |
| // after sending a connection close packet, then the failed packet should be |
| // queued. |
| connection_.SendStreamDataWithString(/*id=*/2, "foo", 0, NO_FIN); |
| |
| EXPECT_FALSE(connection_.connected()); |
| EXPECT_EQ(1u, connection_.NumQueuedPackets()); |
| |
| EXPECT_EQ(0u, connection_.GetStats().packets_discarded); |
| connection_.OnCanWrite(); |
| EXPECT_EQ(1u, connection_.GetStats().packets_discarded); |
| } |
| |
| TEST_P(QuicConnectionTest, ReceiveConnectivityProbingAtServer) { |
| EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); |
| set_perspective(Perspective::IS_SERVER); |
| QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false); |
| EXPECT_EQ(Perspective::IS_SERVER, connection_.perspective()); |
| |
| // Clear direct_peer_address. |
| QuicConnectionPeer::SetDirectPeerAddress(&connection_, QuicSocketAddress()); |
| // Clear effective_peer_address, it is the same as direct_peer_address for |
| // this test. |
| QuicConnectionPeer::SetEffectivePeerAddress(&connection_, |
| QuicSocketAddress()); |
| EXPECT_FALSE(connection_.effective_peer_address().IsInitialized()); |
| |
| QuicFrame frame; |
| if (QuicVersionUsesCryptoFrames(connection_.transport_version())) { |
| frame = QuicFrame(&crypto_frame_); |
| EXPECT_CALL(visitor_, OnCryptoFrame(_)).Times(AnyNumber()); |
| } else { |
| frame = QuicFrame(QuicStreamFrame( |
| QuicUtils::GetCryptoStreamId(connection_.transport_version()), false, |
| 0u, QuicStringPiece())); |
| EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(AnyNumber()); |
| } |
| ProcessFramePacketWithAddresses(frame, kSelfAddress, kPeerAddress); |
| EXPECT_EQ(kPeerAddress, connection_.peer_address()); |
| EXPECT_EQ(kPeerAddress, connection_.effective_peer_address()); |
| |
| EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(0); |
| EXPECT_CALL(visitor_, OnConnectivityProbeReceived(_, _)).Times(1); |
| |
| // Process a padded PING packet from a new peer address on server side |
| // is effectively receiving a connectivity probing. |
| const QuicSocketAddress kNewPeerAddress = |
| QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/23456); |
| |
| OwningSerializedPacketPointer probing_packet = ConstructProbingPacket(); |
| std::unique_ptr<QuicReceivedPacket> received(ConstructReceivedPacket( |
| QuicEncryptedPacket(probing_packet->encrypted_buffer, |
| probing_packet->encrypted_length), |
| clock_.Now())); |
| |
| uint64_t num_probing_received = |
| connection_.GetStats().num_connectivity_probing_received; |
| ProcessReceivedPacket(kSelfAddress, kNewPeerAddress, *received); |
| |
| EXPECT_EQ(num_probing_received + 1, |
| connection_.GetStats().num_connectivity_probing_received); |
| EXPECT_EQ(kPeerAddress, connection_.peer_address()); |
| EXPECT_EQ(kPeerAddress, connection_.effective_peer_address()); |
| |
| // Process another packet with the old peer address on server side will not |
| // start peer migration. |
| EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(0); |
| ProcessFramePacketWithAddresses(frame, kSelfAddress, kPeerAddress); |
| EXPECT_EQ(kPeerAddress, connection_.peer_address()); |
| EXPECT_EQ(kPeerAddress, connection_.effective_peer_address()); |
| } |
| |
| TEST_P(QuicConnectionTest, ReceiveReorderedConnectivityProbingAtServer) { |
| EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); |
| set_perspective(Perspective::IS_SERVER); |
| QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false); |
| EXPECT_EQ(Perspective::IS_SERVER, connection_.perspective()); |
| |
| // Clear direct_peer_address. |
| QuicConnectionPeer::SetDirectPeerAddress(&connection_, QuicSocketAddress()); |
| // Clear effective_peer_address, it is the same as direct_peer_address for |
| // this test. |
| QuicConnectionPeer::SetEffectivePeerAddress(&connection_, |
| QuicSocketAddress()); |
| EXPECT_FALSE(connection_.effective_peer_address().IsInitialized()); |
| |
| QuicFrame frame; |
| if (QuicVersionUsesCryptoFrames(connection_.transport_version())) { |
| frame = QuicFrame(&crypto_frame_); |
| EXPECT_CALL(visitor_, OnCryptoFrame(_)).Times(AnyNumber()); |
| } else { |
| frame = QuicFrame(QuicStreamFrame( |
| QuicUtils::GetCryptoStreamId(connection_.transport_version()), false, |
| 0u, QuicStringPiece())); |
| EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(AnyNumber()); |
| } |
| QuicPacketCreatorPeer::SetPacketNumber(&peer_creator_, 5); |
| ProcessFramePacketWithAddresses(frame, kSelfAddress, kPeerAddress); |
| EXPECT_EQ(kPeerAddress, connection_.peer_address()); |
| EXPECT_EQ(kPeerAddress, connection_.effective_peer_address()); |
| |
| // Decrease packet number to simulate out-of-order packets. |
| QuicPacketCreatorPeer::SetPacketNumber(&peer_creator_, 4); |
| |
| EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(0); |
| EXPECT_CALL(visitor_, OnConnectivityProbeReceived(_, _)).Times(1); |
| |
| // Process a padded PING packet from a new peer address on server side |
| // is effectively receiving a connectivity probing, even if a newer packet has |
| // been received before this one. |
| const QuicSocketAddress kNewPeerAddress = |
| QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/23456); |
| |
| OwningSerializedPacketPointer probing_packet = ConstructProbingPacket(); |
| std::unique_ptr<QuicReceivedPacket> received(ConstructReceivedPacket( |
| QuicEncryptedPacket(probing_packet->encrypted_buffer, |
| probing_packet->encrypted_length), |
| clock_.Now())); |
| |
| uint64_t num_probing_received = |
| connection_.GetStats().num_connectivity_probing_received; |
| ProcessReceivedPacket(kSelfAddress, kNewPeerAddress, *received); |
| |
| EXPECT_EQ(num_probing_received + 1, |
| connection_.GetStats().num_connectivity_probing_received); |
| EXPECT_EQ(kPeerAddress, connection_.peer_address()); |
| EXPECT_EQ(kPeerAddress, connection_.effective_peer_address()); |
| } |
| |
| TEST_P(QuicConnectionTest, MigrateAfterProbingAtServer) { |
| EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); |
| set_perspective(Perspective::IS_SERVER); |
| QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false); |
| EXPECT_EQ(Perspective::IS_SERVER, connection_.perspective()); |
| |
| // Clear direct_peer_address. |
| QuicConnectionPeer::SetDirectPeerAddress(&connection_, QuicSocketAddress()); |
| // Clear effective_peer_address, it is the same as direct_peer_address for |
| // this test. |
| QuicConnectionPeer::SetEffectivePeerAddress(&connection_, |
| QuicSocketAddress()); |
| EXPECT_FALSE(connection_.effective_peer_address().IsInitialized()); |
| |
| QuicFrame frame; |
| if (QuicVersionUsesCryptoFrames(connection_.transport_version())) { |
| frame = QuicFrame(&crypto_frame_); |
| EXPECT_CALL(visitor_, OnCryptoFrame(_)).Times(AnyNumber()); |
| } else { |
| frame = QuicFrame(QuicStreamFrame( |
| QuicUtils::GetCryptoStreamId(connection_.transport_version()), false, |
| 0u, QuicStringPiece())); |
| EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(AnyNumber()); |
| } |
| ProcessFramePacketWithAddresses(frame, kSelfAddress, kPeerAddress); |
| EXPECT_EQ(kPeerAddress, connection_.peer_address()); |
| EXPECT_EQ(kPeerAddress, connection_.effective_peer_address()); |
| |
| EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(0); |
| EXPECT_CALL(visitor_, OnConnectivityProbeReceived(_, _)).Times(1); |
| |
| // Process a padded PING packet from a new peer address on server side |
| // is effectively receiving a connectivity probing. |
| const QuicSocketAddress kNewPeerAddress = |
| QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/23456); |
| |
| OwningSerializedPacketPointer probing_packet = ConstructProbingPacket(); |
| std::unique_ptr<QuicReceivedPacket> received(ConstructReceivedPacket( |
| QuicEncryptedPacket(probing_packet->encrypted_buffer, |
| probing_packet->encrypted_length), |
| clock_.Now())); |
| ProcessReceivedPacket(kSelfAddress, kNewPeerAddress, *received); |
| EXPECT_EQ(kPeerAddress, connection_.peer_address()); |
| EXPECT_EQ(kPeerAddress, connection_.effective_peer_address()); |
| |
| // Process another non-probing packet with the new peer address on server |
| // side will start peer migration. |
| EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(1); |
| |
| ProcessFramePacketWithAddresses(frame, kSelfAddress, kNewPeerAddress); |
| EXPECT_EQ(kNewPeerAddress, connection_.peer_address()); |
| EXPECT_EQ(kNewPeerAddress, connection_.effective_peer_address()); |
| } |
| |
| TEST_P(QuicConnectionTest, ReceivePaddedPingAtClient) { |
| EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); |
| set_perspective(Perspective::IS_CLIENT); |
| EXPECT_EQ(Perspective::IS_CLIENT, connection_.perspective()); |
| |
| // Clear direct_peer_address. |
| QuicConnectionPeer::SetDirectPeerAddress(&connection_, QuicSocketAddress()); |
| // Clear effective_peer_address, it is the same as direct_peer_address for |
| // this test. |
| QuicConnectionPeer::SetEffectivePeerAddress(&connection_, |
| QuicSocketAddress()); |
| EXPECT_FALSE(connection_.effective_peer_address().IsInitialized()); |
| |
| QuicFrame frame; |
| if (QuicVersionUsesCryptoFrames(connection_.transport_version())) { |
| frame = QuicFrame(&crypto_frame_); |
| EXPECT_CALL(visitor_, OnCryptoFrame(_)).Times(AnyNumber()); |
| } else { |
| frame = QuicFrame(QuicStreamFrame( |
| QuicUtils::GetCryptoStreamId(connection_.transport_version()), false, |
| 0u, QuicStringPiece())); |
| EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(AnyNumber()); |
| } |
| ProcessFramePacketWithAddresses(frame, kSelfAddress, kPeerAddress); |
| EXPECT_EQ(kPeerAddress, connection_.peer_address()); |
| EXPECT_EQ(kPeerAddress, connection_.effective_peer_address()); |
| |
| // Client takes all padded PING packet as speculative connectivity |
| // probing packet, and reports to visitor. |
| EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(0); |
| EXPECT_CALL(visitor_, OnConnectivityProbeReceived(_, _)).Times(1); |
| |
| OwningSerializedPacketPointer probing_packet = ConstructProbingPacket(); |
| std::unique_ptr<QuicReceivedPacket> received(ConstructReceivedPacket( |
| QuicEncryptedPacket(probing_packet->encrypted_buffer, |
| probing_packet->encrypted_length), |
| clock_.Now())); |
| uint64_t num_probing_received = |
| connection_.GetStats().num_connectivity_probing_received; |
| ProcessReceivedPacket(kSelfAddress, kPeerAddress, *received); |
| |
| EXPECT_EQ(num_probing_received, |
| connection_.GetStats().num_connectivity_probing_received); |
| EXPECT_EQ(kPeerAddress, connection_.peer_address()); |
| EXPECT_EQ(kPeerAddress, connection_.effective_peer_address()); |
| } |
| |
| TEST_P(QuicConnectionTest, ReceiveConnectivityProbingAtClient) { |
| EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); |
| set_perspective(Perspective::IS_CLIENT); |
| EXPECT_EQ(Perspective::IS_CLIENT, connection_.perspective()); |
| |
| // Clear direct_peer_address. |
| QuicConnectionPeer::SetDirectPeerAddress(&connection_, QuicSocketAddress()); |
| // Clear effective_peer_address, it is the same as direct_peer_address for |
| // this test. |
| QuicConnectionPeer::SetEffectivePeerAddress(&connection_, |
| QuicSocketAddress()); |
| EXPECT_FALSE(connection_.effective_peer_address().IsInitialized()); |
| |
| QuicFrame frame; |
| if (QuicVersionUsesCryptoFrames(connection_.transport_version())) { |
| frame = QuicFrame(&crypto_frame_); |
| EXPECT_CALL(visitor_, OnCryptoFrame(_)).Times(AnyNumber()); |
| } else { |
| frame = QuicFrame(QuicStreamFrame( |
| QuicUtils::GetCryptoStreamId(connection_.transport_version()), false, |
| 0u, QuicStringPiece())); |
| EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(AnyNumber()); |
| } |
| ProcessFramePacketWithAddresses(frame, kSelfAddress, kPeerAddress); |
| EXPECT_EQ(kPeerAddress, connection_.peer_address()); |
| EXPECT_EQ(kPeerAddress, connection_.effective_peer_address()); |
| |
| // Process a padded PING packet with a different self address on client side |
| // is effectively receiving a connectivity probing. |
| EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(0); |
| EXPECT_CALL(visitor_, OnConnectivityProbeReceived(_, _)).Times(1); |
| |
| const QuicSocketAddress kNewSelfAddress = |
| QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/23456); |
| |
| OwningSerializedPacketPointer probing_packet = ConstructProbingPacket(); |
| std::unique_ptr<QuicReceivedPacket> received(ConstructReceivedPacket( |
| QuicEncryptedPacket(probing_packet->encrypted_buffer, |
| probing_packet->encrypted_length), |
| clock_.Now())); |
| uint64_t num_probing_received = |
| connection_.GetStats().num_connectivity_probing_received; |
| ProcessReceivedPacket(kNewSelfAddress, kPeerAddress, *received); |
| |
| EXPECT_EQ(num_probing_received + 1, |
| connection_.GetStats().num_connectivity_probing_received); |
| EXPECT_EQ(kPeerAddress, connection_.peer_address()); |
| EXPECT_EQ(kPeerAddress, connection_.effective_peer_address()); |
| } |
| |
| TEST_P(QuicConnectionTest, PeerAddressChangeAtClient) { |
| EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); |
| set_perspective(Perspective::IS_CLIENT); |
| EXPECT_EQ(Perspective::IS_CLIENT, connection_.perspective()); |
| |
| // Clear direct_peer_address. |
| QuicConnectionPeer::SetDirectPeerAddress(&connection_, QuicSocketAddress()); |
| // Clear effective_peer_address, it is the same as direct_peer_address for |
| // this test. |
| QuicConnectionPeer::SetEffectivePeerAddress(&connection_, |
| QuicSocketAddress()); |
| EXPECT_FALSE(connection_.effective_peer_address().IsInitialized()); |
| |
| QuicFrame frame; |
| if (QuicVersionUsesCryptoFrames(connection_.transport_version())) { |
| frame = QuicFrame(&crypto_frame_); |
| EXPECT_CALL(visitor_, OnCryptoFrame(_)).Times(AnyNumber()); |
| } else { |
| frame = QuicFrame(QuicStreamFrame( |
| QuicUtils::GetCryptoStreamId(connection_.transport_version()), false, |
| 0u, QuicStringPiece())); |
| EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(AnyNumber()); |
| } |
| ProcessFramePacketWithAddresses(frame, kSelfAddress, kPeerAddress); |
| EXPECT_EQ(kPeerAddress, connection_.peer_address()); |
| EXPECT_EQ(kPeerAddress, connection_.effective_peer_address()); |
| |
| // Process another packet with a different peer address on client side will |
| // only update peer address. |
| const QuicSocketAddress kNewPeerAddress = |
| QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/23456); |
| EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(0); |
| ProcessFramePacketWithAddresses(frame, kSelfAddress, kNewPeerAddress); |
| EXPECT_EQ(kNewPeerAddress, connection_.peer_address()); |
| EXPECT_EQ(kNewPeerAddress, connection_.effective_peer_address()); |
| } |
| |
| TEST_P(QuicConnectionTest, MaxPacketSize) { |
| EXPECT_EQ(Perspective::IS_CLIENT, connection_.perspective()); |
| EXPECT_EQ(1350u, connection_.max_packet_length()); |
| } |
| |
| TEST_P(QuicConnectionTest, SmallerServerMaxPacketSize) { |
| TestConnection connection(TestConnectionId(), kPeerAddress, helper_.get(), |
| alarm_factory_.get(), writer_.get(), |
| Perspective::IS_SERVER, version()); |
| EXPECT_EQ(Perspective::IS_SERVER, connection.perspective()); |
| EXPECT_EQ(1000u, connection.max_packet_length()); |
| } |
| |
| TEST_P(QuicConnectionTest, IncreaseServerMaxPacketSize) { |
| EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); |
| |
| set_perspective(Perspective::IS_SERVER); |
| connection_.SetMaxPacketLength(1000); |
| |
| QuicPacketHeader header; |
| header.destination_connection_id = connection_id_; |
| header.version_flag = true; |
| header.packet_number = QuicPacketNumber(1); |
| |
| if (QuicVersionHasLongHeaderLengths( |
| peer_framer_.version().transport_version)) { |
| header.long_packet_type = INITIAL; |
| header.retry_token_length_length = VARIABLE_LENGTH_INTEGER_LENGTH_1; |
| header.length_length = VARIABLE_LENGTH_INTEGER_LENGTH_2; |
| } |
| |
| QuicFrames frames; |
| QuicPaddingFrame padding; |
| if (QuicVersionUsesCryptoFrames(connection_.transport_version())) { |
| frames.push_back(QuicFrame(&crypto_frame_)); |
| } else { |
| frames.push_back(QuicFrame(frame1_)); |
| } |
| frames.push_back(QuicFrame(padding)); |
| std::unique_ptr<QuicPacket> packet(ConstructPacket(header, frames)); |
| char buffer[kMaxOutgoingPacketSize]; |
| size_t encrypted_length = |
| peer_framer_.EncryptPayload(ENCRYPTION_INITIAL, QuicPacketNumber(12), |
| *packet, buffer, kMaxOutgoingPacketSize); |
| EXPECT_EQ(kMaxOutgoingPacketSize, encrypted_length); |
| |
| framer_.set_version(version()); |
| if (QuicVersionUsesCryptoFrames(connection_.transport_version())) { |
| EXPECT_CALL(visitor_, OnCryptoFrame(_)).Times(1); |
| } else { |
| EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); |
| } |
| connection_.ProcessUdpPacket( |
| kSelfAddress, kPeerAddress, |
| QuicReceivedPacket(buffer, encrypted_length, QuicTime::Zero(), false)); |
| |
| EXPECT_EQ(kMaxOutgoingPacketSize, connection_.max_packet_length()); |
| } |
| |
| TEST_P(QuicConnectionTest, IncreaseServerMaxPacketSizeWhileWriterLimited) { |
| EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); |
| |
| const QuicByteCount lower_max_packet_size = 1240; |
| writer_->set_max_packet_size(lower_max_packet_size); |
| set_perspective(Perspective::IS_SERVER); |
| connection_.SetMaxPacketLength(1000); |
| EXPECT_EQ(1000u, connection_.max_packet_length()); |
| |
| QuicPacketHeader header; |
| header.destination_connection_id = connection_id_; |
| header.version_flag = true; |
| header.packet_number = QuicPacketNumber(1); |
| |
| if (QuicVersionHasLongHeaderLengths( |
| peer_framer_.version().transport_version)) { |
| header.long_packet_type = INITIAL; |
| header.retry_token_length_length = VARIABLE_LENGTH_INTEGER_LENGTH_1; |
| header.length_length = VARIABLE_LENGTH_INTEGER_LENGTH_2; |
| } |
| |
| QuicFrames frames; |
| QuicPaddingFrame padding; |
| if (QuicVersionUsesCryptoFrames(connection_.transport_version())) { |
| frames.push_back(QuicFrame(&crypto_frame_)); |
| } else { |
| frames.push_back(QuicFrame(frame1_)); |
| } |
| frames.push_back(QuicFrame(padding)); |
| std::unique_ptr<QuicPacket> packet(ConstructPacket(header, frames)); |
| char buffer[kMaxOutgoingPacketSize]; |
| size_t encrypted_length = |
| peer_framer_.EncryptPayload(ENCRYPTION_INITIAL, QuicPacketNumber(12), |
| *packet, buffer, kMaxOutgoingPacketSize); |
| EXPECT_EQ(kMaxOutgoingPacketSize, encrypted_length); |
| |
| framer_.set_version(version()); |
| if (QuicVersionUsesCryptoFrames(connection_.transport_version())) { |
| EXPECT_CALL(visitor_, OnCryptoFrame(_)).Times(1); |
| } else { |
| EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); |
| } |
| connection_.ProcessUdpPacket( |
| kSelfAddress, kPeerAddress, |
| QuicReceivedPacket(buffer, encrypted_length, QuicTime::Zero(), false)); |
| |
| // Here, the limit imposed by the writer is lower than the size of the packet |
| // received, so the writer max packet size is used. |
| EXPECT_EQ(lower_max_packet_size, connection_.max_packet_length()); |
| } |
| |
| TEST_P(QuicConnectionTest, LimitMaxPacketSizeByWriter) { |
| const QuicByteCount lower_max_packet_size = 1240; |
| writer_->set_max_packet_size(lower_max_packet_size); |
| |
| static_assert(lower_max_packet_size < kDefaultMaxPacketSize, |
| "Default maximum packet size is too low"); |
| connection_.SetMaxPacketLength(kDefaultMaxPacketSize); |
| |
| EXPECT_EQ(lower_max_packet_size, connection_.max_packet_length()); |
| } |
| |
| TEST_P(QuicConnectionTest, LimitMaxPacketSizeByWriterForNewConnection) { |
| const QuicConnectionId connection_id = TestConnectionId(17); |
| const QuicByteCount lower_max_packet_size = 1240; |
| writer_->set_max_packet_size(lower_max_packet_size); |
| TestConnection connection(connection_id, kPeerAddress, helper_.get(), |
| alarm_factory_.get(), writer_.get(), |
| Perspective::IS_CLIENT, version()); |
| EXPECT_EQ(Perspective::IS_CLIENT, connection.perspective()); |
| EXPECT_EQ(lower_max_packet_size, connection.max_packet_length()); |
| } |
| |
| TEST_P(QuicConnectionTest, PacketsInOrder) { |
| if (connection_.SupportsMultiplePacketNumberSpaces()) { |
| return; |
| } |
| EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); |
| |
| ProcessPacket(1); |
| EXPECT_EQ(QuicPacketNumber(1u), LargestAcked(*outgoing_ack())); |
| EXPECT_EQ(1u, outgoing_ack()->packets.NumIntervals()); |
| |
| ProcessPacket(2); |
| EXPECT_EQ(QuicPacketNumber(2u), LargestAcked(*outgoing_ack())); |
| EXPECT_EQ(1u, outgoing_ack()->packets.NumIntervals()); |
| |
| ProcessPacket(3); |
| EXPECT_EQ(QuicPacketNumber(3u), LargestAcked(*outgoing_ack())); |
| EXPECT_EQ(1u, outgoing_ack()->packets.NumIntervals()); |
| } |
| |
| TEST_P(QuicConnectionTest, PacketsOutOfOrder) { |
| if (connection_.SupportsMultiplePacketNumberSpaces()) { |
| return; |
| } |
| EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); |
| |
| ProcessPacket(3); |
| EXPECT_EQ(QuicPacketNumber(3u), LargestAcked(*outgoing_ack())); |
| EXPECT_TRUE(IsMissing(2)); |
| EXPECT_TRUE(IsMissing(1)); |
| |
| ProcessPacket(2); |
| EXPECT_EQ(QuicPacketNumber(3u), LargestAcked(*outgoing_ack())); |
| EXPECT_FALSE(IsMissing(2)); |
| EXPECT_TRUE(IsMissing(1)); |
| |
| ProcessPacket(1); |
| EXPECT_EQ(QuicPacketNumber(3u), LargestAcked(*outgoing_ack())); |
| EXPECT_FALSE(IsMissing(2)); |
| EXPECT_FALSE(IsMissing(1)); |
| } |
| |
| TEST_P(QuicConnectionTest, DuplicatePacket) { |
| if (connection_.SupportsMultiplePacketNumberSpaces()) { |
| return; |
| } |
| EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); |
| |
| ProcessPacket(3); |
| EXPECT_EQ(QuicPacketNumber(3u), LargestAcked(*outgoing_ack())); |
| EXPECT_TRUE(IsMissing(2)); |
| EXPECT_TRUE(IsMissing(1)); |
| |
| // Send packet 3 again, but do not set the expectation that |
| // the visitor OnStreamFrame() will be called. |
| ProcessDataPacket(3); |
| EXPECT_EQ(QuicPacketNumber(3u), LargestAcked(*outgoing_ack())); |
| EXPECT_TRUE(IsMissing(2)); |
| EXPECT_TRUE(IsMissing(1)); |
| } |
| |
| TEST_P(QuicConnectionTest, PacketsOutOfOrderWithAdditionsAndLeastAwaiting) { |
| if (connection_.SupportsMultiplePacketNumberSpaces()) { |
| return; |
| } |
| EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); |
| |
| ProcessPacket(3); |
| EXPECT_EQ(QuicPacketNumber(3u), LargestAcked(*outgoing_ack())); |
| EXPECT_TRUE(IsMissing(2)); |
| EXPECT_TRUE(IsMissing(1)); |
| |
| ProcessPacket(2); |
| EXPECT_EQ(QuicPacketNumber(3u), LargestAcked(*outgoing_ack())); |
| EXPECT_TRUE(IsMissing(1)); |
| |
| ProcessPacket(5); |
| EXPECT_EQ(QuicPacketNumber(5u), LargestAcked(*outgoing_ack())); |
| 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. |
| QuicAckFrame frame = InitAckFrame(1); |
| EXPECT_CALL(*send_algorithm_, OnCongestionEvent(_, _, _, _, _)); |
| ProcessAckPacket(6, &frame); |
| |
| // Force an ack to be sent. |
| SendAckPacketToPeer(); |
| EXPECT_TRUE(IsMissing(4)); |
| } |
| |
| TEST_P(QuicConnectionTest, RejectPacketTooFarOut) { |
| if (GetQuicReloadableFlag(quic_use_uber_received_packet_manager)) { |
| return; |
| } |
| EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_INVALID_PACKET_HEADER, _, |
| ConnectionCloseSource::FROM_SELF)); |
| |
| // Call ProcessDataPacket rather than ProcessPacket, as we should not get a |
| // packet call to the visitor. |
| if (GetQuicRestartFlag(quic_enable_accept_random_ipn)) { |
| ProcessDataPacket(MaxRandomInitialPacketNumber() + 6000); |
| } else { |
| ProcessDataPacket(6000); |
| } |
| EXPECT_FALSE(QuicConnectionPeer::GetConnectionClosePacket(&connection_) == |
| nullptr); |
| } |
| |
| TEST_P(QuicConnectionTest, RejectUnencryptedStreamData) { |
| // EXPECT_QUIC_BUG tests are expensive so only run one instance of them. |
| if (!IsDefaultTestConfiguration()) { |
| return; |
| } |
| |
| // Process an unencrypted packet from the non-crypto stream. |
| frame1_.stream_id = 3; |
| EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); |
| EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_UNENCRYPTED_STREAM_DATA, _, |
| ConnectionCloseSource::FROM_SELF)); |
| EXPECT_QUIC_PEER_BUG(ProcessDataPacketAtLevel(1, false, ENCRYPTION_INITIAL), |
| ""); |
| EXPECT_FALSE(QuicConnectionPeer::GetConnectionClosePacket(&connection_) == |
| nullptr); |
| const std::vector<QuicConnectionCloseFrame>& connection_close_frames = |
| writer_->connection_close_frames(); |
| ASSERT_EQ(1u, connection_close_frames.size()); |
| EXPECT_EQ(QUIC_UNENCRYPTED_STREAM_DATA, |
| connection_close_frames[0].quic_error_code); |
| } |
| |
| TEST_P(QuicConnectionTest, OutOfOrderReceiptCausesAckSend) { |
| EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); |
| |
| ProcessPacket(3); |
| if (GetQuicRestartFlag(quic_enable_accept_random_ipn)) { |
| // Should not cause an ack. |
| EXPECT_EQ(0u, writer_->packets_write_attempts()); |
| } else { |
| // Should ack immediately since we have missing packets. |
| EXPECT_EQ(1u, writer_->packets_write_attempts()); |
| } |
| |
| ProcessPacket(2); |
| if (GetQuicRestartFlag(quic_enable_accept_random_ipn)) { |
| // Should ack immediately, since this fills the last hole. |
| EXPECT_EQ(1u, writer_->packets_write_attempts()); |
| } else { |
| // Should ack immediately since we have missing packets. |
| EXPECT_EQ(2u, writer_->packets_write_attempts()); |
| } |
| |
| ProcessPacket(1); |
| // Should ack immediately, since this fills the last hole. |
| if (GetQuicRestartFlag(quic_enable_accept_random_ipn)) { |
| EXPECT_EQ(2u, writer_->packets_write_attempts()); |
| } else { |
| EXPECT_EQ(3u, writer_->packets_write_attempts()); |
| } |
| |
| ProcessPacket(4); |
| // Should not cause an ack. |
| if (GetQuicRestartFlag(quic_enable_accept_random_ipn)) { |
| EXPECT_EQ(2u, writer_->packets_write_attempts()); |
| } else { |
| EXPECT_EQ(3u, writer_->packets_write_attempts()); |
| } |
| } |
| |
| TEST_P(QuicConnectionTest, OutOfOrderAckReceiptCausesNoAck) { |
| EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); |
| |
| SendStreamDataToPeer(1, "foo", 0, NO_FIN, nullptr); |
| SendStreamDataToPeer(1, "bar", 3, NO_FIN, nullptr); |
| EXPECT_EQ(2u, writer_->packets_write_attempts()); |
| |
| QuicAckFrame ack1 = InitAckFrame(1); |
| QuicAckFrame ack2 = InitAckFrame(2); |
| EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); |
| ProcessAckPacket(2, &ack2); |
| // Should ack immediately since we have missing packets. |
| EXPECT_EQ(2u, writer_->packets_write_attempts()); |
| |
| ProcessAckPacket(1, &ack1); |
| // Should not ack an ack filling a missing packet. |
| EXPECT_EQ(2u, writer_->packets_write_attempts()); |
| } |
| |
| TEST_P(QuicConnectionTest, AckReceiptCausesAckSend) { |
| EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); |
| QuicPacketNumber original, second; |
| |
| QuicByteCount packet_size = |
| SendStreamDataToPeer(3, "foo", 0, NO_FIN, &original); // 1st packet. |
| SendStreamDataToPeer(3, "bar", 3, NO_FIN, &second); // 2nd packet. |
| |
| QuicAckFrame frame = InitAckFrame({{second, second + 1}}); |
| // First nack triggers early retransmit. |
| LostPacketVector lost_packets; |
| lost_packets.push_back(LostPacket(original, kMaxOutgoingPacketSize)); |
| EXPECT_CALL(*loss_algorithm_, DetectLosses(_, _, _, _, _, _)) |
| .WillOnce(SetArgPointee<5>(lost_packets)); |
| EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); |
| QuicPacketNumber retransmission; |
| // Packet 1 is short header for IETF QUIC because the encryption level |
| // switched to ENCRYPTION_FORWARD_SECURE in SendStreamDataToPeer. |
| EXPECT_CALL( |
| *send_algorithm_, |
| OnPacketSent(_, _, _, |
| GetParam().version.transport_version > QUIC_VERSION_43 |
| ? packet_size |
| : packet_size - kQuicVersionSize, |
| _)) |
| .WillOnce(SaveArg<2>(&retransmission)); |
| |
| ProcessAckPacket(&frame); |
| |
| QuicAckFrame frame2 = ConstructAckFrame(retransmission, original); |
| EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); |
| EXPECT_CALL(*loss_algorithm_, DetectLosses(_, _, _, _, _, _)); |
| ProcessAckPacket(&frame2); |
| |
| // Now if the peer sends an ack which still reports the retransmitted packet |
| // as missing, that will bundle an ack with data after two acks in a row |
| // indicate the high water mark needs to be raised. |
| EXPECT_CALL(*send_algorithm_, |
| OnPacketSent(_, _, _, _, HAS_RETRANSMITTABLE_DATA)); |
| connection_.SendStreamDataWithString(3, "foo", 6, NO_FIN); |
| // No ack sent. |
| size_t padding_frame_count = writer_->padding_frames().size(); |
| EXPECT_EQ(padding_frame_count + 1u, writer_->frame_count()); |
| EXPECT_EQ(1u, writer_->stream_frames().size()); |
| |
| // No more packet loss for the rest of the test. |
| EXPECT_CALL(*loss_algorithm_, DetectLosses(_, _, _, _, _, _)) |
| .Times(AnyNumber()); |
| ProcessAckPacket(&frame2); |
| EXPECT_CALL(*send_algorithm_, |
| OnPacketSent(_, _, _, _, HAS_RETRANSMITTABLE_DATA)); |
| connection_.SendStreamDataWithString(3, "foo", 9, NO_FIN); |
| // Ack bundled. |
| if (GetParam().no_stop_waiting) { |
| EXPECT_EQ(2u, writer_->frame_count()); |
| } else { |
| EXPECT_EQ(3u, writer_->frame_count()); |
| } |
| EXPECT_EQ(1u, writer_->stream_frames().size()); |
| EXPECT_FALSE(writer_->ack_frames().empty()); |
| |
| // But an ack with no missing packets will not send an ack. |
| AckPacket(original, &frame2); |
| ProcessAckPacket(&frame2); |
| ProcessAckPacket(&frame2); |
| } |
| |
| TEST_P(QuicConnectionTest, AckSentEveryNthPacket) { |
| connection_.set_ack_frequency_before_ack_decimation(3); |
| |
| EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); |
| EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(39); |
| |
| // Expect 13 acks, every 3rd packet. |
| EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(13); |
| // Receives packets 1 - 39. |
| for (size_t i = 1; i <= 39; ++i) { |
| ProcessDataPacket(i); |
| } |
| } |
| |
| TEST_P(QuicConnectionTest, AckDecimationReducesAcks) { |
| const size_t kMinRttMs = 40; |
| RttStats* rtt_stats = const_cast<RttStats*>(manager_->GetRttStats()); |
| rtt_stats->UpdateRtt(QuicTime::Delta::FromMilliseconds(kMinRttMs), |
| QuicTime::Delta::Zero(), QuicTime::Zero()); |
| EXPECT_CALL(visitor_, OnAckNeedsRetransmittableFrame()).Times(AnyNumber()); |
| |
| QuicConnectionPeer::SetAckMode(&connection_, ACK_DECIMATION_WITH_REORDERING); |
| |
| // Start ack decimation from 10th packet. |
| connection_.set_min_received_before_ack_decimation(10); |
| |
| EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); |
| EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(30); |
| |
| // Expect 6 acks: 5 acks between packets 1-10, and ack at 20. |
| EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(6); |
| // Receives packets 1 - 29. |
| for (size_t i = 1; i <= 29; ++i) { |
| ProcessDataPacket(i); |
| } |
| |
| // We now receive the 30th packet, and so we send an ack. |
| EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); |
| ProcessDataPacket(30); |
| } |
| |
| TEST_P(QuicConnectionTest, AckNeedsRetransmittableFrames) { |
| connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); |
| EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); |
| EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(99); |
| |
| EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(19); |
| // Receives packets 1 - 39. |
| for (size_t i = 1; i <= 39; ++i) { |
| ProcessDataPacket(i); |
| } |
| // Receiving Packet 40 causes 20th ack to send. Session is informed and adds |
| // WINDOW_UPDATE. |
| EXPECT_CALL(visitor_, OnAckNeedsRetransmittableFrame()) |
| .WillOnce(Invoke([this]() { |
| connection_.SendControlFrame( |
| QuicFrame(new QuicWindowUpdateFrame(1, 0, 0))); |
| })); |
| EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); |
| EXPECT_EQ(0u, writer_->window_update_frames().size()); |
| ProcessDataPacket(40); |
| EXPECT_EQ(1u, writer_->window_update_frames().size()); |
| |
| EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(9); |
| // Receives packets 41 - 59. |
| for (size_t i = 41; i <= 59; ++i) { |
| ProcessDataPacket(i); |
| } |
| // Send a packet containing stream frame. |
| SendStreamDataToPeer( |
| QuicUtils::GetFirstBidirectionalStreamId( |
| connection_.version().transport_version, Perspective::IS_CLIENT), |
| "bar", 0, NO_FIN, nullptr); |
| |
| // Session will not be informed until receiving another 20 packets. |
| EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(19); |
| for (size_t i = 60; i <= 98; ++i) { |
| ProcessDataPacket(i); |
| EXPECT_EQ(0u, writer_->window_update_frames().size()); |
| } |
| // Session does not add a retransmittable frame. |
| EXPECT_CALL(visitor_, OnAckNeedsRetransmittableFrame()) |
| .WillOnce(Invoke([this]() { |
| connection_.SendControlFrame(QuicFrame(QuicPingFrame(1))); |
| })); |
| EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); |
| EXPECT_EQ(0u, writer_->ping_frames().size()); |
| ProcessDataPacket(99); |
| EXPECT_EQ(0u, writer_->window_update_frames().size()); |
| // A ping frame will be added. |
| EXPECT_EQ(1u, writer_->ping_frames().size()); |
| } |
| |
| TEST_P(QuicConnectionTest, LeastUnackedLower) { |
| if (GetParam().version.transport_version > QUIC_VERSION_43 || |
| connection_.SupportsMultiplePacketNumberSpaces()) { |
| return; |
| } |
| EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); |
| |
| SendStreamDataToPeer(1, "foo", 0, NO_FIN, nullptr); |
| SendStreamDataToPeer(1, "bar", 3, NO_FIN, nullptr); |
| SendStreamDataToPeer(1, "eep", 6, NO_FIN, nullptr); |
| |
| // Start out saying the least unacked is 2. |
| QuicPacketCreatorPeer::SetPacketNumber(&peer_creator_, 5); |
| ProcessStopWaitingPacket(InitStopWaitingFrame(2)); |
| |
| // Change it to 1, but lower the packet number to fake out-of-order packets. |
| // This should be fine. |
| QuicPacketCreatorPeer::SetPacketNumber(&peer_creator_, 1); |
| // The scheduler will not process out of order acks, but all packet processing |
| // causes the connection to try to write. |
| if (!GetParam().no_stop_waiting) { |
| EXPECT_CALL(visitor_, OnCanWrite()); |
| } |
| ProcessStopWaitingPacket(InitStopWaitingFrame(1)); |
| |
| // Now claim it's one, but set the ordering so it was sent "after" the first |
| // one. This should cause a connection error. |
| QuicPacketCreatorPeer::SetPacketNumber(&peer_creator_, 7); |
| if (!GetParam().no_stop_waiting) { |
| EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)); |
| EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_INVALID_STOP_WAITING_DATA, _, |
| ConnectionCloseSource::FROM_SELF)); |
| } |
| ProcessStopWaitingPacket(InitStopWaitingFrame(1)); |
| } |
| |
| TEST_P(QuicConnectionTest, TooManySentPackets) { |
| EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); |
| |
| QuicPacketCount max_tracked_packets = 50; |
| QuicConnectionPeer::SetMaxTrackedPackets(&connection_, max_tracked_packets); |
| |
| const int num_packets = max_tracked_packets + 5; |
| |
| for (int i = 0; i < num_packets; ++i) { |
| SendStreamDataToPeer(1, "foo", 3 * i, NO_FIN, nullptr); |
| } |
| |
| // Ack packet 1, which leaves more than the limit outstanding. |
| EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); |
| EXPECT_CALL(visitor_, |
| OnConnectionClosed(QUIC_TOO_MANY_OUTSTANDING_SENT_PACKETS, _, |
| ConnectionCloseSource::FROM_SELF)); |
| |
| // Nack the first packet and ack the rest, leaving a huge gap. |
|
|