Commit 97cf302c authored by rtenneti@chromium.org's avatar rtenneti@chromium.org

Land Recent QUIC changes.

This is the first part of the code to allow faster stats/future
compression stuff to register for a notification when a block of data
has been fully ACKed by the peer.

The idea is as follows:

Someone who wants to be notified when their data is ACKed calls
SendStreamDataAndNotifyWhenAcked instead of SendStreamData, and provides
a Closure.

The QuicConnection stores the Closure in a QuicAckNotifier, and
maintains a vector of these.

On every ACK frame received, the OnAck(acked_seqnums) method of each
QuicAckNotifier is called. The QuicAckNotifier keeps track of the
sequence numbers it is waiting to see, and after being notified of all
of them it calls the Closure's Run() method.

Merge internal change: 51476134

Changed SourceAddressToken's code not to include port number while
performing crypto handshake (found in EndToEnd unit tests).

Use IPAddressToPackedString for source address token comparison.


Add CryptoServerConfig::set_strike_register_no_startup_period() to allow
a QuicServer to start accepting 0-RTT handshakes without waiting
a startup period.

Add an end-to-end test for a successful 0-RTT handshake.

Merge internal change: 51419595

Copying the overly-lenient SPDY workarounds to handling priority blocked
streams for idle timeout logic.

Merge internal change: 51406984

For this CL, the only chromium side change is to add
QuicConnectionPeer::SetPeerAddress.

Log packet retransmissions to DLOG(INFO). Use the standard format for
the log messages for crypto handshake messages on the client side.

Merge internal change: 51336227

New frame ID scheme to eliminate conflict between STREAM and PADDING
frames. PADDING frames are now stream type 0. Description of the new
scheme is in quic_framer.cc.

Merge internal change: 51271708

Fixing a bug in implicitly created streams which results in early packet
loss causing all streams to hang.

Merge internal change: 51248632

R=rch@chromium.org

Review URL: https://chromiumcodereview.appspot.com/23464033

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@221419 0039d316-1c4b-4281-b951-d872f2087c98
parent 58f2878c
......@@ -1606,6 +1606,11 @@ std::string IPAddressToStringWithPort(const IPAddressNumber& addr,
return IPAddressToStringWithPort(&addr.front(), addr.size(), port);
}
std::string IPAddressToPackedString(const IPAddressNumber& addr) {
return std::string(reinterpret_cast<const char *>(&addr.front()),
addr.size());
}
std::string GetHostName() {
#if defined(OS_WIN)
EnsureWinsockInit();
......
......@@ -166,6 +166,9 @@ NET_EXPORT std::string IPAddressToString(const IPAddressNumber& addr);
NET_EXPORT std::string IPAddressToStringWithPort(
const IPAddressNumber& addr, uint16 port);
// Returns the address as a sequence of bytes in network-byte-order.
NET_EXPORT std::string IPAddressToPackedString(const IPAddressNumber& addr);
// Returns the hostname of the current system. Returns empty string on failure.
NET_EXPORT std::string GetHostName();
......
......@@ -784,6 +784,8 @@
'quic/crypto/strike_register.h',
'quic/crypto/source_address_token.cc',
'quic/crypto/source_address_token.h',
'quic/quic_ack_notifier.cc',
'quic/quic_ack_notifier.h',
'quic/quic_alarm.cc',
'quic/quic_alarm.h',
'quic/quic_bandwidth.cc',
......@@ -1747,6 +1749,7 @@
'quic/test_tools/simple_quic_framer.h',
'quic/test_tools/test_task_runner.cc',
'quic/test_tools/test_task_runner.h',
'quic/quic_ack_notifier_test.cc',
'quic/quic_alarm_test.cc',
'quic/quic_bandwidth_test.cc',
'quic/quic_client_session_test.cc',
......
......@@ -11,6 +11,7 @@
#include "base/strings/string_number_conversions.h"
#include "crypto/hkdf.h"
#include "crypto/secure_hash.h"
#include "net/base/net_util.h"
#include "net/quic/crypto/aes_128_gcm_12_decrypter.h"
#include "net/quic/crypto/aes_128_gcm_12_encrypter.h"
#include "net/quic/crypto/cert_compressor.h"
......@@ -56,6 +57,7 @@ QuicCryptoServerConfig::QuicCryptoServerConfig(
next_config_promotion_time_(QuicWallTime::Zero()),
strike_register_lock_(),
server_nonce_strike_register_lock_(),
strike_register_no_startup_period_(false),
strike_register_max_entries_(1 << 10),
strike_register_window_secs_(600),
source_address_token_future_secs_(3600),
......@@ -636,6 +638,8 @@ QuicErrorCode QuicCryptoServerConfig::EvaluateClientHello(
static_cast<uint32>(info->now.ToUNIXSeconds()),
strike_register_window_secs_,
orbit,
strike_register_no_startup_period_ ?
StrikeRegister::NO_STARTUP_PERIOD_NEEDED :
StrikeRegister::DENY_REQUESTS_AT_STARTUP));
}
......@@ -908,6 +912,12 @@ void QuicCryptoServerConfig::set_replay_protection(bool on) {
replay_protection_ = on;
}
void QuicCryptoServerConfig::set_strike_register_no_startup_period() {
base::AutoLock auto_lock(strike_register_lock_);
DCHECK(!strike_register_.get());
strike_register_no_startup_period_ = true;
}
void QuicCryptoServerConfig::set_strike_register_max_entries(
uint32 max_entries) {
base::AutoLock locker(strike_register_lock_);
......@@ -949,7 +959,7 @@ string QuicCryptoServerConfig::NewSourceAddressToken(
QuicRandom* rand,
QuicWallTime now) const {
SourceAddressToken source_address_token;
source_address_token.set_ip(ip.ToString());
source_address_token.set_ip(IPAddressToPackedString(ip.address()));
source_address_token.set_timestamp(now.ToUNIXSeconds());
return source_address_token_boxer_.Box(
......@@ -972,7 +982,7 @@ bool QuicCryptoServerConfig::ValidateSourceAddressToken(
return false;
}
if (source_address_token.ip() != ip.ToString()) {
if (source_address_token.ip() != IPAddressToPackedString(ip.address())) {
// It's for a different IP address.
return false;
}
......
......@@ -155,6 +155,10 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig {
// request to be processed twice.
void set_replay_protection(bool on);
// set_strike_register_no_startup_period configures the strike register to
// not have a startup period.
void set_strike_register_no_startup_period();
// set_strike_register_max_entries sets the maximum number of entries that
// the internal strike register will hold. If the strike register fills up
// then the oldest entries (by the client's clock) will be dropped.
......@@ -351,6 +355,7 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig {
// These fields store configuration values. See the comments for their
// respective setter functions.
bool strike_register_no_startup_period_;
uint32 strike_register_max_entries_;
uint32 strike_register_window_secs_;
uint32 source_address_token_future_secs_;
......
......@@ -21,24 +21,36 @@ SourceAddressToken::~SourceAddressToken() {
}
string SourceAddressToken::SerializeAsString() const {
return ip_ + " " + base::Int64ToString(timestamp_);
string out;
out.push_back(ip_.size());
out.append(ip_);
string time_str = base::Int64ToString(timestamp_);
out.push_back(time_str.size());
out.append(time_str);
return out;
}
bool SourceAddressToken::ParseFromArray(const char* plaintext,
size_t plaintext_length) {
string data(plaintext, plaintext_length);
vector<string> results;
base::SplitString(data, ' ', &results);
if (results.size() < 2) {
if (plaintext_length == 0) {
return false;
}
size_t ip_len = plaintext[0];
if (plaintext_length <= 1 + ip_len) {
return false;
}
size_t time_len = plaintext[1 + ip_len];
if (plaintext_length != 1 + ip_len + 1 + time_len) {
return false;
}
string time_str(&plaintext[1 + ip_len + 1], time_len);
int64 timestamp;
if (!base::StringToInt64(results[1], &timestamp)) {
if (!base::StringToInt64(time_str, &timestamp)) {
return false;
}
ip_ = results[0];
ip_.assign(&plaintext[1], ip_len);
timestamp_ = timestamp;
return true;
}
......
// 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/quic_ack_notifier.h"
namespace net {
QuicAckNotifier::DelegateInterface::DelegateInterface() {}
QuicAckNotifier::DelegateInterface::~DelegateInterface() {}
QuicAckNotifier::QuicAckNotifier(DelegateInterface* delegate)
: delegate_(delegate) {
DCHECK(delegate_);
}
QuicAckNotifier::~QuicAckNotifier() {}
void QuicAckNotifier::AddSequenceNumber(
const QuicPacketSequenceNumber& sequence_number) {
sequence_numbers_.insert(sequence_number);
}
void QuicAckNotifier::AddSequenceNumbers(
const SequenceNumberSet& sequence_numbers) {
for (SequenceNumberSet::const_iterator it = sequence_numbers.begin();
it != sequence_numbers.end(); ++it) {
AddSequenceNumber(*it);
}
}
bool QuicAckNotifier::OnAck(SequenceNumberSet sequence_numbers) {
// If the set of sequence numbers we are tracking is empty then this
// QuicAckNotifier should have already been deleted.
DCHECK(!sequence_numbers_.empty());
for (SequenceNumberSet::iterator it = sequence_numbers.begin();
it != sequence_numbers.end(); ++it) {
sequence_numbers_.erase(*it);
if (sequence_numbers_.empty()) {
delegate_->OnAckNotification();
return true;
}
}
return false;
}
void QuicAckNotifier::UpdateSequenceNumber(
QuicPacketSequenceNumber old_sequence_number,
QuicPacketSequenceNumber new_sequence_number) {
sequence_numbers_.erase(old_sequence_number);
sequence_numbers_.insert(new_sequence_number);
}
}; // namespace net
// 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_QUIC_ACK_NOTIFIER_H_
#define NET_QUIC_QUIC_ACK_NOTIFIER_H_
#include "base/callback.h"
#include "net/quic/quic_protocol.h"
namespace net {
// Used to register with a QuicConnection for notification once a set of packets
// have all been ACKed.
// The connection informs this class of newly ACKed sequence numbers, and once
// we have seen ACKs for all the sequence numbers we are interested in, we
// trigger a call to a provided Closure.
class NET_EXPORT_PRIVATE QuicAckNotifier {
public:
class NET_EXPORT_PRIVATE DelegateInterface {
public:
DelegateInterface();
virtual ~DelegateInterface();
virtual void OnAckNotification() = 0;
};
explicit QuicAckNotifier(DelegateInterface* delegate);
virtual ~QuicAckNotifier();
// Register a sequence number that this AckNotifier should be interested in.
void AddSequenceNumber(const QuicPacketSequenceNumber& sequence_number);
// Register a set of sequence numbers that this AckNotifier should be
// interested in.
void AddSequenceNumbers(const SequenceNumberSet& sequence_numbers);
// Called by the QuicConnection on receipt of new ACK frames with a list of
// ACKed sequence numbers.
// Deletes any matching sequence numbers from the set of sequence numbers
// being tracked. If this set is now empty, call the stored delegate's
// OnAckNotification method.
//
// Returns true if the provided sequence_numbers caused the delegate to be
// called, false otherwise.
bool OnAck(SequenceNumberSet sequence_numbers);
// If a packet is retransmitted by the connection it will be sent with a
// different sequence number. Updates our internal set of sequence_numbers to
// track the latest number.
void UpdateSequenceNumber(QuicPacketSequenceNumber old_sequence_number,
QuicPacketSequenceNumber new_sequence_number);
private:
// The delegate's OnAckNotification() method will be called once we have been
// notified of ACKs for all the sequence numbers we are tracking.
// This is not owned by OnAckNotifier and must outlive it.
DelegateInterface* delegate_;
// Set of sequence numbers this notifier is waiting to hear about. The
// delegate will not be called until this is an empty set.
SequenceNumberSet sequence_numbers_;
};
}; // namespace net
#endif // NET_QUIC_QUIC_ACK_NOTIFIER_H_
// 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/quic_ack_notifier.h"
#include "net/quic/test_tools/quic_test_utils.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace net {
namespace test {
namespace {
class QuicAckNotifierTest : public ::testing::Test {
protected:
virtual void SetUp() {
notifier_.reset(new QuicAckNotifier(&delegate_));
sequence_numbers_.insert(26);
sequence_numbers_.insert(99);
sequence_numbers_.insert(1234);
notifier_->AddSequenceNumbers(sequence_numbers_);
}
SequenceNumberSet sequence_numbers_;
MockAckNotifierDelegate delegate_;
scoped_ptr<QuicAckNotifier> notifier_;
};
// Should trigger callback when we receive acks for all the registered seqnums.
TEST_F(QuicAckNotifierTest, TriggerCallback) {
EXPECT_CALL(delegate_, OnAckNotification()).Times(1);
EXPECT_TRUE(notifier_->OnAck(sequence_numbers_));
}
// Should trigger callback when we receive acks for all the registered seqnums,
// even though they are interspersed with other seqnums.
TEST_F(QuicAckNotifierTest, TriggerCallbackInterspersed) {
sequence_numbers_.insert(3);
sequence_numbers_.insert(55);
sequence_numbers_.insert(805);
EXPECT_CALL(delegate_, OnAckNotification()).Times(1);
EXPECT_TRUE(notifier_->OnAck(sequence_numbers_));
}
// Should trigger callback when we receive acks for all the registered seqnums,
// even though they are split over multiple calls to OnAck.
TEST_F(QuicAckNotifierTest, TriggerCallbackMultipleCalls) {
SequenceNumberSet seqnums;
seqnums.insert(26);
EXPECT_CALL(delegate_, OnAckNotification()).Times(0);
EXPECT_FALSE(notifier_->OnAck(seqnums));
seqnums.clear();
seqnums.insert(55);
seqnums.insert(9001);
seqnums.insert(99);
EXPECT_CALL(delegate_, OnAckNotification()).Times(0);
EXPECT_FALSE(notifier_->OnAck(seqnums));
seqnums.clear();
seqnums.insert(1234);
EXPECT_CALL(delegate_, OnAckNotification()).Times(1);
EXPECT_TRUE(notifier_->OnAck(seqnums));
}
// Should not trigger callback if we never provide all the seqnums.
TEST_F(QuicAckNotifierTest, DoesNotTrigger) {
SequenceNumberSet different_seqnums;
different_seqnums.insert(14);
different_seqnums.insert(15);
different_seqnums.insert(16);
// Should not trigger callback as not all packets have been seen.
EXPECT_CALL(delegate_, OnAckNotification()).Times(0);
EXPECT_FALSE(notifier_->OnAck(different_seqnums));
}
// Should trigger even after updating sequence numbers and receiving ACKs for
// new sequeunce numbers.
TEST_F(QuicAckNotifierTest, UpdateSeqNums) {
// Uninteresting sequeunce numbers shouldn't trigger callback.
SequenceNumberSet seqnums;
seqnums.insert(6);
seqnums.insert(7);
seqnums.insert(2000);
EXPECT_CALL(delegate_, OnAckNotification()).Times(0);
EXPECT_FALSE(notifier_->OnAck(seqnums));
// Update a couple of the sequence numbers (i.e. retransmitted packets)
notifier_->UpdateSequenceNumber(99, 3000);
notifier_->UpdateSequenceNumber(1234, 3001);
seqnums.clear();
seqnums.insert(26); // original, unchanged
seqnums.insert(3000); // updated
seqnums.insert(3001); // updated
EXPECT_CALL(delegate_, OnAckNotification()).Times(1);
EXPECT_TRUE(notifier_->OnAck(seqnums));
}
} // namespace
} // namespace test
} // namespace net
......@@ -230,6 +230,10 @@ int QuicClientSession::CryptoConnect(bool require_confirmation,
return ERR_IO_PENDING;
}
int QuicClientSession::GetNumSentClientHellos() const {
return crypto_stream_->num_sent_client_hellos();
}
ReliableQuicStream* QuicClientSession::CreateIncomingReliableStream(
QuicStreamId id) {
DLOG(ERROR) << "Server push not supported";
......
......@@ -132,6 +132,11 @@ class NET_EXPORT_PRIVATE QuicClientSession : public QuicSession {
base::WeakPtr<QuicClientSession> GetWeakPtr();
// Returns the number of client hello messages that have been sent on the
// crypto stream. If the handshake has completed then this is one greater
// than the number of round-trips needed for the handshake.
int GetNumSentClientHellos() const;
protected:
// QuicSession methods:
virtual ReliableQuicStream* CreateIncomingReliableStream(
......
......@@ -190,6 +190,7 @@ QuicConnection::QuicConnection(QuicGuid guid,
}
QuicConnection::~QuicConnection() {
STLDeleteElements(&ack_notifiers_);
STLDeleteElements(&undecryptable_packets_);
STLDeleteValues(&unacked_packets_);
STLDeleteValues(&group_map_);
......@@ -456,6 +457,23 @@ void QuicConnection::ProcessAckFrame(const QuicAckFrame& incoming_ack) {
HandleAckForSentFecPackets(incoming_ack, &acked_packets);
if (acked_packets.size() > 0) {
visitor_->OnAck(acked_packets);
// Inform all the registered AckNotifiers of the new ACKs.
// TODO(rjshade): Make this more efficient by maintaining a mapping of
// <sequence number, set<AckNotifierList>> so that OnAck
// is only called on AckNotifiers that care about the
// packets being ACKed.
AckNotifierList::iterator it = ack_notifiers_.begin();
while (it != ack_notifiers_.end()) {
if ((*it)->OnAck(acked_packets)) {
// The QuicAckNotifier has seen all the ACKs it was interested in, and
// has triggered its callback. No more use for it.
delete *it;
it = ack_notifiers_.erase(it);
} else {
++it;
}
}
}
congestion_manager_.OnIncomingAckFrame(incoming_ack,
time_of_last_received_packet_);
......@@ -821,6 +839,30 @@ QuicConsumedData QuicConnection::SendStreamData(QuicStreamId id,
return consumed_data;
}
QuicConsumedData QuicConnection::SendStreamDataAndNotifyWhenAcked(
QuicStreamId id,
StringPiece data,
QuicStreamOffset offset,
bool fin,
QuicAckNotifier::DelegateInterface* delegate) {
// This notifier will be deleted in ProcessAckFrame once it has seen ACKs for
// all the consumed data (or below if no data was consumed).
QuicAckNotifier* notifier = new QuicAckNotifier(delegate);
QuicConsumedData consumed_data =
packet_generator_.ConsumeData(id, data, offset, fin, notifier);
if (consumed_data.bytes_consumed > 0) {
// If some data was consumed, then the delegate should be registered for
// notification when the data is ACKed.
ack_notifiers_.push_back(notifier);
} else {
// No data was consumed, delete the notifier.
delete notifier;
}
return consumed_data;
}
void QuicConnection::SendRstStream(QuicStreamId id,
QuicRstStreamErrorCode error) {
LOG(INFO) << "Sending RST_STREAM: " << id << " code: " << error;
......@@ -1062,14 +1104,23 @@ void QuicConnection::RetransmitPacket(
// Remove info with old sequence number.
unacked_packets_.erase(unacked_it);
retransmission_map_.erase(retransmission_it);
DVLOG(1) << ENDPOINT << "Retransmitting unacked packet " << sequence_number
<< " as " << serialized_packet.sequence_number;
DLOG(INFO) << ENDPOINT << "Retransmitting unacked packet " << sequence_number
<< " as " << serialized_packet.sequence_number;
DCHECK(unacked_packets_.empty() ||
unacked_packets_.rbegin()->first < serialized_packet.sequence_number);
unacked_packets_.insert(make_pair(serialized_packet.sequence_number,
unacked));
retransmission_map_.insert(make_pair(serialized_packet.sequence_number,
retransmission_info));
// A notifier may be waiting to hear about ACKs for the original sequence
// number. Inform them that the sequence number has changed.
for (AckNotifierList::iterator notifier_it = ack_notifiers_.begin();
notifier_it != ack_notifiers_.end(); ++notifier_it) {
(*notifier_it)->UpdateSequenceNumber(sequence_number,
serialized_packet.sequence_number);
}
if (debug_visitor_) {
debug_visitor_->OnPacketRetransmitted(sequence_number,
serialized_packet.sequence_number);
......
......@@ -27,6 +27,7 @@
#include "net/base/ip_endpoint.h"
#include "net/base/linked_hash_map.h"
#include "net/quic/congestion_control/quic_congestion_manager.h"
#include "net/quic/quic_ack_notifier.h"
#include "net/quic/quic_alarm.h"
#include "net/quic/quic_blocked_writer_interface.h"
#include "net/quic/quic_connection_stats.h"
......@@ -213,6 +214,16 @@ class NET_EXPORT_PRIVATE QuicConnection
base::StringPiece data,
QuicStreamOffset offset,
bool fin);
// Same as above, except that 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 SendStreamDataAndNotifyWhenAcked(
QuicStreamId id,
base::StringPiece data,
QuicStreamOffset offset,
bool fin,
QuicAckNotifier::DelegateInterface* delegate);
// Send a stream reset frame to the peer.
virtual void SendRstStream(QuicStreamId id,
QuicRstStreamErrorCode error);
......@@ -504,6 +515,7 @@ class NET_EXPORT_PRIVATE QuicConnection
std::vector<RetransmissionTime>,
RetransmissionTimeComparator>
RetransmissionTimeouts;
typedef std::list<QuicAckNotifier*> AckNotifierList;
// Sends a version negotiation packet to the peer.
void SendVersionNegotiationPacket();
......@@ -701,6 +713,12 @@ class NET_EXPORT_PRIVATE QuicConnection
// This is checked later on validating a data or version negotiation packet.
bool address_migrating_;
// On every ACK frame received by this connection, all the ack_notifiers_ will
// be told which sequeunce numbers were ACKed.
// Once a given QuicAckNotifier has seen all the sequence numbers it is
// interested in, it will be deleted, and removed from this list.
AckNotifierList ack_notifiers_;
DISALLOW_COPY_AND_ASSIGN(QuicConnection);
};
......
This diff is collapsed.
......@@ -152,7 +152,7 @@ void QuicCryptoClientStream::DoHandshakeLoop(
crypto_config_->LookupOrCreate(server_hostname_);
if (in != NULL) {
DVLOG(1) << "Client received: " << in->DebugString();
DVLOG(1) << "Client: Received " << in->DebugString();
}
for (;;) {
......@@ -172,7 +172,7 @@ void QuicCryptoClientStream::DoHandshakeLoop(
crypto_config_->FillInchoateClientHello(
server_hostname_, cached, &crypto_negotiated_params_, &out);
next_state_ = STATE_RECV_REJ;
DVLOG(1) << "Client Sending: " << out.DebugString();
DVLOG(1) << "Client: Sending " << out.DebugString();
SendHandshakeMessage(out);
return;
}
......@@ -200,7 +200,7 @@ void QuicCryptoClientStream::DoHandshakeLoop(
cert_verify_result_.reset();
}
next_state_ = STATE_RECV_SHLO;
DVLOG(1) << "Client Sending: " << out.DebugString();
DVLOG(1) << "Client: Sending " << out.DebugString();
SendHandshakeMessage(out);
// Be prepared to decrypt with the new server write key.
session()->connection()->SetAlternativeDecrypter(
......
This diff is collapsed.
This diff is collapsed.
......@@ -6,6 +6,7 @@
#include "base/logging.h"
#include "net/quic/crypto/quic_random.h"
#include "net/quic/quic_ack_notifier.h"
#include "net/quic/quic_fec_group.h"
#include "net/quic/quic_utils.h"
......@@ -30,7 +31,7 @@ QuicPacketCreator::QuicPacketCreator(QuicGuid guid,
send_version_in_packet_(!is_server),
sequence_number_length_(options_.send_sequence_number_length),
packet_size_(0) {
framer_->set_fec_builder(this);
framer_->set_fec_builder(reinterpret_cast<QuicFecBuilderInterface*>(this));
}
QuicPacketCreator::~QuicPacketCreator() {
......@@ -138,6 +139,23 @@ size_t QuicPacketCreator::CreateStreamFrame(QuicStreamId id,
return bytes_consumed;
}
size_t QuicPacketCreator::CreateStreamFrameWithNotifier(
QuicStreamId id,
StringPiece data,
QuicStreamOffset offset,
bool fin,
QuicAckNotifier* notifier,
QuicFrame* frame) {
size_t bytes_consumed = CreateStreamFrame(id, data, offset, fin, frame);
// The frame keeps track of the QuicAckNotifier until it is serialized into
// a packet. At that point the notifier is informed of the sequence number
// of the packet that this frame was eventually sent in.
frame->stream_frame->notifier = notifier;
return bytes_consumed;
}
SerializedPacket QuicPacketCreator::ReserializeAllFrames(
const QuicFrames& frames,
QuicSequenceNumberLength original_length) {
......@@ -215,8 +233,19 @@ SerializedPacket QuicPacketCreator::SerializePacket() {
QuicPacketHeader header;
FillPacketHeader(fec_group_number_, false, false, &header);
SerializedPacket serialized = framer_->BuildDataPacket(
header, queued_frames_, PacketSize());
SerializedPacket serialized =
framer_->BuildDataPacket(header, queued_frames_, packet_size_);
// Run through all the included frames and if any of them have an AckNotifier
// registered, then inform the AckNotifier that it should be interested in
// this packet's sequence number.
for (QuicFrames::iterator it = queued_frames_.begin();
it != queued_frames_.end(); ++it) {
if (it->type == STREAM_FRAME && it->stream_frame->notifier != NULL) {
it->stream_frame->notifier->AddSequenceNumber(serialized.sequence_number);
}
}
packet_size_ = 0;
queued_frames_.clear();
serialized.retransmittable_frames = queued_retransmittable_frames_.release();
......
......@@ -23,6 +23,7 @@ namespace test {
class QuicPacketCreatorPeer;
}
class QuicAckNotifier;
class QuicRandom;
class NET_EXPORT_PRIVATE QuicPacketCreator : public QuicFecBuilderInterface {
......@@ -89,6 +90,17 @@ class NET_EXPORT_PRIVATE QuicPacketCreator : public QuicFecBuilderInterface {
bool fin,
QuicFrame* frame);
// As above, but keeps track of an QuicAckNotifier that should be called when
// the packet that contains this stream frame is ACKed.
// The |notifier| is not owned by the QuicPacketGenerator and must outlive the
// generated packet.
size_t CreateStreamFrameWithNotifier(QuicStreamId id,
base::StringPiece data,
QuicStreamOffset offset,
bool fin,
QuicAckNotifier* notifier,
QuicFrame* frame);
// Serializes all frames into a single packet. All frames must fit into a
// single packet. Also, sets the entropy hash of the serialized packet to a
// random bool and returns that value as a member of SerializedPacket.
......
......@@ -12,6 +12,8 @@ using base::StringPiece;
namespace net {
class QuicAckNotifier;
QuicPacketGenerator::QuicPacketGenerator(DelegateInterface* delegate,
DebugDelegateInterface* debug_delegate,
QuicPacketCreator* creator)
......@@ -60,7 +62,6 @@ void QuicPacketGenerator::SetShouldSendAck(bool also_send_feedback) {
SendQueuedFrames();
}
void QuicPacketGenerator::AddControlFrame(const QuicFrame& frame) {
queued_control_frames_.push_back(frame);
SendQueuedFrames();
......@@ -70,6 +71,14 @@ QuicConsumedData QuicPacketGenerator::ConsumeData(QuicStreamId id,
StringPiece data,
QuicStreamOffset offset,
bool fin) {
return ConsumeData(id, data, offset, fin, NULL);
}
QuicConsumedData QuicPacketGenerator::ConsumeData(QuicStreamId id,
StringPiece data,
QuicStreamOffset offset,
bool fin,
QuicAckNotifier* notifier) {
IsHandshake handshake = id == kCryptoStreamId ? IS_HANDSHAKE : NOT_HANDSHAKE;
// The caller should have flushed pending frames before sending handshake
// messages.
......@@ -82,8 +91,15 @@ QuicConsumedData QuicPacketGenerator::ConsumeData(QuicStreamId id,
while (delegate_->CanWrite(NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA,
handshake)) {
QuicFrame frame;
size_t bytes_consumed = packet_creator_->CreateStreamFrame(
size_t bytes_consumed;
if (notifier != NULL) {
// We want to track which packet this stream frame ends up in.
bytes_consumed = packet_creator_->CreateStreamFrameWithNotifier(
id, data, offset + total_bytes_consumed, fin, notifier, &frame);
} else {
bytes_consumed = packet_creator_->CreateStreamFrame(
id, data, offset + total_bytes_consumed, fin, &frame);
}
bool success = AddFrame(frame);
DCHECK(success);
......
......@@ -57,6 +57,8 @@
namespace net {
class QuicAckNotifier;
class NET_EXPORT_PRIVATE QuicPacketGenerator {
public:
class NET_EXPORT_PRIVATE DelegateInterface {
......@@ -90,11 +92,24 @@ class NET_EXPORT_PRIVATE QuicPacketGenerator {
void SetShouldSendAck(bool also_send_feedback);
void AddControlFrame(const QuicFrame& frame);
// Given some data, may consume part or all of it and pass it to the packet
// creator to be serialized into packets. If not in batch mode, these packets
// will also be sent during this call.
QuicConsumedData ConsumeData(QuicStreamId id,
base::StringPiece data,
QuicStreamOffset offset,
bool fin);
// As above, but attaches a QuicAckNotifier to any created stream frames,
// which will be called once the frame is ACKed by the peer.
// The QuicAckNotifier is owned by the QuicConnection.
QuicConsumedData ConsumeData(QuicStreamId id,
base::StringPiece data,
QuicStreamOffset offset,
bool fin,
QuicAckNotifier* notifier);
// Indicates whether batch mode is currently enabled.
bool InBatchMode();
// Disables flushing.
......@@ -121,6 +136,7 @@ class NET_EXPORT_PRIVATE QuicPacketGenerator {
bool AddNextPendingFrame();
bool AddFrame(const QuicFrame& frame);
void SerializeAndSendPacket();
DelegateInterface* delegate_;
......
......@@ -102,7 +102,8 @@ QuicStreamFrame::QuicStreamFrame(QuicStreamId stream_id,
: stream_id(stream_id),
fin(fin),
offset(offset),
data(data) {
data(data),
notifier(NULL) {
}
uint32 MakeQuicTag(char a, char b, char c, char d) {
......@@ -126,6 +127,8 @@ QuicTag QuicVersionToQuicTag(const QuicVersion version) {
return MakeQuicTag('Q', '0', '0', '8');
case QUIC_VERSION_9:
return MakeQuicTag('Q', '0', '0', '9');
case QUIC_VERSION_10:
return MakeQuicTag('Q', '0', '1', '0');
default:
// This shold be an ERROR because we should never attempt to convert an
// invalid QuicVersion to be written to the wire.
......@@ -138,6 +141,7 @@ QuicVersion QuicTagToQuicVersion(const QuicTag version_tag) {
const QuicTag quic_tag_v7 = MakeQuicTag('Q', '0', '0', '7');
const QuicTag quic_tag_v8 = MakeQuicTag('Q', '0', '0', '8');
const QuicTag quic_tag_v9 = MakeQuicTag('Q', '0', '0', '9');
const QuicTag quic_tag_v10 = MakeQuicTag('Q', '0', '1', '0');
if (version_tag == quic_tag_v7) {
return QUIC_VERSION_7;
......@@ -145,6 +149,8 @@ QuicVersion QuicTagToQuicVersion(const QuicTag version_tag) {
return QUIC_VERSION_8;
} else if (version_tag == quic_tag_v9) {
return QUIC_VERSION_9;
} else if (version_tag == quic_tag_v10) {
return QUIC_VERSION_10;
} else {
// Reading from the client so this should not be considered an ERROR.
DLOG(INFO) << "Unsupported QuicTag version: "
......@@ -162,6 +168,7 @@ string QuicVersionToString(const QuicVersion version) {
RETURN_STRING_LITERAL(QUIC_VERSION_7);
RETURN_STRING_LITERAL(QUIC_VERSION_8);
RETURN_STRING_LITERAL(QUIC_VERSION_9);
RETURN_STRING_LITERAL(QUIC_VERSION_10);
default:
return "QUIC_VERSION_UNSUPPORTED";
}
......@@ -414,6 +421,21 @@ void RetransmittableFrames::set_encryption_level(EncryptionLevel level) {
encryption_level_ = level;
}
SerializedPacket::SerializedPacket(
QuicPacketSequenceNumber sequence_number,
QuicSequenceNumberLength sequence_number_length,
QuicPacket* packet,
QuicPacketEntropyHash entropy_hash,
RetransmittableFrames* retransmittable_frames)
: sequence_number(sequence_number),
sequence_number_length(sequence_number_length),
packet(packet),
entropy_hash(entropy_hash),
retransmittable_frames(retransmittable_frames) {
}
SerializedPacket::~SerializedPacket() {}
ostream& operator<<(ostream& os, const QuicEncryptedPacket& s) {
os << s.length() << "-byte data";
return os;
......
......@@ -27,6 +27,7 @@ namespace net {
using ::operator<<;
class QuicAckNotifier;
class QuicPacket;
struct QuicPacketHeader;
......@@ -99,12 +100,12 @@ enum IsHandshake {
enum QuicFrameType {
PADDING_FRAME = 0,
STREAM_FRAME,
ACK_FRAME,
CONGESTION_FEEDBACK_FRAME,
RST_STREAM_FRAME,
CONNECTION_CLOSE_FRAME,
GOAWAY_FRAME,
STREAM_FRAME,
ACK_FRAME,
CONGESTION_FEEDBACK_FRAME,
NUM_FRAME_TYPES
};
......@@ -190,7 +191,8 @@ enum QuicVersion {
QUIC_VERSION_7 = 7,
QUIC_VERSION_8 = 8,
QUIC_VERSION_9 = 9, // Current version.
QUIC_VERSION_9 = 9,
QUIC_VERSION_10 = 10, // Current version.
};
// This vector contains QUIC versions which we currently support.
......@@ -198,7 +200,7 @@ enum QuicVersion {
// element, with subsequent elements in descending order (versions can be
// skipped as necessary).
static const QuicVersion kSupportedQuicVersions[] =
{QUIC_VERSION_9};
{QUIC_VERSION_10, QUIC_VERSION_9};
typedef std::vector<QuicVersion> QuicVersionVector;
......@@ -474,6 +476,10 @@ struct NET_EXPORT_PRIVATE QuicStreamFrame {
bool fin;
QuicStreamOffset offset; // Location of this data in the stream.
base::StringPiece data;
// If this is set, then when this packet is ACKed the AckNotifier will be
// informed.
QuicAckNotifier* notifier;
};
// TODO(ianswett): Re-evaluate the trade-offs of hash_set vs set when framing
......@@ -829,28 +835,24 @@ struct NET_EXPORT_PRIVATE SerializedPacket {
QuicSequenceNumberLength sequence_number_length,
QuicPacket* packet,
QuicPacketEntropyHash entropy_hash,
RetransmittableFrames* retransmittable_frames)
: sequence_number(sequence_number),
sequence_number_length(sequence_number_length),
packet(packet),
entropy_hash(entropy_hash),
retransmittable_frames(retransmittable_frames) {}
RetransmittableFrames* retransmittable_frames);
~SerializedPacket();
QuicPacketSequenceNumber sequence_number;
QuicSequenceNumberLength sequence_number_length;
QuicPacket* packet;
QuicPacketEntropyHash entropy_hash;
RetransmittableFrames* retransmittable_frames;
// If set, these will be called when this packet is ACKed by the peer.
std::set<QuicAckNotifier*> notifiers;
};
// A struct for functions which consume data payloads and fins.
// The first member of the pair indicates bytes consumed.
// The second member of the pair indicates if an incoming fin was consumed.
struct QuicConsumedData {
QuicConsumedData(size_t bytes_consumed, bool fin_consumed)
: bytes_consumed(bytes_consumed),
fin_consumed(fin_consumed) {}
// By default, gtest prints the raw bytes of an object. The bool data
// member causes this object to have padding bytes, which causes the
// default gtest object printer to read uninitialize memory. So we need
......@@ -858,7 +860,10 @@ struct QuicConsumedData {
NET_EXPORT_PRIVATE friend std::ostream& operator<<(
std::ostream& os, const QuicConsumedData& s);
// How many bytes were consumed.
size_t bytes_consumed;
// True if an incoming fin was consumed.
bool fin_consumed;
};
......
......@@ -428,12 +428,13 @@ ReliableQuicStream* QuicSession::GetIncomingReliableStream(
connection()->SendConnectionClose(QUIC_INVALID_STREAM_ID);
return NULL;
}
if (largest_peer_created_stream_id_ != 0) {
for (QuicStreamId id = largest_peer_created_stream_id_ + 2;
id < stream_id;
id += 2) {
implicitly_created_streams_.insert(id);
}
if (largest_peer_created_stream_id_ == 0) {
largest_peer_created_stream_id_= 1;
}
for (QuicStreamId id = largest_peer_created_stream_id_ + 2;
id < stream_id;
id += 2) {
implicitly_created_streams_.insert(id);
}
largest_peer_created_stream_id_ = stream_id;
}
......
......@@ -176,6 +176,15 @@ TEST_F(QuicSessionTest, IsClosedStreamDefault) {
}
}
TEST_F(QuicSessionTest, ImplicitlyCreatedStreams) {
ASSERT_TRUE(session_.GetIncomingReliableStream(7) != NULL);
// Both 3 and 5 should be implicitly created.
EXPECT_FALSE(session_.IsClosedStream(3));
EXPECT_FALSE(session_.IsClosedStream(5));
ASSERT_TRUE(session_.GetIncomingReliableStream(5) != NULL);
ASSERT_TRUE(session_.GetIncomingReliableStream(3) != NULL);
}
TEST_F(QuicSessionTest, IsClosedStreamLocallyCreated) {
TestStream* stream2 = session_.CreateOutgoingReliableStream();
EXPECT_EQ(2u, stream2->id());
......
......@@ -126,6 +126,12 @@ void QuicConnectionPeer::SetSelfAddress(QuicConnection* connection,
connection->self_address_ = self_address;
}
// static
void QuicConnectionPeer::SetPeerAddress(QuicConnection* connection,
const IPEndPoint& peer_address) {
connection->peer_address_ = peer_address;
}
// static
void QuicConnectionPeer::SwapCrypters(QuicConnection* connection,
QuicFramer* framer) {
......
......@@ -78,6 +78,9 @@ class QuicConnectionPeer {
static void SetSelfAddress(QuicConnection* connection,
const IPEndPoint& self_address);
static void SetPeerAddress(QuicConnection* connection,
const IPEndPoint& peer_address);
static void SwapCrypters(QuicConnection* connection, QuicFramer* framer);
static void SetMaxPacketsPerRetransmissionAlarm(QuicConnection* connection,
......
......@@ -284,6 +284,12 @@ MockSendAlgorithm::MockSendAlgorithm() {
MockSendAlgorithm::~MockSendAlgorithm() {
}
MockAckNotifierDelegate::MockAckNotifierDelegate() {
}
MockAckNotifierDelegate::~MockAckNotifierDelegate() {
}
namespace {
string HexDumpWithMarks(const char* data, int length,
......
......@@ -366,6 +366,14 @@ class TestDecompressorVisitor : public QuicSpdyDecompressor::Visitor {
bool error_;
};
class MockAckNotifierDelegate : public QuicAckNotifier::DelegateInterface {
public:
MockAckNotifierDelegate();
virtual ~MockAckNotifierDelegate();
MOCK_METHOD0(OnAckNotification, void());
};
} // namespace test
} // namespace net
......
......@@ -61,13 +61,18 @@ void GenerateBody(string* body, int length) {
// Simple wrapper class to run server in a thread.
class ServerThread : public base::SimpleThread {
public:
explicit ServerThread(IPEndPoint address, const QuicConfig& config)
ServerThread(IPEndPoint address,
const QuicConfig& config,
bool strike_register_no_startup_period)
: SimpleThread("server_thread"),
listening_(true, false),
quit_(true, false),
server_(config),
address_(address),
port_(0) {
if (strike_register_no_startup_period) {
server_.SetStrikeRegisterNoStartupPeriod();
}
}
virtual ~ServerThread() {
}
......@@ -116,7 +121,8 @@ class EndToEndTest : public ::testing::TestWithParam<QuicVersion> {
protected:
EndToEndTest()
: server_hostname_("example.com"),
server_started_(false) {
server_started_(false),
strike_register_no_startup_period_(false) {
net::IPAddressNumber ip;
CHECK(net::ParseIPLiteralToNumber("127.0.0.1", &ip));
server_address_ = IPEndPoint(ip, 0);
......@@ -154,7 +160,8 @@ class EndToEndTest : public ::testing::TestWithParam<QuicVersion> {
}
void StartServer() {
server_thread_.reset(new ServerThread(server_address_, server_config_));
server_thread_.reset(new ServerThread(server_address_, server_config_,
strike_register_no_startup_period_));
server_thread_->Start();
server_thread_->listening()->Wait();
server_address_ = IPEndPoint(server_address_.address(),
......@@ -210,6 +217,7 @@ class EndToEndTest : public ::testing::TestWithParam<QuicVersion> {
QuicConfig client_config_;
QuicConfig server_config_;
QuicVersion version_;
bool strike_register_no_startup_period_;
};
// Run all end to end tests with all supported versions.
......@@ -442,6 +450,9 @@ TEST_P(EndToEndTest, LargePost) {
}
TEST_P(EndToEndTest, LargePostZeroRTTFailure) {
// Have the server accept 0-RTT without waiting a startup period.
strike_register_no_startup_period_ = true;
// Send a request and then disconnect. This prepares the client to attempt
// a 0-RTT handshake for the next request.
ASSERT_TRUE(Initialize());
......@@ -454,14 +465,15 @@ TEST_P(EndToEndTest, LargePostZeroRTTFailure) {
request.AddBody(body, true);
EXPECT_EQ(kFooResponseBody, client_->SendCustomSynchronousRequest(request));
EXPECT_EQ(2, client_->client()->session()->GetNumSentClientHellos());
client_->Disconnect();
// The 0-RTT handshake should succeed.
// TODO(wtc): figure out why this 0-RTT handshake takes 1 RTT.
client_->Connect();
ASSERT_TRUE(client_->client()->connected());
EXPECT_EQ(kFooResponseBody, client_->SendCustomSynchronousRequest(request));
EXPECT_EQ(1, client_->client()->session()->GetNumSentClientHellos());
client_->Disconnect();
......@@ -472,6 +484,7 @@ TEST_P(EndToEndTest, LargePostZeroRTTFailure) {
client_->Connect();
ASSERT_TRUE(client_->client()->connected());
EXPECT_EQ(kFooResponseBody, client_->SendCustomSynchronousRequest(request));
EXPECT_EQ(2, client_->client()->session()->GetNumSentClientHellos());
}
// TODO(ianswett): Enable once b/9295090 is fixed.
......
......@@ -55,6 +55,10 @@ bool QuicClientSession::CryptoConnect() {
return crypto_stream_.CryptoConnect();
}
int QuicClientSession::GetNumSentClientHellos() const {
return crypto_stream_.num_sent_client_hellos();
}
ReliableQuicStream* QuicClientSession::CreateIncomingReliableStream(
QuicStreamId id) {
DLOG(ERROR) << "Server push not supported";
......
......@@ -39,6 +39,11 @@ class QuicClientSession : public QuicSession {
// handshake is started successfully.
bool CryptoConnect();
// Returns the number of client hello messages that have been sent on the
// crypto stream. If the handshake has completed then this is one greater
// than the number of round-trips needed for the handshake.
int GetNumSentClientHellos() const;
protected:
// QuicSession methods:
virtual ReliableQuicStream* CreateIncomingReliableStream(
......
......@@ -18,8 +18,6 @@
namespace net {
class QuicCryptoServerConfig;
namespace tools {
class QuicDispatcher;
......@@ -67,6 +65,10 @@ class QuicServer : public EpollCallbackInterface {
const IPEndPoint& server_address,
const IPEndPoint& client_address);
void SetStrikeRegisterNoStartupPeriod() {
crypto_config_.set_strike_register_no_startup_period();
}
bool overflow_supported() { return overflow_supported_; }
int packets_dropped() { return packets_dropped_; }
......
......@@ -76,6 +76,12 @@ QuicCryptoStream* TestSession::GetCryptoStream() {
return crypto_stream_;
}
MockAckNotifierDelegate::MockAckNotifierDelegate() {
}
MockAckNotifierDelegate::~MockAckNotifierDelegate() {
}
} // namespace test
} // namespace tools
} // namespace net
......@@ -103,6 +103,14 @@ class TestSession : public QuicSession {
DISALLOW_COPY_AND_ASSIGN(TestSession);
};
class MockAckNotifierDelegate : public QuicAckNotifier::DelegateInterface {
public:
MockAckNotifierDelegate();
virtual ~MockAckNotifierDelegate();
MOCK_METHOD0(OnAckNotification, void());
};
} // namespace test
} // namespace tools
} // namespace net
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment