summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrch@chromium.org <rch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-10-23 23:29:37 +0000
committerrch@chromium.org <rch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-10-23 23:29:37 +0000
commita5061244d407a27683b36e4d1ed35d895722805d (patch)
treec48806826561e5d26be8e9ac45012984c1a65ecf
parent46c39747bfa9048442e89f47b578df99001a9575 (diff)
downloadchromium_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.gyp6
-rw-r--r--net/quic/quic_fec_group.cc158
-rw-r--r--net/quic/quic_fec_group.h81
-rw-r--r--net/quic/quic_fec_group_test.cc207
-rw-r--r--net/quic/quic_framer.cc4
-rw-r--r--net/quic/quic_framer_test.cc4
-rw-r--r--net/quic/quic_packet_creator.cc185
-rw-r--r--net/quic/quic_packet_creator.h93
-rw-r--r--net/quic/quic_packet_creator_test.cc258
-rw-r--r--net/quic/quic_protocol.cc4
-rw-r--r--net/quic/quic_protocol.h16
-rw-r--r--net/quic/quic_utils.cc2
-rw-r--r--net/quic/quic_utils.h2
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