diff options
author | rch@chromium.org <rch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-10-23 23:29:37 +0000 |
---|---|---|
committer | rch@chromium.org <rch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-10-23 23:29:37 +0000 |
commit | a5061244d407a27683b36e4d1ed35d895722805d (patch) | |
tree | c48806826561e5d26be8e9ac45012984c1a65ecf | |
parent | 46c39747bfa9048442e89f47b578df99001a9575 (diff) | |
download | chromium_src-a5061244d407a27683b36e4d1ed35d895722805d.zip chromium_src-a5061244d407a27683b36e4d1ed35d895722805d.tar.gz chromium_src-a5061244d407a27683b36e4d1ed35d895722805d.tar.bz2 |
Add QuicPacketCreator and QuicFecGroup.
Review URL: https://chromiumcodereview.appspot.com/11238013
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@163724 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | net/net.gyp | 6 | ||||
-rw-r--r-- | net/quic/quic_fec_group.cc | 158 | ||||
-rw-r--r-- | net/quic/quic_fec_group.h | 81 | ||||
-rw-r--r-- | net/quic/quic_fec_group_test.cc | 207 | ||||
-rw-r--r-- | net/quic/quic_framer.cc | 4 | ||||
-rw-r--r-- | net/quic/quic_framer_test.cc | 4 | ||||
-rw-r--r-- | net/quic/quic_packet_creator.cc | 185 | ||||
-rw-r--r-- | net/quic/quic_packet_creator.h | 93 | ||||
-rw-r--r-- | net/quic/quic_packet_creator_test.cc | 258 | ||||
-rw-r--r-- | net/quic/quic_protocol.cc | 4 | ||||
-rw-r--r-- | net/quic/quic_protocol.h | 16 | ||||
-rw-r--r-- | net/quic/quic_utils.cc | 2 | ||||
-rw-r--r-- | net/quic/quic_utils.h | 2 |
13 files changed, 1005 insertions, 15 deletions
diff --git a/net/net.gyp b/net/net.gyp index f23b5c2..28605c8 100644 --- a/net/net.gyp +++ b/net/net.gyp @@ -644,8 +644,12 @@ 'quic/quic_data_reader.h', 'quic/quic_data_writer.cc', 'quic/quic_data_writer.h', + 'quic/quic_fec_group.cc', + 'quic/quic_fec_group.h', 'quic/quic_framer.cc', 'quic/quic_framer.h', + 'quic/quic_packet_creator.cc', + 'quic/quic_packet_creator.h', 'quic/quic_protocol.cc', 'quic/quic_protocol.h', 'quic/quic_utils.cc', @@ -1378,7 +1382,9 @@ 'quic/crypto/null_encrypter_test.cc', 'quic/test_tools/quic_test_utils.cc', 'quic/test_tools/quic_test_utils.h', + 'quic/quic_fec_group_test.cc', 'quic/quic_framer_test.cc', + 'quic/quic_packet_creator_test.cc', 'socket/buffered_write_stream_socket_unittest.cc', 'socket/client_socket_pool_base_unittest.cc', 'socket/deterministic_socket_data_unittest.cc', diff --git a/net/quic/quic_fec_group.cc b/net/quic/quic_fec_group.cc new file mode 100644 index 0000000..c678787 --- /dev/null +++ b/net/quic/quic_fec_group.cc @@ -0,0 +1,158 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/quic/quic_fec_group.h" + +#include <limits> + +#include "base/logging.h" + +using base::StringPiece; +using std::numeric_limits; +using std::set; + +namespace net { + +namespace { +const QuicPacketSequenceNumber kNoSequenceNumber = kuint64max; +} // namespace + +QuicFecGroup::QuicFecGroup() + : min_protected_packet_(kNoSequenceNumber), + max_protected_packet_(kNoSequenceNumber), + parity_len_(0) { +} + +QuicFecGroup::~QuicFecGroup() {} + +bool QuicFecGroup::Update(const QuicPacketHeader& header, + StringPiece decrypted_payload) { + if (received_packets_.count(header.packet_sequence_number) != 0) { + return false; + } + if (min_protected_packet_ != kNoSequenceNumber && + max_protected_packet_ != kNoSequenceNumber && + (header.packet_sequence_number < min_protected_packet_ || + header.packet_sequence_number > max_protected_packet_)) { + DLOG(ERROR) << "FEC group does not cover received packet: " + << header.packet_sequence_number; + return false; + } + if (!UpdateParity(decrypted_payload)) { + return false; + } + received_packets_.insert(header.packet_sequence_number); + return true; +} + +bool QuicFecGroup::UpdateFec( + QuicPacketSequenceNumber fec_packet_sequence_number, + const QuicFecData& fec) { + if (min_protected_packet_ != kNoSequenceNumber) { + return false; + } + set<QuicPacketSequenceNumber>::const_iterator it = received_packets_.begin(); + while (it != received_packets_.end()) { + if ((*it < fec.min_protected_packet_sequence_number) || + (*it >= fec_packet_sequence_number)) { + DLOG(ERROR) << "FEC group does not cover received packet: " << *it; + return false; + } + ++it; + } + if (!UpdateParity(fec.redundancy)) { + return false; + } + min_protected_packet_ = fec.min_protected_packet_sequence_number; + max_protected_packet_ = fec_packet_sequence_number - 1; + return true; +} + +bool QuicFecGroup::CanRevive() const { + // We can revive if we're missing exactly 1 packet. + return NumMissingPackets() == 1; +} + +bool QuicFecGroup::IsFinished() const { + // We are finished if we are not missing any packets. + return NumMissingPackets() == 0; +} + +size_t QuicFecGroup::Revive(QuicPacketHeader* header, + char* decrypted_payload, + size_t decrypted_payload_len) { + if (!CanRevive()) { + return 0; + } + + // Identify the packet sequence number to be resurrected. + QuicPacketSequenceNumber missing = kNoSequenceNumber; + for (QuicPacketSequenceNumber i = min_protected_packet_; + i <= max_protected_packet_; ++i) { + // Is this packet missing? + if (received_packets_.count(i) == 0) { + missing = i; + break; + } + } + DCHECK_NE(kNoSequenceNumber, missing); + + DCHECK_LE(parity_len_, decrypted_payload_len); + if (parity_len_ > decrypted_payload_len) { + return 0; + } + for (size_t i = 0; i < parity_len_; ++i) { + decrypted_payload[i] = parity_[i]; + } + header->packet_sequence_number = missing; + received_packets_.insert(missing); + return parity_len_; +} + +bool QuicFecGroup::ProtectsPacketsBefore(QuicPacketSequenceNumber num) const { + if (max_protected_packet_ != kNoSequenceNumber) { + return max_protected_packet_ < num; + } + // Since we might not yet have recevied the FEC packet, we must check + // the packets we have received. + return *received_packets_.begin() < num; +} + +bool QuicFecGroup::UpdateParity(StringPiece payload) { + DCHECK_LE(payload.size(), kMaxPacketSize); + if (payload.size() > kMaxPacketSize) { + DLOG(ERROR) << "Illegal payload size: " << payload.size(); + return false; + } + if (parity_len_ < payload.size()) { + parity_len_ = payload.size(); + } + DCHECK_LE(payload.size(), kMaxPacketSize); + if (received_packets_.size() == 0 && + min_protected_packet_ == kNoSequenceNumber) { + // Initialize the parity to the value of this payload + memcpy(parity_, payload.data(), payload.size()); + if (payload.size() < kMaxPacketSize) { + // TODO(rch): expand as needed. + memset(parity_ + payload.size(), 0, + kMaxPacketSize - payload.size()); + } + return true; + } + // Update the parity by XORing in the data (padding with 0s if necessary). + for (size_t i = 0; i < kMaxPacketSize; ++i) { + uint8 byte = i < payload.size() ? payload[i] : 0x00; + parity_[i] ^= byte; + } + return true; +} + +size_t QuicFecGroup::NumMissingPackets() const { + if (min_protected_packet_ == kNoSequenceNumber) + return numeric_limits<size_t>::max(); + return (max_protected_packet_ - min_protected_packet_ + 1) - + received_packets_.size(); +} + +} // namespace net diff --git a/net/quic/quic_fec_group.h b/net/quic/quic_fec_group.h new file mode 100644 index 0000000..63a3253 --- /dev/null +++ b/net/quic/quic_fec_group.h @@ -0,0 +1,81 @@ +// 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. +// +// Tracks information about an FEC group, including the packets +// that have been seen, and the running parity. Provided the ability +// to revive a dropped packet. + +#ifndef NET_QUIC_QUIC_FEC_GROUP_H_ +#define NET_QUIC_QUIC_FEC_GROUP_H_ + +#include <set> + +#include "base/string_piece.h" +#include "net/quic/quic_protocol.h" + +namespace net { + +class NET_EXPORT_PRIVATE QuicFecGroup { + public: + QuicFecGroup(); + ~QuicFecGroup(); + + // Updates the FEC group based on the delivery of a data packet. + // Returns false if this packet has already been seen, true otherwise. + bool Update(const QuicPacketHeader& header, + base::StringPiece decrypted_payload); + + // Updates the FEC group based on the delivery of an FEC packet. + // Returns false if this packet has already been seen or if it does + // not claim to protect all the packets previously seen in this group. + bool UpdateFec(QuicPacketSequenceNumber fec_packet_sequence_number, + const QuicFecData& fec); + + // Returns true if a packet can be revived from this FEC group. + bool CanRevive() const; + + // Returns true if all packets (FEC and data) from this FEC group have been + // seen or revived + bool IsFinished() const; + + // Revives the missing packet from this FEC group. This may return a packet + // that is null padded to a greater length than the original packet, but + // the framer will handle it correctly. Returns the length of the data + // written to |decrypted_payload|, or 0 if the packet could not be revived. + size_t Revive(QuicPacketHeader* header, + char* decrypted_payload, + size_t decrypted_payload_len); + + // Returns true of this FEC group protects any packets with sequence + // numbers less than |num|. + bool ProtectsPacketsBefore(QuicPacketSequenceNumber num) const; + + const base::StringPiece parity() const { + return base::StringPiece(parity_, parity_len_); + } + + private: + bool UpdateParity(base::StringPiece payload); + // Returns the number of missing packets, or size_t max if the number + // of missing packets is not known. + size_t NumMissingPackets() const; + + // Set of packets that we have recevied. + std::set<QuicPacketSequenceNumber> received_packets_; + // Sequence number of the first protected packet in this group (the one + // with the lowest packet sequence number). Will only be set once the FEC + // packet has been seen. + QuicPacketSequenceNumber min_protected_packet_; + // Sequence number of the last protected packet in this group (the one + // with the highest packet sequence number). Will only be set once the FEC + // packet has been seen. + QuicPacketSequenceNumber max_protected_packet_; + // The cumulative parity calculation of all received packets. + char parity_[kMaxPacketSize]; + size_t parity_len_; +}; + +} // namespace net + +#endif // NET_QUIC_QUIC_FEC_GROUP_H_ diff --git a/net/quic/quic_fec_group_test.cc b/net/quic/quic_fec_group_test.cc new file mode 100644 index 0000000..4c87488 --- /dev/null +++ b/net/quic/quic_fec_group_test.cc @@ -0,0 +1,207 @@ +// 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 <algorithm> +#include <vector> + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "net/quic/quic_fec_group.h" +#include "testing/gmock/include/gmock/gmock.h" + +using ::testing::_; +using base::StringPiece; + +namespace net { + +namespace { + +const char* kData[] = { + "abc12345678", + "987defg", + "ghi12345", + "987jlkmno", + "mno4567890", + "789pqrstuvw", +}; + +} // namespace + +class QuicFecGroupTest : public ::testing::Test { + protected: + void RunTest(size_t num_packets, size_t lost_packet, bool out_of_order) { + size_t max_len = strlen(kData[0]); + scoped_array<char>redundancy(new char[max_len]); + for (size_t i = 0; i < max_len; i++) { + // Initialize to the first packet. + DLOG(INFO) << "Setting redundancy[" << i << "]: " << kData[0][i]; + redundancy[i] = kData[0][i]; + // XOR in the remaining packets. + for (size_t packet = 1; packet < num_packets; packet++) { + uint8 byte = i > strlen(kData[packet]) ? 0x00 : kData[packet][i]; + redundancy[i] = redundancy[i] ^ byte; + DLOG(INFO) << "XORing redundancy[" << i << "]: " << byte; + } + } + + for (size_t i = 0; i < strlen(kData[0]); i++) { + DLOG(INFO) << "redundancy[" << i << "]: " << redundancy[i]; + } + + QuicFecGroup group; + + // If we're out of order, send the FEC packet in the position of the + // lost packet. Otherwise send all (non-missing) packets, then FEC. + if (out_of_order) { + // Update the FEC state for each non-lost packet. + for (size_t packet = 0; packet < num_packets; packet++) { + if (packet == lost_packet) { + ASSERT_FALSE(group.IsFinished()); + QuicFecData fec; + fec.min_protected_packet_sequence_number = 0; + fec.redundancy = StringPiece(redundancy.get(), strlen(kData[0])); + DLOG(INFO) << "fec.redundancy: " << fec.redundancy; + ASSERT_TRUE(group.UpdateFec(num_packets, fec)); + } else { + QuicPacketHeader header; + header.packet_sequence_number = packet; + ASSERT_TRUE(group.Update(header, kData[packet])); + } + ASSERT_TRUE(group.CanRevive() == (packet == num_packets - 1)); + } + } else { + // Update the FEC state for each non-lost packet. + for (size_t packet = 0; packet < num_packets; packet++) { + if (packet == lost_packet) { + continue; + } + + QuicPacketHeader header; + header.packet_sequence_number = packet; + ASSERT_TRUE(group.Update(header, kData[packet])); + ASSERT_FALSE(group.CanRevive()); + } + + ASSERT_FALSE(group.IsFinished()); + // Attempt to revive the missing packet. + QuicFecData fec; + fec.min_protected_packet_sequence_number = 0; + fec.redundancy = StringPiece(redundancy.get(), strlen(kData[0])); + DLOG(INFO) << "fec.redundancy: " << fec.redundancy; + + ASSERT_TRUE(group.UpdateFec(num_packets, fec)); + } + QuicPacketHeader header; + char recovered[kMaxPacketSize]; + ASSERT_TRUE(group.CanRevive()); + size_t len = group.Revive(&header, recovered, arraysize(recovered)); + ASSERT_NE(0u, len) + << "Failed to revive packet " << lost_packet << " out of " + << num_packets; + EXPECT_EQ(lost_packet, header.packet_sequence_number) + << "Failed to revive packet " << lost_packet << " out of " + << num_packets; + ASSERT_GE(len, strlen(kData[lost_packet])) << "Incorrect length"; + for (size_t i = 0; i < strlen(kData[lost_packet]); i++) { + EXPECT_EQ(kData[lost_packet][i], recovered[i]); + } + ASSERT_TRUE(group.IsFinished()); + } +}; + +TEST_F(QuicFecGroupTest, UpdateAndRevive) { + RunTest(2, 0, false); + RunTest(2, 1, false); + + RunTest(3, 0, false); + RunTest(3, 1, false); + RunTest(3, 2, false); +} + +TEST_F(QuicFecGroupTest, UpdateAndReviveOutOfOrder) { + RunTest(2, 0, true); + RunTest(2, 1, true); + + RunTest(3, 0, true); + RunTest(3, 1, true); + RunTest(3, 2, true); +} + +TEST_F(QuicFecGroupTest, UpdateFecIfReceivedPacketIsNotCovered) { + char data1[] = "abc123"; + char redundancy[arraysize(data1)]; + for (size_t i = 0; i < arraysize(data1); i++) { + redundancy[i] = data1[i]; + } + + QuicFecGroup group; + + QuicPacketHeader header; + header.packet_sequence_number = 3; + group.Update(header, data1); + + QuicFecData fec; + fec.min_protected_packet_sequence_number = 1; + fec.redundancy = redundancy; + + ASSERT_FALSE(group.UpdateFec(2, fec)); +} + +TEST_F(QuicFecGroupTest, ProtectsPacketsBefore) { + QuicPacketHeader header; + header.packet_sequence_number = 3; + + QuicFecGroup group; + ASSERT_TRUE(group.Update(header, kData[0])); + + EXPECT_FALSE(group.ProtectsPacketsBefore(1)); + EXPECT_FALSE(group.ProtectsPacketsBefore(2)); + EXPECT_FALSE(group.ProtectsPacketsBefore(3)); + EXPECT_TRUE(group.ProtectsPacketsBefore(4)); + EXPECT_TRUE(group.ProtectsPacketsBefore(5)); + EXPECT_TRUE(group.ProtectsPacketsBefore(50)); +} + +TEST_F(QuicFecGroupTest, ProtectsPacketsBeforeWithSeveralPackets) { + QuicPacketHeader header; + header.packet_sequence_number = 3; + + QuicFecGroup group; + ASSERT_TRUE(group.Update(header, kData[0])); + + header.packet_sequence_number = 7; + ASSERT_TRUE(group.Update(header, kData[0])); + + header.packet_sequence_number = 5; + ASSERT_TRUE(group.Update(header, kData[0])); + + EXPECT_FALSE(group.ProtectsPacketsBefore(1)); + EXPECT_FALSE(group.ProtectsPacketsBefore(2)); + EXPECT_FALSE(group.ProtectsPacketsBefore(3)); + EXPECT_TRUE(group.ProtectsPacketsBefore(4)); + EXPECT_TRUE(group.ProtectsPacketsBefore(5)); + EXPECT_TRUE(group.ProtectsPacketsBefore(6)); + EXPECT_TRUE(group.ProtectsPacketsBefore(7)); + EXPECT_TRUE(group.ProtectsPacketsBefore(8)); + EXPECT_TRUE(group.ProtectsPacketsBefore(9)); + EXPECT_TRUE(group.ProtectsPacketsBefore(50)); +} + +TEST_F(QuicFecGroupTest, ProtectsPacketsBeforeWithFecData) { + QuicFecData fec; + fec.min_protected_packet_sequence_number = 2; + fec.redundancy = kData[0]; + + QuicFecGroup group; + ASSERT_TRUE(group.UpdateFec(3, fec)); + + EXPECT_FALSE(group.ProtectsPacketsBefore(1)); + EXPECT_FALSE(group.ProtectsPacketsBefore(2)); + EXPECT_TRUE(group.ProtectsPacketsBefore(3)); + EXPECT_TRUE(group.ProtectsPacketsBefore(4)); + EXPECT_TRUE(group.ProtectsPacketsBefore(5)); + EXPECT_TRUE(group.ProtectsPacketsBefore(50)); +} + +} // namespace net diff --git a/net/quic/quic_framer.cc b/net/quic/quic_framer.cc index 143d6b9..8540a19 100644 --- a/net/quic/quic_framer.cc +++ b/net/quic/quic_framer.cc @@ -114,7 +114,7 @@ bool QuicFramer::ConstructFecPacket(const QuicPacketHeader& header, return false; } - if (!writer.WriteUInt48(fec.first_protected_packet_sequence_number)) { + if (!writer.WriteUInt48(fec.min_protected_packet_sequence_number)) { return false; } @@ -177,7 +177,7 @@ bool QuicFramer::ProcessPacket(const IPEndPoint& peer_address, QuicFecData fec_data; fec_data.fec_group = header.fec_group; if (!reader_->ReadUInt48( - &fec_data.first_protected_packet_sequence_number)) { + &fec_data.min_protected_packet_sequence_number)) { set_detailed_error("Unable to read first protected packet."); return RaiseError(QUIC_INVALID_FEC_DATA); } diff --git a/net/quic/quic_framer_test.cc b/net/quic/quic_framer_test.cc index 3b660f8..6b178d5 100644 --- a/net/quic/quic_framer_test.cc +++ b/net/quic/quic_framer_test.cc @@ -1289,7 +1289,7 @@ TEST_F(QuicFramerTest, FecPacket) { ASSERT_EQ(1, visitor_.fec_count_); const QuicFecData& fec_data = *visitor_.fec_data_[0]; EXPECT_EQ(GG_UINT64_C(0x0123456789ABB), - fec_data.first_protected_packet_sequence_number); + fec_data.min_protected_packet_sequence_number); EXPECT_EQ("abcdefghijklmnop", fec_data.redundancy); } @@ -2100,7 +2100,7 @@ TEST_F(QuicFramerTest, ConstructFecPacket) { QuicFecData fec_data; fec_data.fec_group = 1; - fec_data.first_protected_packet_sequence_number = + fec_data.min_protected_packet_sequence_number = GG_UINT64_C(0x123456789ABB); fec_data.redundancy = "abcdefghijklmnop"; diff --git a/net/quic/quic_packet_creator.cc b/net/quic/quic_packet_creator.cc new file mode 100644 index 0000000..deef83d --- /dev/null +++ b/net/quic/quic_packet_creator.cc @@ -0,0 +1,185 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/quic/quic_packet_creator.h" + +#include "base/logging.h" +#include "net/quic/quic_utils.h" +//#include "util/random/acmrandom.h" + +using base::StringPiece; +using std::make_pair; +using std::min; +using std::pair; +using std::vector; + +namespace net { + +QuicPacketCreator::QuicPacketCreator(QuicGuid guid, QuicFramer* framer) + : guid_(guid), + framer_(framer), + sequence_number_(0), + fec_group_number_(1) { + framer_->set_fec_builder(this); +} + +QuicPacketCreator::~QuicPacketCreator() { +} + +void QuicPacketCreator::OnBuiltFecProtectedPayload( + const QuicPacketHeader& header, + StringPiece payload) { + if (fec_group_.get()) { + fec_group_->Update(header, payload); + } +} + +void QuicPacketCreator::DataToStream(QuicStreamId id, + StringPiece data, + QuicStreamOffset offset, + bool fin, + vector<PacketPair>* packets) { + DCHECK_GT(options_.max_packet_length, + QuicUtils::StreamFramePacketOverhead(1)); + + QuicPacketHeader header; + + QuicPacket* packet = NULL; + QuicFrames frames; + QuicFecGroupNumber current_fec_group = 0; + QuicFecData fec_data; + if (options_.use_fec) { + DCHECK(!fec_group_.get()); + fec_group_.reset(new QuicFecGroup); + current_fec_group = fec_group_number_; + fec_data.fec_group = current_fec_group; + fec_data.min_protected_packet_sequence_number = sequence_number_ + 1; + } + + if (data.size() != 0) { + size_t data_to_send = data.size(); + size_t max_frame_len = framer_->GetMaxPlaintextSize( + options_.max_packet_length - + QuicUtils::StreamFramePacketOverhead(1)); + DCHECK_GT(max_frame_len, 0u); + size_t frame_len = min<size_t>(max_frame_len, data_to_send); + + while (data_to_send > 0) { + bool set_fin = false; + if (data_to_send <= frame_len) { // last loop + frame_len = min(data_to_send, frame_len); + set_fin = fin && !options_.separate_fin_packet; + } + StringPiece data_frame(data.data() + data.size() - data_to_send, + frame_len); + + QuicStreamFrame frame(id, set_fin, offset, data_frame); + frames.push_back(QuicFrame(&frame)); + FillPacketHeader(current_fec_group, PACKET_FLAGS_NONE, &header); + offset += frame_len; + data_to_send -= frame_len; + + // Produce the data packet (which might fin the stream). + framer_->ConstructFragementDataPacket(header, frames, &packet); + DCHECK_GE(options_.max_packet_length, packet->length()); + packets->push_back(make_pair(header.packet_sequence_number, packet)); + frames.clear(); + } + } + + // Create a new packet for the fin, if necessary. + if (fin && (options_.separate_fin_packet || data.size() == 0)) { + FillPacketHeader(current_fec_group, PACKET_FLAGS_NONE, &header); + QuicStreamFrame frame(id, true, offset, ""); + frames.push_back(QuicFrame(&frame)); + framer_->ConstructFragementDataPacket(header, frames, &packet); + packets->push_back(make_pair(header.packet_sequence_number, packet)); + frames.clear(); + } + + // Create a new FEC packet, if necessary + if (current_fec_group != 0) { + FillPacketHeader(current_fec_group, PACKET_FLAGS_FEC, &header); + fec_data.redundancy = fec_group_->parity(); + QuicPacket* fec_packet; + framer_->ConstructFecPacket(header, fec_data, &fec_packet); + packets->push_back(make_pair(header.packet_sequence_number, fec_packet)); + ++fec_group_number_; + } + /* + if (options_.random_reorder) { + int32 seed = ACMRandom::HostnamePidTimeSeed(); + ACMRandom random(seed); + DLOG(INFO) << "Seed " << seed; + + vector<PacketPair> tmp_store; + tmp_store.swap(*packets); + + while (tmp_store.size() != 0) { + int idx = random.Uniform(tmp_store.size()); + packets->push_back(tmp_store[idx]); + tmp_store.erase(tmp_store.begin() + idx); + } + } + */ + fec_group_.reset(NULL); +} + +QuicPacketCreator::PacketPair QuicPacketCreator::ResetStream( + QuicStreamId id, + QuicStreamOffset offset, + QuicErrorCode error) { + QuicPacketHeader header; + FillPacketHeader(0, PACKET_FLAGS_NONE, &header); + + QuicRstStreamFrame close_frame(id, offset, error); + + QuicPacket* packet; + QuicFrames frames; + frames.push_back(QuicFrame(&close_frame)); + framer_->ConstructFragementDataPacket(header, frames, &packet); + return make_pair(header.packet_sequence_number, packet); +} + +QuicPacketCreator::PacketPair QuicPacketCreator::CloseConnection( + QuicConnectionCloseFrame* close_frame) { + + QuicPacketHeader header; + FillPacketHeader(0, PACKET_FLAGS_NONE, &header); + + QuicPacket* packet; + QuicFrames frames; + frames.push_back(QuicFrame(close_frame)); + framer_->ConstructFragementDataPacket(header, frames, &packet); + return make_pair(header.packet_sequence_number, packet); +} + +QuicPacketCreator::PacketPair QuicPacketCreator::AckPacket( + QuicAckFrame* ack_frame) { + + QuicPacketHeader header; + FillPacketHeader(0, PACKET_FLAGS_NONE, &header); + + QuicPacket* packet; + QuicFrames frames; + frames.push_back(QuicFrame(ack_frame)); + framer_->ConstructFragementDataPacket(header, frames, &packet); + return make_pair(header.packet_sequence_number, packet); +} + +void QuicPacketCreator::FillPacketHeader(QuicFecGroupNumber fec_group, + QuicPacketFlags flags, + QuicPacketHeader* header) { + header->guid = guid_; + header->flags = flags; + header->packet_sequence_number = ++sequence_number_; + header->fec_group = fec_group; + + // Default to zero - the sender should increment this as packets are + // retransmitted. + header->retransmission_count = 0; + header->transmission_time = 0; +} + +} // namespace net diff --git a/net/quic/quic_packet_creator.h b/net/quic/quic_packet_creator.h new file mode 100644 index 0000000..2aa26b9 --- /dev/null +++ b/net/quic/quic_packet_creator.h @@ -0,0 +1,93 @@ +// 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. +// +// Some helpers for quic packet creation. + +#ifndef NET_QUIC_QUIC_PACKET_CREATOR_H_ +#define NET_QUIC_QUIC_PACKET_CREATOR_H_ + +#include <utility> +#include <vector> + +#include "base/string_piece.h" +#include "net/quic/quic_fec_group.h" +#include "net/quic/quic_framer.h" +#include "net/quic/quic_protocol.h" + +namespace net { + +class NET_EXPORT_PRIVATE QuicPacketCreator : public QuicFecBuilderInterface { + public: + // Options for controlling how packets are created. + struct Options { + Options() { + Clear(); + } + void Clear() { + memset(this, 0, sizeof(Options)); + max_packet_length = kMaxPacketSize; + } + + // TODO(alyssar, rch) max frames/packet + size_t max_packet_length; + bool separate_fin_packet; + bool random_reorder; // Inefficient: rewrite if used at scale. + // TODO(rch) should probably be max packets per group. + bool use_fec; + }; + + QuicPacketCreator(QuicGuid guid, QuicFramer* framer); + + virtual ~QuicPacketCreator(); + + // QuicFecBuilderInterface + virtual void OnBuiltFecProtectedPayload(const QuicPacketHeader& header, + base::StringPiece payload) OVERRIDE; + + typedef std::pair<QuicPacketSequenceNumber, QuicPacket*> PacketPair; + + // Converts a raw payload to a series of QuicPackets. + void DataToStream(QuicStreamId id, + base::StringPiece data, + QuicStreamOffset offset, + bool fin, + std::vector<PacketPair>* packets); + + PacketPair ResetStream(QuicStreamId id, + QuicStreamOffset offset, + QuicErrorCode error); + + PacketPair CloseConnection(QuicConnectionCloseFrame* close_frame); + + PacketPair AckPacket(QuicAckFrame* ack_frame); + + QuicPacketSequenceNumber sequence_number() const { + return sequence_number_; + } + + void set_sequence_number(QuicPacketSequenceNumber s) { + sequence_number_ = s; + } + + Options* options() { + return &options_; + } + + private: + void FillPacketHeader(QuicFecGroupNumber fec_group, + QuicPacketFlags flags, + QuicPacketHeader* header); + + Options options_; + QuicGuid guid_; + QuicFramer* framer_; + QuicPacketSequenceNumber sequence_number_; + QuicFecGroupNumber fec_group_number_; + scoped_ptr<QuicFecGroup> fec_group_; + +}; + +} // namespace net + +#endif // NET_QUIC_QUIC_PACKET_CREATOR_H_ diff --git a/net/quic/quic_packet_creator_test.cc b/net/quic/quic_packet_creator_test.cc new file mode 100644 index 0000000..3860456 --- /dev/null +++ b/net/quic/quic_packet_creator_test.cc @@ -0,0 +1,258 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/quic/quic_packet_creator.h" + +#include "base/stl_util.h" +#include "net/quic/crypto/null_encrypter.h" +#include "net/quic/quic_utils.h" +#include "net/quic/test_tools/quic_test_utils.h" +#include "testing/gmock/include/gmock/gmock.h" + +using testing::InSequence; +using testing::_; +using std::vector; +using std::string; + +namespace net { +namespace test { +namespace { + +class QuicPacketCreatorTest : public ::testing::Test { + protected: + QuicPacketCreatorTest() + : framer_(QuicDecrypter::Create(kNULL), QuicEncrypter::Create(kNULL)), + id_(1), + sequence_number_(0), + guid_(2), + data_("foo"), + utils_(guid_, &framer_) { + framer_.set_visitor(&framer_visitor_); + } + ~QuicPacketCreatorTest() { + STLDeleteValues(&packets_); + } + + void ProcessPackets() { + for (size_t i = 0; i < packets_.size(); ++i) { + scoped_ptr<QuicEncryptedPacket> encrypted( + framer_.EncryptPacket(*packets_[i].second)); + framer_.ProcessPacket(IPEndPoint(), *encrypted); + } + } + + vector<QuicPacketCreator::PacketPair> packets_; + QuicFramer framer_; + testing::StrictMock<MockFramerVisitor> framer_visitor_; + QuicStreamId id_; + QuicPacketSequenceNumber sequence_number_; + QuicGuid guid_; + string data_; + QuicPacketCreator utils_; +}; + +TEST_F(QuicPacketCreatorTest, DataToStreamBasic) { + utils_.DataToStream(id_, data_, 0, true, &packets_); + + ASSERT_EQ(1u, packets_.size()); + ASSERT_EQ(1u, utils_.sequence_number()); + + InSequence s; + EXPECT_CALL(framer_visitor_, OnPacket(_)); + EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); + EXPECT_CALL(framer_visitor_, OnStreamFrame(_)); + EXPECT_CALL(framer_visitor_, OnPacketComplete()); + + ProcessPackets(); +} + +TEST_F(QuicPacketCreatorTest, DataToStreamFec) { + utils_.options()->use_fec = true; + utils_.DataToStream(id_, data_, 0, true, &packets_); + + ASSERT_EQ(2u, packets_.size()); + ASSERT_EQ(2u, utils_.sequence_number()); + + InSequence s; + EXPECT_CALL(framer_visitor_, OnPacket(_)); + EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); + EXPECT_CALL(framer_visitor_, OnFecProtectedPayload(_)); + EXPECT_CALL(framer_visitor_, OnStreamFrame(_)); + EXPECT_CALL(framer_visitor_, OnPacketComplete()); + + EXPECT_CALL(framer_visitor_, OnPacket(_)); + EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); + EXPECT_CALL(framer_visitor_, OnFecData(_)); + EXPECT_CALL(framer_visitor_, OnPacketComplete()); + + ProcessPackets(); +} + +TEST_F(QuicPacketCreatorTest, DataToStreamFecHandled) { + utils_.options()->use_fec = true; + utils_.DataToStream(id_, data_, 0, true, &packets_); + + ASSERT_EQ(2u, packets_.size()); + ASSERT_EQ(2u, utils_.sequence_number()); + + QuicFecData fec_data; + fec_data.fec_group = 1; + fec_data.min_protected_packet_sequence_number = 1; + fec_data.redundancy = packets_[0].second->FecProtectedData(); + + InSequence s; + // Data packet + EXPECT_CALL(framer_visitor_, OnPacket(_)); + EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); + EXPECT_CALL(framer_visitor_, OnFecProtectedPayload(_)); + EXPECT_CALL(framer_visitor_, OnStreamFrame(_)); + EXPECT_CALL(framer_visitor_, OnPacketComplete()); + + // FEC packet + EXPECT_CALL(framer_visitor_, OnPacket(_)); + EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); + EXPECT_CALL(framer_visitor_, OnFecData(fec_data)); + EXPECT_CALL(framer_visitor_, OnPacketComplete()); + + ProcessPackets(); + + // Revived data packet + EXPECT_CALL(framer_visitor_, OnRevivedPacket()); + EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); + EXPECT_CALL(framer_visitor_, OnStreamFrame(_)); + EXPECT_CALL(framer_visitor_, OnPacketComplete()); + + QuicPacketHeader header; + framer_.ProcessRevivedPacket(header, fec_data.redundancy); +} + +TEST_F(QuicPacketCreatorTest, DataToStreamSkipFin) { + utils_.DataToStream(id_, data_, 0, false, &packets_); + + ASSERT_EQ(1u, packets_.size()); + ASSERT_EQ(1u, utils_.sequence_number()); + + InSequence s; + EXPECT_CALL(framer_visitor_, OnPacket(_)); + EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); + EXPECT_CALL(framer_visitor_, OnStreamFrame(_)); + EXPECT_CALL(framer_visitor_, OnPacketComplete()); + + ProcessPackets(); +} + +TEST_F(QuicPacketCreatorTest, DataToStreamSeparateFin) { + utils_.options()->separate_fin_packet = true; + + utils_.DataToStream(id_, data_, 0, true, &packets_); + + ASSERT_EQ(2u, packets_.size()); + ASSERT_EQ(2u, utils_.sequence_number()); + + InSequence s; + EXPECT_CALL(framer_visitor_, OnPacket(_)); + EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); + EXPECT_CALL(framer_visitor_, OnStreamFrame(_)); + EXPECT_CALL(framer_visitor_, OnPacketComplete()); + + EXPECT_CALL(framer_visitor_, OnPacket(_)); + EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); + EXPECT_CALL(framer_visitor_, OnStreamFrame(_)); + EXPECT_CALL(framer_visitor_, OnPacketComplete()); + + ProcessPackets(); +} + +TEST_F(QuicPacketCreatorTest, NoData) { + data_ = ""; + + utils_.DataToStream(id_, data_, 0, true, &packets_); + + ASSERT_EQ(1u, packets_.size()); + ASSERT_EQ(1u, utils_.sequence_number()); + + InSequence s; + EXPECT_CALL(framer_visitor_, OnPacket(_)); + EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); + EXPECT_CALL(framer_visitor_, OnStreamFrame(_)); + EXPECT_CALL(framer_visitor_, OnPacketComplete()); + + ProcessPackets(); +} + +TEST_F(QuicPacketCreatorTest, NoDataSeparateFin) { + utils_.options()->separate_fin_packet = true; + data_ = ""; + + utils_.DataToStream(id_, data_, 0, true, &packets_); + + ASSERT_EQ(1u, packets_.size()); + ASSERT_EQ(1u, utils_.sequence_number()); + + InSequence s; + EXPECT_CALL(framer_visitor_, OnPacket(_)); + EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); + EXPECT_CALL(framer_visitor_, OnStreamFrame(_)); + EXPECT_CALL(framer_visitor_, OnPacketComplete()); + + ProcessPackets(); +} + +TEST_F(QuicPacketCreatorTest, MultiplePackets) { + size_t ciphertext_size = NullEncrypter().GetCiphertextSize(2); + utils_.options()->max_packet_length = + ciphertext_size + QuicUtils::StreamFramePacketOverhead(1); + + utils_.DataToStream(id_, data_, 0, true, &packets_); + + ASSERT_EQ(2u, packets_.size()); + ASSERT_EQ(2u, utils_.sequence_number()); + + InSequence s; + EXPECT_CALL(framer_visitor_, OnPacket(_)); + EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); + EXPECT_CALL(framer_visitor_, OnStreamFrame(_)); + EXPECT_CALL(framer_visitor_, OnPacketComplete()); + + EXPECT_CALL(framer_visitor_, OnPacket(_)); + EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); + EXPECT_CALL(framer_visitor_, OnStreamFrame(_)); + EXPECT_CALL(framer_visitor_, OnPacketComplete()); + + ProcessPackets(); +} + +TEST_F(QuicPacketCreatorTest, MultiplePacketsWithSeparateFin) { + size_t ciphertext_size = NullEncrypter().GetCiphertextSize(2); + utils_.options()->max_packet_length = + ciphertext_size + QuicUtils::StreamFramePacketOverhead(1); + utils_.options()->separate_fin_packet = true; + + utils_.DataToStream(id_, data_, 0, true, &packets_); + + ASSERT_EQ(3u, packets_.size()); + ASSERT_EQ(3u, utils_.sequence_number()); + + InSequence s; + EXPECT_CALL(framer_visitor_, OnPacket(_)); + EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); + EXPECT_CALL(framer_visitor_, OnStreamFrame(_)); + EXPECT_CALL(framer_visitor_, OnPacketComplete()); + + EXPECT_CALL(framer_visitor_, OnPacket(_)); + EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); + EXPECT_CALL(framer_visitor_, OnStreamFrame(_)); + EXPECT_CALL(framer_visitor_, OnPacketComplete()); + + EXPECT_CALL(framer_visitor_, OnPacket(_)); + EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); + EXPECT_CALL(framer_visitor_, OnStreamFrame(_)); + EXPECT_CALL(framer_visitor_, OnPacketComplete()); + + ProcessPackets(); +} + +} // namespace +} // namespace test +} // namespace net diff --git a/net/quic/quic_protocol.cc b/net/quic/quic_protocol.cc index ffa2e08..4ca2d3e 100644 --- a/net/quic/quic_protocol.cc +++ b/net/quic/quic_protocol.cc @@ -57,8 +57,8 @@ bool QuicFecData::operator==(const QuicFecData& other) const { if (fec_group != other.fec_group) { return false; } - if (first_protected_packet_sequence_number != - other.first_protected_packet_sequence_number) { + if (min_protected_packet_sequence_number != + other.min_protected_packet_sequence_number) { return false; } if (redundancy != other.redundancy) { diff --git a/net/quic/quic_protocol.h b/net/quic/quic_protocol.h index 04cd091..9990fac 100644 --- a/net/quic/quic_protocol.h +++ b/net/quic/quic_protocol.h @@ -154,9 +154,9 @@ struct NET_EXPORT_PRIVATE QuicPacketHeader { struct NET_EXPORT_PRIVATE QuicStreamFrame { QuicStreamFrame(); QuicStreamFrame(QuicStreamId stream_id, - bool fin, - uint64 offset, - base::StringPiece data); + bool fin, + uint64 offset, + base::StringPiece data); QuicStreamId stream_id; bool fin; @@ -241,8 +241,8 @@ struct NET_EXPORT_PRIVATE CongestionInfo { struct NET_EXPORT_PRIVATE QuicAckFrame { QuicAckFrame() {} QuicAckFrame(QuicPacketSequenceNumber largest_received, - QuicTransmissionTime time_received, - QuicPacketSequenceNumber least_unacked) { + QuicTransmissionTime time_received, + QuicPacketSequenceNumber least_unacked) { received_info.largest_received = largest_received; received_info.time_received = time_received; sent_info.least_unacked = least_unacked; @@ -307,12 +307,14 @@ typedef std::vector<QuicFrame> QuicFrames; struct NET_EXPORT_PRIVATE QuicFecData { QuicFecData(); + + bool operator==(const QuicFecData& other) const; + QuicFecGroupNumber fec_group; - QuicPacketSequenceNumber first_protected_packet_sequence_number; + QuicPacketSequenceNumber min_protected_packet_sequence_number; // The last protected packet's sequence number will be one // less than the sequence number of the FEC packet. base::StringPiece redundancy; - bool operator==(const QuicFecData& other) const; }; struct NET_EXPORT_PRIVATE QuicPacketData { diff --git a/net/quic/quic_utils.cc b/net/quic/quic_utils.cc index 44e8343..289c910 100644 --- a/net/quic/quic_utils.cc +++ b/net/quic/quic_utils.cc @@ -10,7 +10,7 @@ namespace net { // static -int QuicUtils::StreamFramePacketOverhead(int num_frames) { +size_t QuicUtils::StreamFramePacketOverhead(int num_frames) { // TODO(jar): Use sizeof(some name). return kPacketHeaderSize + 1 + // frame count diff --git a/net/quic/quic_utils.h b/net/quic/quic_utils.h index fd2facb..1c8298a 100644 --- a/net/quic/quic_utils.h +++ b/net/quic/quic_utils.h @@ -25,7 +25,7 @@ class NET_EXPORT_PRIVATE QuicUtils { public: // The overhead the quic framing will add for a packet with num_frames // frames. - static int StreamFramePacketOverhead(int num_frames); + static size_t StreamFramePacketOverhead(int num_frames); // returns the 128 bit FNV1a hash of the data. See // http://www.isthe.com/chongo/tech/comp/fnv/index.html#FNV-param |