summaryrefslogtreecommitdiffstats
path: root/net/quic
diff options
context:
space:
mode:
authorrtenneti@chromium.org <rtenneti@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-10-28 01:51:15 +0000
committerrtenneti@chromium.org <rtenneti@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-10-28 01:51:15 +0000
commit583bcbcff1814196c2e7fa4b999712c3d66b126a (patch)
tree11b67f5bab0d47b8863b4ea05b4c9accd81f794e /net/quic
parent963eac2fb452fe9b9c1ba69ddabb3d1a012eff20 (diff)
downloadchromium_src-583bcbcff1814196c2e7fa4b999712c3d66b126a.zip
chromium_src-583bcbcff1814196c2e7fa4b999712c3d66b126a.tar.gz
chromium_src-583bcbcff1814196c2e7fa4b999712c3d66b126a.tar.bz2
Land Recent QUIC changes.
Enables the padding of QUIC handshake packets to the max packet size. Does not pad packets which do not leave enough space to fit a padding frame after expanding the final stream frame. An alternative would be to add a new type of padding frame which could show up earlier in the packet, but this is good enough for now. flag to enable the padding of QUIC handshake packets to full size Merge internal change: 55118010 Add a set_seed method to the PacketDroppingTestWriter to facilitate reproducing test behavior. Merge internal change: 55107501 Consistently pass the IsHandshake flag to the congestion control algorithm in QUIC. Merge internal change: 55046282 The goal is parity between HTTP and QUIC testing. Merge internal change: 55000555 Fixed comments - found while back porting changes to server. Merge internal change: 54999641 Fix bug in QuicPacketCreator's packet size calculation when a stream frame is close to the maximum possible. Merge internal change: 54933729 Remove unused random_reorder field from QuicPacketCreator::Options Merge internal change: 54885136 Using GFE's IOVector to replace uses of struct iovec on the QUIC's Write path. Merge internal change: 54805402 R=rch@chromium.org Review URL: https://codereview.chromium.org/46903002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@231278 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/quic')
-rw-r--r--net/quic/iovector.cc13
-rw-r--r--net/quic/iovector.h201
-rw-r--r--net/quic/quic_connection.cc203
-rw-r--r--net/quic/quic_connection.h48
-rw-r--r--net/quic/quic_connection_helper_test.cc19
-rw-r--r--net/quic/quic_connection_test.cc148
-rw-r--r--net/quic/quic_framer.cc19
-rw-r--r--net/quic/quic_framer.h6
-rw-r--r--net/quic/quic_packet_creator.cc130
-rw-r--r--net/quic/quic_packet_creator.h19
-rw-r--r--net/quic/quic_packet_creator_test.cc162
-rw-r--r--net/quic/quic_packet_writer.h6
-rw-r--r--net/quic/quic_session.cc4
-rw-r--r--net/quic/test_tools/quic_test_utils.cc11
-rw-r--r--net/quic/test_tools/quic_test_utils.h5
15 files changed, 731 insertions, 263 deletions
diff --git a/net/quic/iovector.cc b/net/quic/iovector.cc
new file mode 100644
index 0000000..a6d2c61
--- /dev/null
+++ b/net/quic/iovector.cc
@@ -0,0 +1,13 @@
+// Copyright 2013 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/iovector.h"
+
+namespace net {
+
+IOVector::IOVector() {}
+
+IOVector::~IOVector() {}
+
+} // namespace net
diff --git a/net/quic/iovector.h b/net/quic/iovector.h
new file mode 100644
index 0000000..52e9d1a
--- /dev/null
+++ b/net/quic/iovector.h
@@ -0,0 +1,201 @@
+// Copyright 2013 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.
+
+#ifndef NET_QUIC_IOVECTOR_H_
+#define NET_QUIC_IOVECTOR_H_
+
+#include <stddef.h>
+#include <algorithm>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "net/base/iovec.h"
+#include "net/base/net_export.h"
+
+namespace net {
+
+// Calculate the total number of bytes in an array of iovec structures.
+inline size_t TotalIovecLength(const struct iovec* iov, int iovcnt) {
+ size_t length = 0;
+ if (iov != NULL) {
+ for (int i = 0; i < iovcnt; ++i) {
+ length += iov[i].iov_len;
+ }
+ }
+ return length;
+}
+
+// IOVector is a helper class that makes it easier to work with POSIX vector I/O
+// struct. It is a thin wrapper by design and thus has no virtual functions and
+// all inlined methods. This class makes no assumptions about the ordering of
+// the pointer values of the blocks appended, it simply counts bytes when asked
+// to consume bytes.
+//
+// IOVector is a bookkeeping object that collects a description of buffers to
+// be read or written together and in order. It does not take ownership of the
+// blocks appended.
+//
+// Because it is used for scatter-gather operations, the order in which the
+// buffer blocks are added to the IOVector is important to the client. The
+// intended usage pattern is:
+//
+// iovector.Append(p0, len0);
+// ...
+// iovector.Append(pn, lenn);
+// int bytes_written = writev(fd, iovector.iovec(), iovector.Size());
+// if (bytes_written > 0)
+// iovector.Consume(bytes_written);
+//
+// The sequence is the same for readv, except that Consume() in this case is
+// used to change the IOVector to only keep track of description of blocks of
+// memory not yet written to.
+//
+// IOVector does not have any method to change the iovec entries that it
+// accumulates. This is due to the block merging nature of Append(): we'd like
+// to avoid accidentally change an entry that is assembled by two or more
+// Append()'s by simply an index access.
+//
+
+class NET_EXPORT_PRIVATE IOVector {
+ public:
+ // Provide a default constructor so it'll never be inhibited by adding other
+ // constructors.
+ IOVector();
+ ~IOVector();
+
+ // Provides a way to convert system call-like iovec representation to
+ // IOVector.
+ void AppendIovec(const struct iovec* iov, int iovcnt) {
+ for (int i = 0; i < iovcnt; ++i)
+ Append(static_cast<char*>(iov[i].iov_base), iov[i].iov_len);
+ }
+
+ // Appends at most max_bytes from iovec to the IOVector.
+ size_t AppendIovecAtMostBytes(const struct iovec* iov,
+ int iovcnt,
+ size_t max_bytes) {
+ size_t bytes_appended = 0;
+ for (int i = 0; i < iovcnt && max_bytes > 0; ++i) {
+ const size_t length = std::min(max_bytes, iov[i].iov_len);
+ Append(static_cast<char*>(iov[i].iov_base), length);
+ max_bytes -= length;
+ bytes_appended += length;
+ }
+ return bytes_appended;
+ }
+
+ // Append another block to the IOVector. Since IOVector can be used for read
+ // and write, it always takes char*. Clients that writes will need to cast
+ // away the constant of the pointer before appending a block.
+ void Append(char* buffer, size_t length) {
+ if (buffer != NULL && length > 0) {
+ if (iovec_.size() > 0) {
+ struct iovec& last = iovec_.back();
+ // If the new block is contiguous with the last block, just extend.
+ if (static_cast<char*>(last.iov_base) + last.iov_len == buffer) {
+ last.iov_len += length;
+ return;
+ }
+ }
+ struct iovec tmp = {buffer, length};
+ iovec_.push_back(tmp);
+ }
+ }
+
+ // Same as Append, but doesn't do the tail merge optimization.
+ // Intended for testing.
+ void AppendNoCoalesce(char* buffer, size_t length) {
+ if (buffer != NULL && length > 0) {
+ struct iovec tmp = {buffer, length};
+ iovec_.push_back(tmp);
+ }
+ }
+
+ // Remove a number of bytes from the beginning of the IOVector. Since vector
+ // I/O operations always occur at the beginning of the block list, a method
+ // to remove bytes at the end is not provided.
+ // It returns the number of bytes actually consumed (it'll only be smaller
+ // than the requested number if the IOVector contains less data).
+ size_t Consume(size_t length) {
+ if (length == 0) return 0;
+
+ size_t bytes_to_consume = length;
+ std::vector<struct iovec>::iterator iter = iovec_.begin();
+ std::vector<struct iovec>::iterator end = iovec_.end();
+ for (; iter < end && bytes_to_consume >= iter->iov_len; ++iter) {
+ bytes_to_consume -= iter->iov_len;
+ }
+ iovec_.erase(iovec_.begin(), iter);
+ if (iovec_.size() > 0 && bytes_to_consume != 0) {
+ iovec_[0].iov_base =
+ static_cast<char*>(iovec_[0].iov_base) + bytes_to_consume;
+ iovec_[0].iov_len -= bytes_to_consume;
+ return length;
+ }
+ if (iovec_.size() == 0 && bytes_to_consume > 0) {
+ LOG(DFATAL) << "Attempting to consume " << bytes_to_consume
+ << " non-existent bytes.";
+ }
+ // At this point bytes_to_consume is the number of wanted bytes left over
+ // after walking through all the iovec entries.
+ return length - bytes_to_consume;
+ }
+
+ // TODO(joechan): If capacity is large, swap out for a blank one.
+ // Clears the IOVector object to contain no blocks.
+ void Clear() { iovec_.clear(); }
+
+ // Swap the guts of two IOVector.
+ void Swap(IOVector* other) { iovec_.swap(other->iovec_); }
+
+ // Returns the number of valid blocks in the IOVector (not the number of
+ // bytes).
+ int Size() const { return iovec_.size(); }
+
+ // Returns the total storage used by the IOVector in number of blocks (not
+ // the number of bytes).
+ int Capacity() const { return iovec_.capacity(); }
+
+ // Returns true if there are no blocks in the IOVector.
+ bool Empty() const { return iovec_.empty(); }
+
+ // Returns the pointer to the beginning of the iovec to be used for vector
+ // I/O operations. If the IOVector has no blocks appened, this function
+ // returns NULL.
+ struct iovec* iovec() { return !Empty() ? &iovec_[0] : NULL; }
+
+ // Const version.
+ const struct iovec* iovec() const { return !Empty() ? &iovec_[0] : NULL; }
+
+ // Returns a pointer to one past the last byte of the last block. If the
+ // IOVector is empty, NULL is returned.
+ const char* LastBlockEnd() const {
+ return iovec_.size() > 0 ?
+ static_cast<char *>(iovec_.back().iov_base) + iovec_.back().iov_len :
+ NULL;
+ }
+
+ // Returns the total number of bytes in the IOVector.
+ size_t TotalBufferSize() const { return TotalIovecLength(iovec(), Size()); }
+
+ void Resize(int count) {
+ iovec_.resize(count);
+ }
+
+ private:
+ std::vector<struct iovec> iovec_;
+
+ // IOVector has value-semantics; copy and assignment are allowed.
+ // This class does not explicitly define copy/move constructors or the
+ // assignment operator to preserve compiler-generated copy/move constructors
+ // and assignment operators. Note that since IOVector does not own the
+ // actual buffers that the struct iovecs point to, copies and assignments
+ // result in a shallow copy of the buffers; resulting IOVectors will point
+ // to the same copy of the underlying data.
+};
+
+} // namespace net
+
+#endif // NET_QUIC_IOVECTOR_H_
diff --git a/net/quic/quic_connection.cc b/net/quic/quic_connection.cc
index 7448b51..ea575aa 100644
--- a/net/quic/quic_connection.cc
+++ b/net/quic/quic_connection.cc
@@ -17,6 +17,7 @@
#include "base/stl_util.h"
#include "net/quic/crypto/quic_decrypter.h"
#include "net/quic/crypto/quic_encrypter.h"
+#include "net/quic/iovector.h"
#include "net/quic/quic_bandwidth.h"
#include "net/quic/quic_utils.h"
@@ -175,10 +176,13 @@ net::QuicConnection::Force HasForcedFrames(
}
net::IsHandshake HasCryptoHandshake(
- const RetransmittableFrames& retransmittable_frames) {
- for (size_t i = 0; i < retransmittable_frames.frames().size(); ++i) {
- if (retransmittable_frames.frames()[i].type == STREAM_FRAME &&
- retransmittable_frames.frames()[i].stream_frame->stream_id ==
+ const RetransmittableFrames* retransmittable_frames) {
+ if (!retransmittable_frames) {
+ return net::NOT_HANDSHAKE;
+ }
+ for (size_t i = 0; i < retransmittable_frames->frames().size(); ++i) {
+ if (retransmittable_frames->frames()[i].type == STREAM_FRAME &&
+ retransmittable_frames->frames()[i].stream_frame->stream_id ==
kCryptoStreamId) {
return net::IS_HANDSHAKE;
}
@@ -846,10 +850,9 @@ void QuicConnection::SendVersionNegotiationPacket() {
pending_version_negotiation_packet_ = true;
}
-QuicConsumedData QuicConnection::SendvStreamDataInner(
+QuicConsumedData QuicConnection::SendStreamDataInner(
QuicStreamId id,
- const struct iovec* iov,
- int iov_count,
+ const IOVector& data,
QuicStreamOffset offset,
bool fin,
QuicAckNotifier* notifier) {
@@ -861,17 +864,18 @@ QuicConsumedData QuicConnection::SendvStreamDataInner(
size_t bytes_written = 0;
bool fin_consumed = false;
- for (int i = 0; i < iov_count; ++i) {
- bool send_fin = fin && (i == iov_count - 1);
- if (!send_fin && iov[i].iov_len == 0) {
+ for (int i = 0; i < data.Size(); ++i) {
+ bool send_fin = fin && (i == data.Size() - 1);
+ if (!send_fin && data.iovec()[i].iov_len == 0) {
LOG(DFATAL) << "Attempt to send empty stream frame";
}
- StringPiece data(static_cast<char*>(iov[i].iov_base), iov[i].iov_len);
+ StringPiece data_piece(static_cast<char*>(data.iovec()[i].iov_base),
+ data.iovec()[i].iov_len);
int currentOffset = offset + bytes_written;
QuicConsumedData consumed_data =
packet_generator_.ConsumeData(id,
- data,
+ data_piece,
currentOffset,
send_fin,
notifier);
@@ -881,12 +885,12 @@ QuicConsumedData QuicConnection::SendvStreamDataInner(
fin_consumed = consumed_data.fin_consumed;
// If no bytes were consumed, bail now, because the stream can not write
// more data.
- if (consumed_data.bytes_consumed < iov[i].iov_len) {
+ if (consumed_data.bytes_consumed < data.iovec()[i].iov_len) {
break;
}
}
// Handle the 0 byte write properly.
- if (iov_count == 0) {
+ if (data.Empty()) {
DCHECK(fin);
QuicConsumedData consumed_data = packet_generator_.ConsumeData(
id, StringPiece(), offset, fin, NULL);
@@ -897,22 +901,20 @@ QuicConsumedData QuicConnection::SendvStreamDataInner(
return QuicConsumedData(bytes_written, fin_consumed);
}
-QuicConsumedData QuicConnection::SendvStreamData(QuicStreamId id,
- const struct iovec* iov,
- int iov_count,
- QuicStreamOffset offset,
- bool fin) {
- return SendvStreamDataInner(id, iov, iov_count, offset, fin, NULL);
+QuicConsumedData QuicConnection::SendStreamData(QuicStreamId id,
+ const IOVector& data,
+ QuicStreamOffset offset,
+ bool fin) {
+ return SendStreamDataInner(id, data, offset, fin, NULL);
}
-QuicConsumedData QuicConnection::SendvStreamDataAndNotifyWhenAcked(
+QuicConsumedData QuicConnection::SendStreamDataAndNotifyWhenAcked(
QuicStreamId id,
- const struct iovec* iov,
- int iov_count,
+ const IOVector& data,
QuicStreamOffset offset,
bool fin,
QuicAckNotifier::DelegateInterface* delegate) {
- if (!fin && iov_count == 0) {
+ if (!fin && data.Empty()) {
LOG(DFATAL) << "Attempt to send empty stream frame";
}
@@ -920,7 +922,7 @@ QuicConsumedData QuicConnection::SendvStreamDataAndNotifyWhenAcked(
// no data was consumed).
QuicAckNotifier* notifier = new QuicAckNotifier(delegate);
QuicConsumedData consumed_data =
- SendvStreamDataInner(id, iov, iov_count, offset, fin, notifier);
+ SendStreamDataInner(id, data, offset, fin, notifier);
if (consumed_data.bytes_consumed == 0) {
// No data was consumed, delete the notifier.
@@ -1066,6 +1068,7 @@ bool QuicConnection::WriteQueuedPackets() {
packet_iterator->packet,
packet_iterator->transmission_type,
packet_iterator->retransmittable,
+ packet_iterator->handshake,
packet_iterator->forced)) {
packet_iterator = queued_packets_.erase(packet_iterator);
} else {
@@ -1086,7 +1089,7 @@ void QuicConnection::WritePendingRetransmissions() {
sent_packet_manager_.NextPendingRetransmission();
if (HasForcedFrames(&pending.retransmittable_frames) == NO_FORCE &&
!CanWrite(pending.transmission_type, HAS_RETRANSMITTABLE_DATA,
- HasCryptoHandshake(pending.retransmittable_frames))) {
+ HasCryptoHandshake(&pending.retransmittable_frames))) {
break;
}
@@ -1117,6 +1120,8 @@ void QuicConnection::WritePendingRetransmissions() {
serialized_packet.entropy_hash,
pending.transmission_type,
HAS_RETRANSMITTABLE_DATA,
+ HasCryptoHandshake(
+ serialized_packet.retransmittable_frames),
HasForcedFrames(
serialized_packet.retransmittable_frames));
}
@@ -1242,70 +1247,13 @@ bool QuicConnection::WritePacket(EncryptionLevel level,
QuicPacket* packet,
TransmissionType transmission_type,
HasRetransmittableData retransmittable,
+ IsHandshake handshake,
Force forced) {
- if (!connected_) {
- DLOG(INFO) << ENDPOINT
- << "Not sending packet as connection is disconnected.";
+ if (ShouldDiscardPacket(level, sequence_number, retransmittable)) {
delete packet;
- // Returning true because we deleted the packet and the caller shouldn't
- // delete it again.
return true;
}
- if (encryption_level_ == ENCRYPTION_FORWARD_SECURE &&
- level == ENCRYPTION_NONE) {
- // Drop packets that are NULL encrypted since the peer won't accept them
- // anymore.
- DLOG(INFO) << ENDPOINT << "Dropping packet: " << sequence_number
- << " since the packet is NULL encrypted.";
- sent_packet_manager_.DiscardUnackedPacket(sequence_number);
- delete packet;
- return true;
- }
-
- if (retransmittable == HAS_RETRANSMITTABLE_DATA) {
- if (!sent_packet_manager_.IsUnacked(sequence_number)) {
- // This is a crazy edge case, but if we retransmit a packet,
- // (but have to queue it for some reason) then receive an ack
- // for the previous transmission (but not the retransmission)
- // then receive a truncated ACK which causes us to raise the
- // high water mark, all before we're able to send the packet
- // then we can simply drop it.
- DLOG(INFO) << ENDPOINT << "Dropping packet: " << sequence_number
- << " since it has already been acked.";
- delete packet;
- return true;
- }
-
- if (sent_packet_manager_.IsPreviousTransmission(sequence_number)) {
- // If somehow we have already retransmitted this packet *before*
- // we actually send it for the first time (I think this is probably
- // impossible in the real world), then don't bother sending it.
- // We don't want to call DiscardUnackedPacket because in this case
- // the peer has not yet ACK'd the data. We need the subsequent
- // retransmission to be sent.
- DLOG(INFO) << ENDPOINT << "Dropping packet: " << sequence_number
- << " since it has already been retransmitted.";
- delete packet;
- return true;
- }
-
- if (!sent_packet_manager_.HasRetransmittableFrames(sequence_number)) {
- DLOG(INFO) << ENDPOINT << "Dropping packet: " << sequence_number
- << " since a previous transmission has been acked.";
- sent_packet_manager_.DiscardUnackedPacket(sequence_number);
- delete packet;
- return true;
- }
- }
-
- // TODO(wtc): use the same logic that is used in the packet generator.
- // Namely, a packet is a handshake if it contains a stream frame for the
- // crypto stream. It should be possible to look at the RetransmittableFrames
- // in the SerializedPacket to determine this for a packet.
- IsHandshake handshake = level == ENCRYPTION_NONE ? IS_HANDSHAKE
- : NOT_HANDSHAKE;
-
// If we are not forced and we can't write, then simply return false;
if (forced == NO_FORCE &&
!CanWrite(transmission_type, retransmittable, handshake)) {
@@ -1351,7 +1299,13 @@ bool QuicConnection::WritePacket(EncryptionLevel level,
packet->is_fec_packet(),
packet->length()));
- WriteResult result = WritePacketToWire(sequence_number, level, *encrypted);
+ WriteResult result =
+ writer_->WritePacket(encrypted->data(), encrypted->length(),
+ self_address().address(), peer_address(), this);
+ if (debug_visitor_) {
+ // Pass the write result to the visitor.
+ debug_visitor_->OnPacketSent(sequence_number, level, *encrypted, result);
+ }
if (result.status == WRITE_STATUS_BLOCKED) {
// TODO(satyashekhar): It might be more efficient (fewer system calls), if
// all connections share this variable i.e this becomes a part of
@@ -1376,6 +1330,62 @@ bool QuicConnection::WritePacket(EncryptionLevel level,
return false;
}
+bool QuicConnection::ShouldDiscardPacket(
+ EncryptionLevel level,
+ QuicPacketSequenceNumber sequence_number,
+ HasRetransmittableData retransmittable) {
+ if (!connected_) {
+ DLOG(INFO) << ENDPOINT
+ << "Not sending packet as connection is disconnected.";
+ return true;
+ }
+
+ if (encryption_level_ == ENCRYPTION_FORWARD_SECURE &&
+ level == ENCRYPTION_NONE) {
+ // Drop packets that are NULL encrypted since the peer won't accept them
+ // anymore.
+ DLOG(INFO) << ENDPOINT << "Dropping packet: " << sequence_number
+ << " since the packet is NULL encrypted.";
+ sent_packet_manager_.DiscardUnackedPacket(sequence_number);
+ return true;
+ }
+
+ if (retransmittable == HAS_RETRANSMITTABLE_DATA) {
+ if (!sent_packet_manager_.IsUnacked(sequence_number)) {
+ // This is a crazy edge case, but if we retransmit a packet,
+ // (but have to queue it for some reason) then receive an ack
+ // for the previous transmission (but not the retransmission)
+ // then receive a truncated ACK which causes us to raise the
+ // high water mark, all before we're able to send the packet
+ // then we can simply drop it.
+ DLOG(INFO) << ENDPOINT << "Dropping packet: " << sequence_number
+ << " since it has already been acked.";
+ return true;
+ }
+
+ if (sent_packet_manager_.IsPreviousTransmission(sequence_number)) {
+ // If somehow we have already retransmitted this packet *before*
+ // we actually send it for the first time (I think this is probably
+ // impossible in the real world), then don't bother sending it.
+ // We don't want to call DiscardUnackedPacket because in this case
+ // the peer has not yet ACK'd the data. We need the subsequent
+ // retransmission to be sent.
+ DLOG(INFO) << ENDPOINT << "Dropping packet: " << sequence_number
+ << " since it has already been retransmitted.";
+ return true;
+ }
+
+ if (!sent_packet_manager_.HasRetransmittableFrames(sequence_number)) {
+ DLOG(INFO) << ENDPOINT << "Dropping packet: " << sequence_number
+ << " since a previous transmission has been acked.";
+ sent_packet_manager_.DiscardUnackedPacket(sequence_number);
+ return true;
+ }
+ }
+
+ return false;
+}
+
bool QuicConnection::OnPacketSent(WriteResult result) {
DCHECK_NE(WRITE_STATUS_BLOCKED, result.status);
if (pending_write_.get() == NULL) {
@@ -1435,20 +1445,6 @@ bool QuicConnection::OnPacketSent(WriteResult result) {
return true;
}
-WriteResult QuicConnection::WritePacketToWire(
- QuicPacketSequenceNumber sequence_number,
- EncryptionLevel level,
- const QuicEncryptedPacket& packet) {
- WriteResult result =
- writer_->WritePacket(packet.data(), packet.length(),
- self_address().address(), peer_address(), this);
- if (debug_visitor_) {
- // Pass the write result to the visitor.
- debug_visitor_->OnPacketSent(sequence_number, level, packet, result);
- }
- return result;
-}
-
bool QuicConnection::OnSerializedPacket(
const SerializedPacket& serialized_packet) {
if (serialized_packet.retransmittable_frames) {
@@ -1467,6 +1463,8 @@ bool QuicConnection::OnSerializedPacket(
serialized_packet.retransmittable_frames != NULL ?
HAS_RETRANSMITTABLE_DATA :
NO_RETRANSMITTABLE_DATA,
+ HasCryptoHandshake(
+ serialized_packet.retransmittable_frames),
HasForcedFrames(
serialized_packet.retransmittable_frames));
}
@@ -1490,13 +1488,14 @@ bool QuicConnection::SendOrQueuePacket(EncryptionLevel level,
QuicPacketEntropyHash entropy_hash,
TransmissionType transmission_type,
HasRetransmittableData retransmittable,
+ IsHandshake handshake,
Force forced) {
sent_entropy_manager_.RecordPacketEntropyHash(sequence_number, entropy_hash);
if (!WritePacket(level, sequence_number, packet,
- transmission_type, retransmittable, forced)) {
+ transmission_type, retransmittable, handshake, forced)) {
queued_packets_.push_back(QueuedPacket(sequence_number, packet, level,
transmission_type, retransmittable,
- forced));
+ handshake, forced));
return false;
}
return true;
diff --git a/net/quic/quic_connection.h b/net/quic/quic_connection.h
index 7f3875b..0487b3e 100644
--- a/net/quic/quic_connection.h
+++ b/net/quic/quic_connection.h
@@ -28,6 +28,7 @@
#include "net/base/iovec.h"
#include "net/base/ip_endpoint.h"
#include "net/quic/congestion_control/quic_congestion_manager.h"
+#include "net/quic/iovector.h"
#include "net/quic/quic_ack_notifier.h"
#include "net/quic/quic_ack_notifier_manager.h"
#include "net/quic/quic_alarm.h"
@@ -198,24 +199,22 @@ class NET_EXPORT_PRIVATE QuicConnection
QuicVersion version);
virtual ~QuicConnection();
- // Send the data in |iov| to the peer in as few packets as possible.
+ // Send the data in |data| to the peer in as few packets as possible.
// Returns a pair with the number of bytes consumed from data, and a boolean
// indicating if the fin bit was consumed. This does not indicate the data
// has been sent on the wire: it may have been turned into a packet and queued
// if the socket was unexpectedly blocked.
- QuicConsumedData SendvStreamData(QuicStreamId id,
- const struct iovec* iov,
- int iov_count,
- QuicStreamOffset offset,
- bool fin);
+ QuicConsumedData SendStreamData(QuicStreamId id,
+ const IOVector& data,
+ QuicStreamOffset offset,
+ bool fin);
- // Same as SendvStreamData, except the provided delegate will be informed
+ // Same as SendStreamData, except the provided delegate will be informed
// once ACKs have been received for all the packets written.
// The |delegate| is not owned by the QuicConnection and must outlive it.
- QuicConsumedData SendvStreamDataAndNotifyWhenAcked(
+ QuicConsumedData SendStreamDataAndNotifyWhenAcked(
QuicStreamId id,
- const struct iovec* iov,
- int iov_count,
+ const IOVector& data,
QuicStreamOffset offset,
bool fin,
QuicAckNotifier::DelegateInterface* delegate);
@@ -417,6 +416,7 @@ class NET_EXPORT_PRIVATE QuicConnection
QuicPacketEntropyHash entropy_hash,
TransmissionType transmission_type,
HasRetransmittableData retransmittable,
+ IsHandshake handshake,
Force forced);
// Writes the given packet to socket, encrypted with |level|, with the help
@@ -432,12 +432,9 @@ class NET_EXPORT_PRIVATE QuicConnection
QuicPacket* packet,
TransmissionType transmission_type,
HasRetransmittableData retransmittable,
+ IsHandshake handshake,
Force force);
- WriteResult WritePacketToWire(QuicPacketSequenceNumber sequence_number,
- EncryptionLevel level,
- const QuicEncryptedPacket& packet);
-
// Make sure an ack we got from our peer is sane.
bool ValidateAckFrame(const QuicAckFrame& incoming_ack);
@@ -486,12 +483,14 @@ class NET_EXPORT_PRIVATE QuicConnection
EncryptionLevel level,
TransmissionType transmission_type,
HasRetransmittableData retransmittable,
+ IsHandshake handshake,
Force forced)
: sequence_number(sequence_number),
packet(packet),
encryption_level(level),
transmission_type(transmission_type),
retransmittable(retransmittable),
+ handshake(handshake),
forced(forced) {
}
@@ -500,6 +499,7 @@ class NET_EXPORT_PRIVATE QuicConnection
const EncryptionLevel encryption_level;
TransmissionType transmission_type;
HasRetransmittableData retransmittable;
+ IsHandshake handshake;
Force forced;
};
@@ -573,14 +573,13 @@ class NET_EXPORT_PRIVATE QuicConnection
RetransmissionTimeComparator>
RetransmissionTimeouts;
- // Inner helper function for SendvStreamData and
- // SendvStreamDataAndNotifyWhenAcked.
- QuicConsumedData SendvStreamDataInner(QuicStreamId id,
- const struct iovec* iov,
- int iov_count,
- QuicStreamOffset offset,
- bool fin,
- QuicAckNotifier *notifier);
+ // Inner helper function for SendStreamData and
+ // SendStreamDataAndNotifyWhenAcked.
+ QuicConsumedData SendStreamDataInner(QuicStreamId id,
+ const IOVector& data,
+ QuicStreamOffset offset,
+ bool fin,
+ QuicAckNotifier *notifier);
// Sends a version negotiation packet to the peer.
void SendVersionNegotiationPacket();
@@ -617,6 +616,11 @@ class NET_EXPORT_PRIVATE QuicConnection
// Writes as many pending retransmissions as possible.
void WritePendingRetransmissions();
+ // Returns true if the packet should be discarded and not sent.
+ bool ShouldDiscardPacket(EncryptionLevel level,
+ QuicPacketSequenceNumber sequence_number,
+ HasRetransmittableData retransmittable);
+
// Queues |packet| in the hopes that it can be decrypted in the
// future, when a new key is installed.
void QueueUndecryptablePacket(const QuicEncryptedPacket& packet);
diff --git a/net/quic/quic_connection_helper_test.cc b/net/quic/quic_connection_helper_test.cc
index d88bcb6..89915bc 100644
--- a/net/quic/quic_connection_helper_test.cc
+++ b/net/quic/quic_connection_helper_test.cc
@@ -355,15 +355,15 @@ TEST_F(QuicConnectionHelperTest, TestRTORetransmission) {
QuicTime::Delta::FromMilliseconds(500);
QuicTime start = clock_.ApproximateNow();
+ char buffer[] = "foo";
+
EXPECT_CALL(*send_algorithm_, OnPacketSent(_, 1, _, NOT_RETRANSMISSION, _));
EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(1, _));
EXPECT_CALL(visitor_, OnCanWrite()).WillOnce(Return(true));
EXPECT_CALL(visitor_, HasPendingHandshake()).Times(AnyNumber());
-
- // Send a packet.
- struct iovec iov = {const_cast<char*>(kData),
- static_cast<size_t>(strlen(kData))};
- connection_->SendvStreamData(1, &iov, 1, 0, false);
+ IOVector data;
+ data.Append(buffer, 3);
+ connection_->SendStreamData(1, data, 0, false);
EXPECT_CALL(*send_algorithm_, OnPacketSent(_, 2, _, RTO_RETRANSMISSION, _));
// Since no ack was received, the retransmission alarm will fire and
// retransmit it.
@@ -387,15 +387,16 @@ TEST_F(QuicConnectionHelperTest, TestMultipleRTORetransmission) {
QuicTime::Delta::FromMilliseconds(500);
QuicTime start = clock_.ApproximateNow();
+ char buffer[] = "foo";
+
EXPECT_CALL(*send_algorithm_, OnPacketSent(_, 1, _, NOT_RETRANSMISSION, _));
EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(1, _));
EXPECT_CALL(visitor_, OnCanWrite()).WillRepeatedly(Return(true));
EXPECT_CALL(visitor_, HasPendingHandshake()).Times(AnyNumber());
- // Send a packet.
- struct iovec iov = {const_cast<char*>(kData),
- static_cast<size_t>(strlen(kData))};
- connection_->SendvStreamData(1, &iov, 1, 0, false);
+ IOVector data;
+ data.Append(buffer, 3);
+ connection_->SendStreamData(1, data, 0, false);
EXPECT_CALL(*send_algorithm_, OnPacketSent(_, 2, _, RTO_RETRANSMISSION, _));
// Since no ack was received, the retransmission alarm will fire and
// retransmit it.
diff --git a/net/quic/quic_connection_test.cc b/net/quic/quic_connection_test.cc
index 8f76cb6..5cf94d7 100644
--- a/net/quic/quic_connection_test.cc
+++ b/net/quic/quic_connection_test.cc
@@ -6,6 +6,7 @@
#include "base/basictypes.h"
#include "base/bind.h"
+#include "base/stl_util.h"
#include "net/base/net_errors.h"
#include "net/quic/congestion_control/receive_algorithm_interface.h"
#include "net/quic/congestion_control/send_algorithm_interface.h"
@@ -277,6 +278,10 @@ class TestPacketWriter : public QuicPacketWriter {
packets_write_attempts_(0) {
}
+ virtual ~TestPacketWriter() {
+ STLDeleteElements(&stream_data_);
+ }
+
// QuicPacketWriter
virtual WriteResult WritePacket(
const char* buffer, size_t buf_len,
@@ -308,6 +313,12 @@ class TestPacketWriter : public QuicPacketWriter {
}
if (visitor.stream_frames() != NULL && !visitor.stream_frames()->empty()) {
stream_frames_ = *visitor.stream_frames();
+ // Also make a copy of underlying data, since the data that the frames in
+ // |stream_frames_| point to is bound to the |visitor|'s scope.
+ for (size_t i = 0; i < stream_frames_.size(); ++i) {
+ stream_data_.push_back(new string(*visitor.stream_data()[i]));
+ stream_frames_[i].data = *(stream_data_.back());
+ }
}
if (visitor.version_negotiation_packet() != NULL) {
version_negotiation_packet_.reset(new QuicVersionNegotiationPacket(
@@ -370,6 +381,7 @@ class TestPacketWriter : public QuicPacketWriter {
scoped_ptr<QuicAckFrame> ack_;
scoped_ptr<QuicCongestionFeedbackFrame> feedback_;
vector<QuicStreamFrame> stream_frames_;
+ vector<string*> stream_data_;
scoped_ptr<QuicVersionNegotiationPacket> version_negotiation_packet_;
size_t last_packet_size_;
bool blocked_;
@@ -421,33 +433,38 @@ class TestConnection : public QuicConnection {
packet, entropy_hash, retransmittable_frames));
}
- QuicConsumedData SendStreamData(QuicStreamId id,
- StringPiece data,
- QuicStreamOffset offset,
- bool fin) {
- struct iovec iov = {const_cast<char*>(data.data()),
- static_cast<size_t>(data.size())};
- return SendvStreamData(id, &iov, 1, offset, fin);
+ QuicConsumedData SendStreamDataWithString(QuicStreamId id,
+ StringPiece data,
+ QuicStreamOffset offset,
+ bool fin) {
+ IOVector data_iov;
+ if (!data.empty()) {
+ data_iov.Append(const_cast<char*>(data.data()), data.size());
+ }
+ return QuicConnection::SendStreamData(id, data_iov, offset, fin);
}
- QuicConsumedData SendStreamDataAndNotifyWhenAcked(
+ QuicConsumedData SendStreamDataWithStringAndNotifyWhenAcked(
QuicStreamId id,
StringPiece data,
QuicStreamOffset offset,
bool fin,
QuicAckNotifier::DelegateInterface* delegate) {
- struct iovec iov = {const_cast<char*>(data.data()),
- static_cast<size_t>(data.size())};
- return SendvStreamDataAndNotifyWhenAcked(id, &iov, 1, offset, fin,
- delegate);
+ IOVector data_iov;
+ if (!data.empty()) {
+ data_iov.Append(const_cast<char*>(data.data()), data.size());
+ }
+ return QuicConnection::SendStreamDataAndNotifyWhenAcked(id, data_iov,
+ offset, fin,
+ delegate);
}
QuicConsumedData SendStreamData3() {
- return SendStreamData(kStreamId3, "food", 0, !kFin);
+ return SendStreamDataWithString(kStreamId3, "food", 0, !kFin);
}
QuicConsumedData SendStreamData5() {
- return SendStreamData(kStreamId5, "food2", 0, !kFin);
+ return SendStreamDataWithString(kStreamId5, "food2", 0, !kFin);
}
// The crypto stream has special semantics so that it is not blocked by a
@@ -458,7 +475,7 @@ class TestConnection : public QuicConnection {
QuicConsumedData SendCryptoStreamData() {
this->Flush();
QuicConsumedData consumed =
- SendStreamData(kCryptoStreamId, "chlo", 0, !kFin);
+ SendStreamDataWithString(kCryptoStreamId, "chlo", 0, !kFin);
this->Flush();
return consumed;
}
@@ -703,7 +720,7 @@ class QuicConnectionTest : public ::testing::TestWithParam<bool> {
QuicByteCount packet_size;
EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
.WillOnce(DoAll(SaveArg<2>(&packet_size), Return(true)));
- connection_.SendStreamData(id, data, offset, fin);
+ connection_.SendStreamDataWithString(id, data, offset, fin);
if (last_packet != NULL) {
*last_packet =
QuicConnectionPeer::GetPacketCreator(&connection_)->sequence_number();
@@ -997,7 +1014,7 @@ TEST_P(QuicConnectionTest, AckReceiptCausesAckSend) {
.WillOnce(DoAll(SaveArg<1>(&original), SaveArg<2>(&packet_size),
Return(true)));
EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(1, _)).Times(1);
- connection_.SendStreamData(1, "foo", 0, !kFin);
+ connection_.SendStreamDataWithString(1, "foo", 0, !kFin);
QuicAckFrame frame(original, QuicTime::Zero(), 1);
frame.received_info.missing_packets.insert(original);
frame.received_info.entropy_hash = QuicConnectionPeer::GetSentEntropyHash(
@@ -1294,8 +1311,8 @@ TEST_P(QuicConnectionTest, FECSending) {
EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(6);
// The first stream frame will consume 2 fewer bytes than the other three.
const string payload(payload_length * 4 - 6, 'a');
- connection_.SendStreamData(1, payload, 0, !kFin);
- // Expect the FEC group to be closed after SendStreamData.
+ connection_.SendStreamDataWithString(1, payload, 0, !kFin);
+ // Expect the FEC group to be closed after SendStreamDataWithString.
EXPECT_FALSE(creator_.ShouldSendFec(true));
}
@@ -1312,7 +1329,7 @@ TEST_P(QuicConnectionTest, FECQueueing) {
EXPECT_EQ(0u, connection_.NumQueuedPackets());
writer_->set_blocked(true);
const string payload(payload_length, 'a');
- connection_.SendStreamData(1, payload, 0, !kFin);
+ connection_.SendStreamDataWithString(1, payload, 0, !kFin);
EXPECT_FALSE(creator_.ShouldSendFec(true));
// Expect the first data packet and the fec packet to be queued.
EXPECT_EQ(2u, connection_.NumQueuedPackets());
@@ -1322,7 +1339,7 @@ TEST_P(QuicConnectionTest, AbandonFECFromCongestionWindow) {
connection_.options()->max_packets_per_fec_group = 1;
// 1 Data and 1 FEC packet.
EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(2);
- connection_.SendStreamData(1, "foo", 0, !kFin);
+ connection_.SendStreamDataWithString(1, "foo", 0, !kFin);
const QuicTime::Delta retransmission_time =
QuicTime::Delta::FromMilliseconds(5000);
@@ -1342,7 +1359,7 @@ TEST_P(QuicConnectionTest, DontAbandonAckedFEC) {
// 1 Data and 1 FEC packet.
EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(2);
- connection_.SendStreamData(1, "foo", 0, !kFin);
+ connection_.SendStreamDataWithString(1, "foo", 0, !kFin);
QuicAckFrame ack_fec(2, QuicTime::Zero(), 1);
// Data packet missing.
@@ -1528,9 +1545,10 @@ TEST_P(QuicConnectionTest, FramePackingSendv) {
EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, NOT_RETRANSMISSION, _));
char data[] = "ABCD";
- iovec iov[2] = { {static_cast<void*>(data), 2},
- {static_cast<void*>(data + 2), 2} };
- connection_.SendvStreamData(1, iov, 2, 0, !kFin);
+ IOVector data_iov;
+ data_iov.AppendNoCoalesce(data, 2);
+ data_iov.AppendNoCoalesce(data + 2, 2);
+ connection_.SendStreamData(1, data_iov, 0, !kFin);
EXPECT_EQ(0u, connection_.NumQueuedPackets());
EXPECT_FALSE(connection_.HasQueuedData());
@@ -1541,6 +1559,8 @@ TEST_P(QuicConnectionTest, FramePackingSendv) {
EXPECT_EQ(2u, writer_->stream_frames()->size());
EXPECT_EQ(1u, (*writer_->stream_frames())[0].stream_id);
EXPECT_EQ(1u, (*writer_->stream_frames())[1].stream_id);
+ EXPECT_EQ("AB", (*writer_->stream_frames())[0].data.as_string());
+ EXPECT_EQ("CD", (*writer_->stream_frames())[1].data.as_string());
}
TEST_P(QuicConnectionTest, FramePackingSendvQueued) {
@@ -1549,9 +1569,10 @@ TEST_P(QuicConnectionTest, FramePackingSendvQueued) {
writer_->set_blocked(true);
char data[] = "ABCD";
- iovec iov[2] = { {static_cast<void*>(data), 2},
- {static_cast<void*>(data + 2), 2} };
- connection_.SendvStreamData(1, iov, 2, 0, !kFin);
+ IOVector data_iov;
+ data_iov.AppendNoCoalesce(data, 2);
+ data_iov.AppendNoCoalesce(data + 2, 2);
+ connection_.SendStreamData(1, data_iov, 0, !kFin);
EXPECT_EQ(1u, connection_.NumQueuedPackets());
EXPECT_TRUE(connection_.HasQueuedData());
@@ -1577,9 +1598,8 @@ TEST_P(QuicConnectionTest, FramePackingSendvQueued) {
TEST_P(QuicConnectionTest, SendingZeroBytes) {
// Send a zero byte write with a fin using writev.
EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, NOT_RETRANSMISSION, _));
-
- iovec iov[1];
- connection_.SendvStreamData(1, iov, 0, 0, kFin);
+ IOVector empty_iov;
+ connection_.SendStreamData(1, empty_iov, 0, kFin);
EXPECT_EQ(0u, connection_.NumQueuedPackets());
EXPECT_FALSE(connection_.HasQueuedData());
@@ -1720,7 +1740,7 @@ TEST_P(QuicConnectionTest, RetransmitNackedLargestObserved) {
.WillOnce(DoAll(SaveArg<1>(&largest_observed), SaveArg<2>(&packet_size),
Return(true)));
EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(1, _)).Times(1);
- connection_.SendStreamData(1, "foo", 0, !kFin);
+ connection_.SendStreamDataWithString(1, "foo", 0, !kFin);
QuicAckFrame frame(1, QuicTime::Zero(), largest_observed);
frame.received_info.missing_packets.insert(largest_observed);
frame.received_info.entropy_hash = QuicConnectionPeer::GetSentEntropyHash(
@@ -1737,7 +1757,7 @@ TEST_P(QuicConnectionTest, RetransmitNackedLargestObserved) {
TEST_P(QuicConnectionTest, QueueAfterTwoRTOs) {
for (int i = 0; i < 10; ++i) {
EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
- connection_.SendStreamData(1, "foo", i * 3, !kFin);
+ connection_.SendStreamDataWithString(1, "foo", i * 3, !kFin);
}
// Block the congestion window and ensure they're queued.
@@ -1763,7 +1783,7 @@ TEST_P(QuicConnectionTest, WriteBlockedThenSent) {
writer_->set_blocked(true);
writer_->set_is_write_blocked_data_buffered(true);
- connection_.SendStreamData(1, "foo", 0, !kFin);
+ connection_.SendStreamDataWithString(1, "foo", 0, !kFin);
EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet());
EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
@@ -1998,9 +2018,9 @@ TEST_P(QuicConnectionTest, SendHandshakeMessages) {
// Attempt to send a handshake message while the congestion manager
// does not permit sending.
EXPECT_CALL(*send_algorithm_,
- TimeUntilSend(_, _, _, _)).WillRepeatedly(
+ TimeUntilSend(_, _, _, IS_HANDSHAKE)).WillRepeatedly(
testing::Return(QuicTime::Delta::Infinite()));
- connection_.SendStreamData(1, "foo", 0, !kFin);
+ connection_.SendStreamDataWithString(1, "foo", 0, !kFin);
// The packet should be serialized, but not queued.
EXPECT_EQ(1u, connection_.NumQueuedPackets());
@@ -2010,7 +2030,7 @@ TEST_P(QuicConnectionTest, SendHandshakeMessages) {
// Now become writeable and flush the packets.
EXPECT_CALL(*send_algorithm_,
- TimeUntilSend(_, _, _, _)).WillRepeatedly(
+ TimeUntilSend(_, _, _, IS_HANDSHAKE)).WillRepeatedly(
testing::Return(QuicTime::Delta::Zero()));
EXPECT_CALL(visitor_, OnCanWrite());
connection_.OnCanWrite();
@@ -2094,11 +2114,11 @@ TEST_P(QuicConnectionTest, TestRetransmitOrder) {
EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).WillOnce(
DoAll(SaveArg<2>(&first_packet_size), Return(true)));
- connection_.SendStreamData(1, "first_packet", 0, !kFin);
+ connection_.SendStreamDataWithString(1, "first_packet", 0, !kFin);
QuicByteCount second_packet_size;
EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).WillOnce(
DoAll(SaveArg<2>(&second_packet_size), Return(true)));
- connection_.SendStreamData(1, "second_packet", 12, !kFin);
+ connection_.SendStreamDataWithString(1, "second_packet", 12, !kFin);
EXPECT_NE(first_packet_size, second_packet_size);
// Advance the clock by huge time to make sure packets will be retransmitted.
clock_.AdvanceTime(QuicTime::Delta::FromSeconds(10));
@@ -2130,7 +2150,7 @@ TEST_P(QuicConnectionTest, TestRetransmissionCountCalculation) {
QuicPacketSequenceNumber original_sequence_number;
EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, NOT_RETRANSMISSION, _))
.WillOnce(DoAll(SaveArg<1>(&original_sequence_number), Return(true)));
- connection_.SendStreamData(1, "foo", 0, !kFin);
+ connection_.SendStreamDataWithString(1, "foo", 0, !kFin);
EXPECT_TRUE(QuicConnectionPeer::IsSavedForRetransmission(
&connection_, original_sequence_number));
@@ -2181,7 +2201,7 @@ TEST_P(QuicConnectionTest, TestRetransmissionCountCalculation) {
TEST_P(QuicConnectionTest, SetRTOAfterWritingToSocket) {
writer_->set_blocked(true);
- connection_.SendStreamData(1, "foo", 0, !kFin);
+ connection_.SendStreamDataWithString(1, "foo", 0, !kFin);
// Make sure that RTO is not started when the packet is queued.
EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet());
@@ -2195,8 +2215,8 @@ TEST_P(QuicConnectionTest, DelayRTOWithAckReceipt) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, NOT_RETRANSMISSION, _))
.Times(2);
- connection_.SendStreamData(1, "foo", 0, !kFin);
- connection_.SendStreamData(2, "bar", 0, !kFin);
+ connection_.SendStreamDataWithString(1, "foo", 0, !kFin);
+ connection_.SendStreamDataWithString(2, "bar", 0, !kFin);
QuicAlarm* retransmission_alarm = connection_.GetRetransmissionAlarm();
EXPECT_TRUE(retransmission_alarm->IsSet());
@@ -2233,7 +2253,7 @@ TEST_P(QuicConnectionTest, DelayRTOWithAckReceipt) {
TEST_P(QuicConnectionTest, TestQueued) {
EXPECT_EQ(0u, connection_.NumQueuedPackets());
writer_->set_blocked(true);
- connection_.SendStreamData(1, "foo", 0, !kFin);
+ connection_.SendStreamDataWithString(1, "foo", 0, !kFin);
EXPECT_EQ(1u, connection_.NumQueuedPackets());
// Attempt to send all packets, but since we're actually still
@@ -2423,7 +2443,7 @@ TEST_P(QuicConnectionTest, SendSchedulerDelayThenRetransmit) {
EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(1, _)).Times(1);
EXPECT_CALL(*send_algorithm_,
OnPacketSent(_, 1, _, NOT_RETRANSMISSION, _));
- connection_.SendStreamData(1, "foo", 0, !kFin);
+ connection_.SendStreamDataWithString(1, "foo", 0, !kFin);
EXPECT_EQ(0u, connection_.NumQueuedPackets());
// Advance the time for retransmission of lost packet.
clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(501));
@@ -2540,7 +2560,8 @@ TEST_P(QuicConnectionTest, TestQueueLimitsOnSendStreamData) {
testing::Return(QuicTime::Delta::FromMicroseconds(10)));
const string payload(payload_length, 'a');
EXPECT_EQ(0u,
- connection_.SendStreamData(3, payload, 0, !kFin).bytes_consumed);
+ connection_.SendStreamDataWithString(3, payload, 0,
+ !kFin).bytes_consumed);
EXPECT_EQ(0u, connection_.NumQueuedPackets());
}
@@ -2557,7 +2578,8 @@ TEST_P(QuicConnectionTest, LoopThroughSendingPackets) {
// The first stream frame will consume 2 fewer bytes than the other six.
const string payload(payload_length * 7 - 12, 'a');
EXPECT_EQ(payload.size(),
- connection_.SendStreamData(1, payload, 0, !kFin).bytes_consumed);
+ connection_.SendStreamDataWithString(1, payload, 0,
+ !kFin).bytes_consumed);
}
TEST_P(QuicConnectionTest, SendDelayedAckOnTimer) {
@@ -2593,7 +2615,7 @@ TEST_P(QuicConnectionTest, SendDelayedAckOnOutgoingPacket) {
}
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
ProcessPacket(1);
- connection_.SendStreamData(1, "foo", 0, !kFin);
+ connection_.SendStreamDataWithString(1, "foo", 0, !kFin);
// Check that ack is bundled with outgoing data and that delayed ack
// alarm is reset.
EXPECT_EQ(2u, writer_->frame_count());
@@ -2910,12 +2932,12 @@ TEST_P(QuicConnectionTest, BadVersionNegotiation) {
TEST_P(QuicConnectionTest, CheckSendStats) {
EXPECT_CALL(*send_algorithm_,
OnPacketSent(_, _, _, NOT_RETRANSMISSION, _));
- connection_.SendStreamData(1u, "first", 0, !kFin);
+ connection_.SendStreamDataWithString(1u, "first", 0, !kFin);
size_t first_packet_size = last_sent_packet_size();
EXPECT_CALL(*send_algorithm_,
OnPacketSent(_, _, _, NOT_RETRANSMISSION, _));
- connection_.SendStreamData(1u, "second", 0, !kFin);
+ connection_.SendStreamDataWithString(1u, "second", 0, !kFin);
size_t second_packet_size = last_sent_packet_size();
// 2 retransmissions due to rto, 1 due to explicit nack.
@@ -3074,7 +3096,7 @@ TEST_P(QuicConnectionTest, ConnectionCloseWhenNotWriteBlocked) {
writer_->set_blocked(false); // Already default.
// Send a packet (but write will not block).
- connection_.SendStreamData(1, "foo", 0, !kFin);
+ connection_.SendStreamDataWithString(1, "foo", 0, !kFin);
EXPECT_EQ(0u, connection_.NumQueuedPackets());
EXPECT_EQ(1u, writer_->packets_write_attempts());
@@ -3089,7 +3111,7 @@ TEST_P(QuicConnectionTest, ConnectionCloseWhenWriteBlocked) {
writer_->set_blocked(true);
// Send a packet to so that write will really block.
- connection_.SendStreamData(1, "foo", 0, !kFin);
+ connection_.SendStreamDataWithString(1, "foo", 0, !kFin);
EXPECT_EQ(1u, connection_.NumQueuedPackets());
EXPECT_EQ(1u, writer_->packets_write_attempts());
@@ -3116,7 +3138,8 @@ TEST_P(QuicConnectionTest, AckNotifierTriggerCallback) {
EXPECT_CALL(delegate, OnAckNotification()).Times(1);;
// Send some data, which will register the delegate to be notified.
- connection_.SendStreamDataAndNotifyWhenAcked(1, "foo", 0, !kFin, &delegate);
+ connection_.SendStreamDataWithStringAndNotifyWhenAcked(1, "foo", 0, !kFin,
+ &delegate);
// Process an ACK from the server which should trigger the callback.
EXPECT_CALL(*send_algorithm_, OnIncomingAck(_, _, _)).Times(1);
@@ -3136,11 +3159,12 @@ TEST_P(QuicConnectionTest, AckNotifierFailToTriggerCallback) {
// Send some data, which will register the delegate to be notified. This will
// not be ACKed and so the delegate should never be called.
- connection_.SendStreamDataAndNotifyWhenAcked(1, "foo", 0, !kFin, &delegate);
+ connection_.SendStreamDataWithStringAndNotifyWhenAcked(1, "foo", 0, !kFin,
+ &delegate);
// Send some other data which we will ACK.
- connection_.SendStreamData(1, "foo", 0, !kFin);
- connection_.SendStreamData(1, "bar", 0, !kFin);
+ connection_.SendStreamDataWithString(1, "foo", 0, !kFin);
+ connection_.SendStreamDataWithString(1, "bar", 0, !kFin);
// Now we receive ACK for packets 2 and 3, but importantly missing packet 1
// which we registered to be notified about.
@@ -3163,10 +3187,11 @@ TEST_P(QuicConnectionTest, AckNotifierCallbackAfterRetransmission) {
EXPECT_CALL(*send_algorithm_, OnIncomingLoss(_)).Times(1);
// Send four packets, and register to be notified on ACK of packet 2.
- connection_.SendStreamData(1, "foo", 0, !kFin);
- connection_.SendStreamDataAndNotifyWhenAcked(1, "bar", 0, !kFin, &delegate);
- connection_.SendStreamData(1, "baz", 0, !kFin);
- connection_.SendStreamData(1, "qux", 0, !kFin);
+ connection_.SendStreamDataWithString(1, "foo", 0, !kFin);
+ connection_.SendStreamDataWithStringAndNotifyWhenAcked(1, "bar", 0, !kFin,
+ &delegate);
+ connection_.SendStreamDataWithString(1, "baz", 0, !kFin);
+ connection_.SendStreamDataWithString(1, "qux", 0, !kFin);
// Now we receive ACK for packets 1, 3, and 4.
QuicAckFrame frame(4, QuicTime::Zero(), 0);
@@ -3201,7 +3226,8 @@ TEST_P(QuicConnectionTest, AckNotifierCallbackAfterFECRecovery) {
EXPECT_CALL(*send_algorithm_, OnIncomingAck(_, _, _)).Times(1);
// Send one packet, and register to be notified on ACK.
- connection_.SendStreamDataAndNotifyWhenAcked(1, "foo", 0, !kFin, &delegate);
+ connection_.SendStreamDataWithStringAndNotifyWhenAcked(1, "foo", 0, !kFin,
+ &delegate);
// Ack packet gets dropped, but we receive an FEC packet that covers it.
// Should recover the Ack packet and trigger the notification callback.
diff --git a/net/quic/quic_framer.cc b/net/quic/quic_framer.cc
index 02ca896..2701f39 100644
--- a/net/quic/quic_framer.cc
+++ b/net/quic/quic_framer.cc
@@ -160,7 +160,7 @@ size_t QuicFramer::GetMinRstStreamFrameSize() {
// static
size_t QuicFramer::GetMinConnectionCloseFrameSize() {
return kQuicFrameTypeSize + kQuicErrorCodeSize + kQuicErrorDetailsLengthSize +
- GetMinAckFrameSize();
+ GetMinAckFrameSize() - 1; // Don't include the frame type again.
}
// static
@@ -237,18 +237,15 @@ bool QuicFramer::IsSupportedVersion(const QuicVersion version) const {
return false;
}
-size_t QuicFramer::GetSerializedFrameLength(
- const QuicFrame& frame, size_t free_bytes, bool first_frame) {
+size_t QuicFramer::GetSerializedFrameLength(const QuicFrame& frame,
+ size_t free_bytes,
+ bool first_frame,
+ bool last_frame) {
if (frame.type == PADDING_FRAME) {
// PADDING implies end of packet.
return free_bytes;
}
- // See if it fits as the non-last frame.
- size_t frame_len = ComputeFrameLength(frame, false);
- // STREAM frames save two bytes when they're the last frame in the packet.
- if (frame_len > free_bytes && frame.type == STREAM_FRAME) {
- frame_len = ComputeFrameLength(frame, true);
- }
+ size_t frame_len = ComputeFrameLength(frame, last_frame);
if (frame_len > free_bytes) {
// Only truncate the first frame in a packet, so if subsequent ones go
// over, stop including more frames.
@@ -282,8 +279,10 @@ SerializedPacket QuicFramer::BuildUnsizedDataPacket(
size_t packet_size = GetPacketHeaderSize(header);
for (size_t i = 0; i < frames.size(); ++i) {
DCHECK_LE(packet_size, max_plaintext_size);
+ bool first_frame = i == 0;
+ bool last_frame = i == frames.size() - 1;
const size_t frame_size = GetSerializedFrameLength(
- frames[i], max_plaintext_size - packet_size, i == 0);
+ frames[i], max_plaintext_size - packet_size, first_frame, last_frame);
DCHECK(frame_size);
packet_size += frame_size;
}
diff --git a/net/quic/quic_framer.h b/net/quic/quic_framer.h
index 94b77b1..e2556df 100644
--- a/net/quic/quic_framer.h
+++ b/net/quic/quic_framer.h
@@ -251,8 +251,10 @@ class NET_EXPORT_PRIVATE QuicFramer {
// Returns the number of bytes added to the packet for the specified frame,
// and 0 if the frame doesn't fit. Includes the header size for the first
// frame.
- size_t GetSerializedFrameLength(
- const QuicFrame& frame, size_t free_bytes, bool first_frame);
+ size_t GetSerializedFrameLength(const QuicFrame& frame,
+ size_t free_bytes,
+ bool first_frame,
+ bool last_frame);
// Returns the associated data from the encrypted packet |encrypted| as a
// stringpiece.
diff --git a/net/quic/quic_packet_creator.cc b/net/quic/quic_packet_creator.cc
index d9ce156..17839af 100644
--- a/net/quic/quic_packet_creator.cc
+++ b/net/quic/quic_packet_creator.cc
@@ -17,6 +17,10 @@ using std::min;
using std::pair;
using std::vector;
+// If true, then QUIC handshake packets will be padded to the maximium packet
+// size.
+bool FLAGS_pad_quic_handshake_packets = false;
+
namespace net {
QuicPacketCreator::QuicPacketCreator(QuicGuid guid,
@@ -132,41 +136,42 @@ size_t QuicPacketCreator::CreateStreamFrame(QuicStreamId id,
framer_->version(), id, offset, true);
}
+ if (data.size() == 0) {
+ DCHECK(fin);
+ // Create a new packet for the fin, if necessary.
+ *frame = QuicFrame(new QuicStreamFrame(id, true, offset, ""));
+ return 0;
+ }
+
const size_t free_bytes = BytesFree();
size_t bytes_consumed = 0;
- if (data.size() != 0) {
- // When a STREAM frame is the last frame in a packet, it consumes two fewer
- // bytes of framing overhead.
- // Anytime more data is available than fits in with the extra two bytes,
- // the frame will be the last, and up to two extra bytes are consumed.
- // TODO(ianswett): If QUIC pads, the 1 byte PADDING frame does not fit when
- // 1 byte is available, because then the STREAM frame isn't the last.
-
- // The minimum frame size(0 bytes of data) if it's not the last frame.
- size_t min_frame_size = QuicFramer::GetMinStreamFrameSize(
- framer_->version(), id, offset, false);
- // Check if it's the last frame in the packet.
- if (data.size() + min_frame_size > free_bytes) {
- // The minimum frame size(0 bytes of data) if it is the last frame.
- size_t min_last_frame_size = QuicFramer::GetMinStreamFrameSize(
- framer_->version(), id, offset, true);
- bytes_consumed =
- min<size_t>(free_bytes - min_last_frame_size, data.size());
- } else {
- DCHECK_LT(data.size(), BytesFree());
- bytes_consumed = data.size();
- }
-
- bool set_fin = fin && bytes_consumed == data.size(); // Last frame.
- StringPiece data_frame(data.data(), bytes_consumed);
- *frame = QuicFrame(new QuicStreamFrame(id, set_fin, offset, data_frame));
+ // When a STREAM frame is the last frame in a packet, it consumes two fewer
+ // bytes of framing overhead.
+ // Anytime more data is available than fits in with the extra two bytes,
+ // the frame will be the last, and up to two extra bytes are consumed.
+ // TODO(ianswett): If QUIC pads, the 1 byte PADDING frame does not fit when
+ // 1 byte is available, because then the STREAM frame isn't the last.
+
+ // The minimum frame size(0 bytes of data) if it's not the last frame.
+ size_t min_frame_size = QuicFramer::GetMinStreamFrameSize(
+ framer_->version(), id, offset, false);
+ // Check if it's the last frame in the packet.
+ if (data.size() + min_frame_size > free_bytes) {
+ // The minimum frame size(0 bytes of data) if it is the last frame.
+ size_t min_last_frame_size = QuicFramer::GetMinStreamFrameSize(
+ framer_->version(), id, offset, true);
+ bytes_consumed =
+ min<size_t>(free_bytes - min_last_frame_size, data.size());
} else {
- DCHECK(fin);
- // Create a new packet for the fin, if necessary.
- *frame = QuicFrame(new QuicStreamFrame(id, true, offset, ""));
+ DCHECK_LT(data.size(), BytesFree());
+ bytes_consumed = data.size();
}
+ bool set_fin = fin && bytes_consumed == data.size(); // Last frame.
+ StringPiece data_frame(data.data(), bytes_consumed);
+ *frame = QuicFrame(new QuicStreamFrame(id, set_fin, offset, data_frame));
+
return bytes_consumed;
}
@@ -238,10 +243,20 @@ bool QuicPacketCreator::HasPendingFrames() {
size_t QuicPacketCreator::BytesFree() const {
const size_t max_plaintext_size =
framer_->GetMaxPlaintextSize(options_.max_packet_length);
- if (PacketSize() >= max_plaintext_size) {
+ DCHECK_GE(max_plaintext_size, PacketSize());
+
+ // If the last frame in the packet is a stream frame, then it can be
+ // two bytes smaller than if it were not the last. So this means that
+ // there are two fewer bytes available to the next frame in this case.
+ bool has_trailing_stream_frame =
+ !queued_frames_.empty() && queued_frames_.back().type == STREAM_FRAME;
+ size_t expanded_packet_size = PacketSize() +
+ (has_trailing_stream_frame ? kQuicStreamPayloadLengthSize : 0);
+
+ if (expanded_packet_size >= max_plaintext_size) {
return 0;
}
- return max_plaintext_size - PacketSize();
+ return max_plaintext_size - expanded_packet_size;
}
size_t QuicPacketCreator::PacketSize() const {
@@ -272,8 +287,28 @@ SerializedPacket QuicPacketCreator::SerializePacket() {
QuicPacketHeader header;
FillPacketHeader(fec_group_number_, false, false, &header);
+ if (FLAGS_pad_quic_handshake_packets) {
+ MaybeAddPadding();
+ }
+
+ size_t max_plaintext_size =
+ framer_->GetMaxPlaintextSize(options_.max_packet_length);
+ DCHECK_GE(max_plaintext_size, packet_size_);
+ // ACK and CONNECTION_CLOSE Frames will only be truncated only if they're
+ // the first frame in the packet. If truncation is to occure, then
+ // GetSerializedFrameLength will have returned all bytes free.
+ bool possibly_truncated =
+ packet_size_ != max_plaintext_size ||
+ queued_frames_.size() != 1 ||
+ (queued_frames_.back().type == ACK_FRAME ||
+ queued_frames_.back().type == CONNECTION_CLOSE_FRAME);
SerializedPacket serialized =
framer_->BuildDataPacket(header, queued_frames_, packet_size_);
+ DCHECK(serialized.packet);
+ // Because of possible truncation, we can't be confident that our
+ // packet size calculation worked correctly.
+ if (!possibly_truncated)
+ DCHECK_EQ(packet_size_, serialized.packet->length());
packet_size_ = 0;
queued_frames_.clear();
@@ -358,14 +393,18 @@ bool QuicPacketCreator::ShouldRetransmit(const QuicFrame& frame) {
bool QuicPacketCreator::AddFrame(const QuicFrame& frame,
bool save_retransmittable_frames) {
size_t frame_len = framer_->GetSerializedFrameLength(
- frame, BytesFree(), queued_frames_.empty());
+ frame, BytesFree(), queued_frames_.empty(), true);
if (frame_len == 0) {
return false;
}
DCHECK_LT(0u, packet_size_);
MaybeStartFEC();
packet_size_ += frame_len;
-
+ // If the last frame in the packet was a stream frame, then once we add the
+ // new frame it's serialization will be two bytes larger.
+ if (!queued_frames_.empty() && queued_frames_.back().type == STREAM_FRAME) {
+ packet_size_ += kQuicStreamPayloadLengthSize;
+ }
if (save_retransmittable_frames && ShouldRetransmit(frame)) {
if (queued_retransmittable_frames_.get() == NULL) {
queued_retransmittable_frames_.reset(new RetransmittableFrames());
@@ -383,4 +422,29 @@ bool QuicPacketCreator::AddFrame(const QuicFrame& frame,
return true;
}
+void QuicPacketCreator::MaybeAddPadding() {
+ if (BytesFree() == 0) {
+ // Don't pad full packets.
+ return;
+ }
+
+ // If any of the frames in the current packet are on the crypto stream
+ // then they contain handshake messagses, and we should pad them.
+ bool is_handshake = false;
+ for (size_t i = 0; i < queued_frames_.size(); ++i) {
+ if (queued_frames_[i].type == STREAM_FRAME &&
+ queued_frames_[i].stream_frame->stream_id == kCryptoStreamId) {
+ is_handshake = true;
+ break;
+ }
+ }
+ if (!is_handshake) {
+ return;
+ }
+
+ QuicPaddingFrame padding;
+ bool success = AddFrame(QuicFrame(&padding), false);
+ DCHECK(success);
+}
+
} // namespace net
diff --git a/net/quic/quic_packet_creator.h b/net/quic/quic_packet_creator.h
index 643c45b..614ebbc 100644
--- a/net/quic/quic_packet_creator.h
+++ b/net/quic/quic_packet_creator.h
@@ -18,6 +18,8 @@
#include "net/quic/quic_framer.h"
#include "net/quic/quic_protocol.h"
+NET_EXPORT_PRIVATE extern bool FLAGS_pad_quic_handshake_packets;
+
namespace net {
namespace test {
class QuicPacketCreatorPeer;
@@ -32,13 +34,11 @@ class NET_EXPORT_PRIVATE QuicPacketCreator : public QuicFecBuilderInterface {
struct Options {
Options()
: max_packet_length(kMaxPacketSize),
- random_reorder(false),
max_packets_per_fec_group(0),
send_guid_length(PACKET_8BYTE_GUID),
send_sequence_number_length(PACKET_1BYTE_SEQUENCE_NUMBER) {}
size_t max_packet_length;
- bool random_reorder; // Inefficient: rewrite if used at scale.
// 0 indicates fec is disabled.
size_t max_packets_per_fec_group;
// Length of guid to send over the wire.
@@ -116,10 +116,16 @@ class NET_EXPORT_PRIVATE QuicPacketCreator : public QuicFecBuilderInterface {
// Returns true if there are frames pending to be serialized.
bool HasPendingFrames();
- // Returns the number of bytes which are free to frames in the current packet.
+ // Returns the number of bytes which are available to be used by additional
+ // frames in the packet. Since stream frames are slightly smaller when they
+ // are the last frame in a packet, this method will return a different
+ // value than max_packet_size - PacketSize(), in this case.
size_t BytesFree() const;
- // Returns the number of bytes in the current packet, including the header.
+ // Returns the number of bytes in the current packet, including the header,
+ // if serialized with the current frames. Adding a frame to the packet
+ // may change the serialized length of existing frames, as per the comment
+ // in BytesFree.
size_t PacketSize() const;
// Adds |frame| to the packet creator's list of frames to be serialized.
@@ -185,6 +191,11 @@ class NET_EXPORT_PRIVATE QuicPacketCreator : public QuicFecBuilderInterface {
// Particularly useful for retransmits using SerializeAllFrames().
bool AddFrame(const QuicFrame& frame, bool save_retransmittable_frames);
+ // Adds a padding frame to the current packet only if the current packet
+ // contains a handshake message, and there is sufficient room to fit a
+ // padding frame.
+ void MaybeAddPadding();
+
Options options_;
QuicGuid guid_;
QuicFramer* framer_;
diff --git a/net/quic/quic_packet_creator_test.cc b/net/quic/quic_packet_creator_test.cc
index c6edf93..4f243ef 100644
--- a/net/quic/quic_packet_creator_test.cc
+++ b/net/quic/quic_packet_creator_test.cc
@@ -27,6 +27,25 @@ namespace net {
namespace test {
namespace {
+template<typename SaveType>
+class ValueRestore {
+ public:
+ ValueRestore(SaveType* name, SaveType value)
+ : name_(name),
+ value_(*name) {
+ *name_ = value;
+ }
+ ~ValueRestore() {
+ *name_ = value_;
+ }
+
+ private:
+ SaveType* name_;
+ SaveType value_;
+
+ DISALLOW_COPY_AND_ASSIGN(ValueRestore);
+};
+
class QuicPacketCreatorTest : public ::testing::TestWithParam<bool> {
protected:
QuicPacketCreatorTest()
@@ -49,8 +68,11 @@ class QuicPacketCreatorTest : public ::testing::TestWithParam<bool> {
server_framer_.ProcessPacket(*encrypted);
}
- void CheckStreamFrame(const QuicFrame& frame, QuicStreamId stream_id,
- const string& data, QuicStreamOffset offset, bool fin) {
+ void CheckStreamFrame(const QuicFrame& frame,
+ QuicStreamId stream_id,
+ const string& data,
+ QuicStreamOffset offset,
+ bool fin) {
EXPECT_EQ(STREAM_FRAME, frame.type);
ASSERT_TRUE(frame.stream_frame);
EXPECT_EQ(stream_id, frame.stream_frame->stream_id);
@@ -59,6 +81,33 @@ class QuicPacketCreatorTest : public ::testing::TestWithParam<bool> {
EXPECT_EQ(fin, frame.stream_frame->fin);
}
+ // Returns the number of bytes consumed by the header of packet, including
+ // the version, that is not in an FEC group.
+ size_t GetPacketHeaderOverhead() {
+ return GetPacketHeaderSize(creator_.options()->send_guid_length,
+ kIncludeVersion,
+ creator_.options()->send_sequence_number_length,
+ NOT_IN_FEC_GROUP);
+ }
+
+ // Returns the number of bytes of overhead that will be added to a packet
+ // of maximum length.
+ size_t GetEncryptionOverhead() {
+ return creator_.options()->max_packet_length -
+ client_framer_.GetMaxPlaintextSize(
+ creator_.options()->max_packet_length);
+ }
+
+ // Returns the number of bytes consumed by the non-data fields of a stream
+ // frame, assuming it is the last frame in the packet
+ size_t GetStreamFrameOverhead() {
+ return QuicFramer::GetMinStreamFrameSize(
+ client_framer_.version(), kStreamId, kOffset, true);
+ }
+
+ static const QuicStreamId kStreamId = 1u;
+ static const QuicStreamOffset kOffset = 1u;
+
QuicFrames frames_;
QuicFramer server_framer_;
QuicFramer client_framer_;
@@ -246,6 +295,7 @@ TEST_F(QuicPacketCreatorTest, ReserializeFramesWithSequenceNumberLength) {
TEST_F(QuicPacketCreatorTest, SerializeConnectionClose) {
QuicConnectionCloseFrame frame;
frame.error_code = QUIC_NO_ERROR;
+ frame.error_details = "error";
frame.ack_frame = QuicAckFrame(0u, QuicTime::Zero(), 0u);
SerializedPacket serialized = creator_.SerializeConnectionClose(&frame);
@@ -288,18 +338,10 @@ TEST_F(QuicPacketCreatorTest, CreateStreamFrameFinOnly) {
}
TEST_F(QuicPacketCreatorTest, CreateAllFreeBytesForStreamFrames) {
- QuicStreamId kStreamId = 1u;
- QuicStreamOffset kOffset = 1u;
- for (int i = 0; i < 100; ++i) {
+ const size_t overhead = GetPacketHeaderOverhead() + GetEncryptionOverhead();
+ for (size_t i = overhead; i < overhead + 100; ++i) {
creator_.options()->max_packet_length = i;
- const size_t max_plaintext_size = client_framer_.GetMaxPlaintextSize(i);
- const bool should_have_room = max_plaintext_size >
- (QuicFramer::GetMinStreamFrameSize(
- client_framer_.version(), kStreamId, kOffset, true) +
- GetPacketHeaderSize(creator_.options()->send_guid_length,
- kIncludeVersion,
- creator_.options()->send_sequence_number_length,
- NOT_IN_FEC_GROUP));
+ const bool should_have_room = i > overhead + GetStreamFrameOverhead();
ASSERT_EQ(should_have_room,
creator_.HasRoomForStreamFrame(kStreamId, kOffset));
if (should_have_room) {
@@ -316,6 +358,100 @@ TEST_F(QuicPacketCreatorTest, CreateAllFreeBytesForStreamFrames) {
}
}
+TEST_F(QuicPacketCreatorTest, StreamFrameConsumption) {
+ // Compute the total overhead for a single frame in packet.
+ const size_t overhead = GetPacketHeaderOverhead() + GetEncryptionOverhead()
+ + GetStreamFrameOverhead();
+ size_t capacity = kMaxPacketSize - overhead;
+ // Now, test various sizes around this size.
+ for (int delta = -5; delta <= 5; ++delta) {
+ string data(capacity + delta, 'A');
+ size_t bytes_free = delta > 0 ? 0 : 0 - delta;
+ QuicFrame frame;
+ size_t bytes_consumed = creator_.CreateStreamFrame(
+ kStreamId, data, kOffset, false, &frame);
+ EXPECT_EQ(capacity - bytes_free, bytes_consumed);
+
+ ASSERT_TRUE(creator_.AddSavedFrame(frame));
+ // BytesFree() returns bytes available for the next frame, which will
+ // be two bytes smaller since the stream frame would need to be grown.
+ size_t expected_bytes_free = bytes_free < 3 ? 0 : bytes_free - 2;
+ EXPECT_EQ(expected_bytes_free, creator_.BytesFree()) << "delta: " << delta;
+ SerializedPacket serialized_packet = creator_.SerializePacket();
+ ASSERT_TRUE(serialized_packet.packet);
+ delete serialized_packet.packet;
+ delete serialized_packet.retransmittable_frames;
+ }
+}
+
+TEST_F(QuicPacketCreatorTest, CryptoStreamFramePacketPadding) {
+ ValueRestore<bool> old_flag(&FLAGS_pad_quic_handshake_packets, true);
+
+ // Compute the total overhead for a single frame in packet.
+ const size_t overhead = GetPacketHeaderOverhead() + GetEncryptionOverhead()
+ + GetStreamFrameOverhead();
+ ASSERT_GT(kMaxPacketSize, overhead);
+ size_t capacity = kMaxPacketSize - overhead;
+ // Now, test various sizes around this size.
+ for (int delta = -5; delta <= 5; ++delta) {
+ string data(capacity + delta, 'A');
+ size_t bytes_free = delta > 0 ? 0 : 0 - delta;
+
+ QuicFrame frame;
+ size_t bytes_consumed = creator_.CreateStreamFrame(
+ kStreamId, data, kOffset, false, &frame);
+ EXPECT_LT(0u, bytes_consumed);
+ ASSERT_TRUE(creator_.AddSavedFrame(frame));
+ SerializedPacket serialized_packet = creator_.SerializePacket();
+ ASSERT_TRUE(serialized_packet.packet);
+ // If there is not enough space in the packet to fit a padding frame
+ // (1 byte) and to expand the stream frame (another 2 bytes) the packet
+ // will not be padded.
+ if (bytes_free < 3) {
+ EXPECT_EQ(client_framer_.GetMaxPlaintextSize(kMaxPacketSize) - bytes_free,
+ serialized_packet.packet->length());
+ } else {
+ EXPECT_EQ(client_framer_.GetMaxPlaintextSize(kMaxPacketSize),
+ serialized_packet.packet->length());
+ }
+ delete serialized_packet.packet;
+ delete serialized_packet.retransmittable_frames;
+ }
+}
+
+
+TEST_F(QuicPacketCreatorTest, NonCryptoStreamFramePacketNonPadding) {
+ ValueRestore<bool> old_flag(&FLAGS_pad_quic_handshake_packets, true);
+
+ // Compute the total overhead for a single frame in packet.
+ const size_t overhead = GetPacketHeaderOverhead() + GetEncryptionOverhead()
+ + GetStreamFrameOverhead();
+ ASSERT_GT(kMaxPacketSize, overhead);
+ size_t capacity = kMaxPacketSize - overhead;
+ // Now, test various sizes around this size.
+ for (int delta = -5; delta <= 5; ++delta) {
+ string data(capacity + delta, 'A');
+ size_t bytes_free = delta > 0 ? 0 : 0 - delta;
+
+ QuicFrame frame;
+ size_t bytes_consumed = creator_.CreateStreamFrame(
+ kStreamId + 2, data, kOffset, false, &frame);
+ EXPECT_LT(0u, bytes_consumed);
+ ASSERT_TRUE(creator_.AddSavedFrame(frame));
+ SerializedPacket serialized_packet = creator_.SerializePacket();
+ ASSERT_TRUE(serialized_packet.packet);
+ if (bytes_free > 0) {
+ EXPECT_EQ(client_framer_.GetMaxPlaintextSize(kMaxPacketSize) - bytes_free,
+ serialized_packet.packet->length());
+ } else {
+ EXPECT_EQ(client_framer_.GetMaxPlaintextSize(kMaxPacketSize),
+ serialized_packet.packet->length());
+ }
+ delete serialized_packet.packet;
+ delete serialized_packet.retransmittable_frames;
+ }
+}
+
TEST_F(QuicPacketCreatorTest, SerializeVersionNegotiationPacket) {
QuicPacketCreatorPeer::SetIsServer(&creator_, true);
QuicVersionVector versions;
diff --git a/net/quic/quic_packet_writer.h b/net/quic/quic_packet_writer.h
index 2e8fcf6..5df30c8 100644
--- a/net/quic/quic_packet_writer.h
+++ b/net/quic/quic_packet_writer.h
@@ -21,9 +21,9 @@ class NET_EXPORT_PRIVATE QuicPacketWriter {
virtual ~QuicPacketWriter() {}
// Sends the packet out to the peer. If the write succeeded, the result's
- // status is WRITE_OK and bytes_written is populated. If the write failed,
- // the result's status is WRITE_BLOCKED or WRITE_ERROR and error_code is
- // populated.
+ // status is WRITE_STATUS_OK and bytes_written is populated. If the write
+ // failed, the result's status is WRITE_STATUS_BLOCKED or WRITE_STATUS_ERROR
+ // and error_code is populated.
virtual WriteResult WritePacket(
const char* buffer, size_t buf_len,
const net::IPAddressNumber& self_address,
diff --git a/net/quic/quic_session.cc b/net/quic/quic_session.cc
index 4a487d2..77494b5 100644
--- a/net/quic/quic_session.cc
+++ b/net/quic/quic_session.cc
@@ -242,7 +242,9 @@ QuicConsumedData QuicSession::WritevData(QuicStreamId id,
int iov_count,
QuicStreamOffset offset,
bool fin) {
- return connection_->SendvStreamData(id, iov, iov_count, offset, fin);
+ IOVector data;
+ data.AppendIovec(iov, iov_count);
+ return connection_->SendStreamData(id, data, offset, fin);
}
void QuicSession::SendRstStream(QuicStreamId id,
diff --git a/net/quic/test_tools/quic_test_utils.cc b/net/quic/test_tools/quic_test_utils.cc
index 8b594c2..d2bc7e9 100644
--- a/net/quic/test_tools/quic_test_utils.cc
+++ b/net/quic/test_tools/quic_test_utils.cc
@@ -109,6 +109,7 @@ FramerVisitorCapturingFrames::FramerVisitorCapturingFrames() : frame_count_(0) {
}
FramerVisitorCapturingFrames::~FramerVisitorCapturingFrames() {
+ STLDeleteElements(&stream_data_);
}
bool FramerVisitorCapturingFrames::OnPacketHeader(
@@ -119,9 +120,12 @@ bool FramerVisitorCapturingFrames::OnPacketHeader(
}
bool FramerVisitorCapturingFrames::OnStreamFrame(const QuicStreamFrame& frame) {
- // TODO(ianswett): Own the underlying string, so it will not exist outside
- // this callback.
- stream_frames_.push_back(frame);
+ // Make a copy of the frame and store a copy of underlying string, since
+ // frame.data may not exist outside this callback.
+ stream_data_.push_back(new string(frame.data.as_string()));
+ QuicStreamFrame frame_copy = frame;
+ stream_frames_.push_back(frame_copy);
+ stream_frames_.back().data = *(stream_data_.back());
++frame_count_;
return true;
}
@@ -252,6 +256,7 @@ bool PacketSavingConnection::SendOrQueuePacket(
QuicPacketEntropyHash /* entropy_hash */,
TransmissionType /* transmission_type */,
HasRetransmittableData /* retransmittable */,
+ IsHandshake /* handshake */,
Force /* forced */) {
packets_.push_back(packet);
QuicEncryptedPacket* encrypted =
diff --git a/net/quic/test_tools/quic_test_utils.h b/net/quic/test_tools/quic_test_utils.h
index 31b6c03..2992125 100644
--- a/net/quic/test_tools/quic_test_utils.h
+++ b/net/quic/test_tools/quic_test_utils.h
@@ -147,6 +147,9 @@ class FramerVisitorCapturingFrames : public NoOpFramerVisitor {
const std::vector<QuicStreamFrame>* stream_frames() const {
return &stream_frames_;
}
+ const std::vector<string*>& stream_data() const {
+ return stream_data_;
+ }
QuicAckFrame* ack() { return ack_.get(); }
QuicCongestionFeedbackFrame* feedback() { return feedback_.get(); }
QuicRstStreamFrame* rst() { return rst_.get(); }
@@ -160,6 +163,7 @@ class FramerVisitorCapturingFrames : public NoOpFramerVisitor {
size_t frame_count_;
QuicPacketHeader header_;
std::vector<QuicStreamFrame> stream_frames_;
+ std::vector<std::string*> stream_data_;
scoped_ptr<QuicAckFrame> ack_;
scoped_ptr<QuicCongestionFeedbackFrame> feedback_;
scoped_ptr<QuicRstStreamFrame> rst_;
@@ -263,6 +267,7 @@ class PacketSavingConnection : public MockConnection {
QuicPacketEntropyHash entropy_hash,
TransmissionType transmission_type,
HasRetransmittableData has_retransmittable_data,
+ IsHandshake handshake,
Force forced) OVERRIDE;
std::vector<QuicPacket*> packets_;