summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--net/quic/congestion_control/send_algorithm_simulator.cc21
-rw-r--r--net/quic/congestion_control/send_algorithm_simulator.h9
-rw-r--r--net/quic/crypto/crypto_handshake.cc3
-rw-r--r--net/quic/crypto/crypto_handshake.h7
-rw-r--r--net/quic/crypto/quic_crypto_client_config.cc7
-rw-r--r--net/quic/crypto/quic_crypto_server_config.cc65
-rw-r--r--net/quic/crypto/quic_crypto_server_config.h9
-rw-r--r--net/quic/quic_connection.cc15
-rw-r--r--net/quic/quic_connection.h8
-rw-r--r--net/quic/quic_connection_logger.h2
-rw-r--r--net/quic/quic_crypto_client_stream.cc1
-rw-r--r--net/quic/quic_crypto_server_stream.cc54
-rw-r--r--net/quic/quic_crypto_server_stream.h40
-rw-r--r--net/quic/quic_crypto_server_stream_test.cc2
-rw-r--r--net/quic/quic_crypto_stream.cc8
-rw-r--r--net/quic/quic_crypto_stream.h4
-rw-r--r--net/quic/quic_crypto_stream_test.cc3
-rw-r--r--net/quic/quic_data_stream_test.cc6
-rw-r--r--net/quic/quic_dispatcher.cc43
-rw-r--r--net/quic/quic_dispatcher.h13
-rw-r--r--net/quic/quic_flags.cc12
-rw-r--r--net/quic/quic_flags.h3
-rw-r--r--net/quic/quic_flow_controller.cc7
-rw-r--r--net/quic/quic_framer.h2
-rw-r--r--net/quic/quic_framer_test.cc5
-rw-r--r--net/quic/quic_headers_stream_test.cc3
-rw-r--r--net/quic/quic_protocol.cc3
-rw-r--r--net/quic/quic_protocol.h8
-rw-r--r--net/quic/quic_sent_packet_manager.cc9
-rw-r--r--net/quic/quic_sent_packet_manager.h8
-rw-r--r--net/quic/quic_session.cc16
-rw-r--r--net/quic/quic_session_test.cc43
-rw-r--r--net/quic/quic_utils.cc1
-rw-r--r--net/quic/reliable_quic_stream_test.cc12
-rw-r--r--net/quic/test_tools/crypto_test_utils.cc13
-rw-r--r--net/tools/quic/end_to_end_test.cc5
-rw-r--r--net/tools/quic/quic_dispatcher.cc28
-rw-r--r--net/tools/quic/quic_dispatcher.h13
38 files changed, 312 insertions, 199 deletions
diff --git a/net/quic/congestion_control/send_algorithm_simulator.cc b/net/quic/congestion_control/send_algorithm_simulator.cc
index 7af949a..33f6ba00 100644
--- a/net/quic/congestion_control/send_algorithm_simulator.cc
+++ b/net/quic/congestion_control/send_algorithm_simulator.cc
@@ -195,7 +195,7 @@ QuicTime::Delta SendAlgorithmSimulator::FindNextAck(
continue;
}
// Lost packets don't trigger an ack.
- if (it->ack_time == QuicTime::Zero()) {
+ if (it->lost) {
continue;
}
DCHECK_LT(*next_acked, it->sequence_number);
@@ -228,7 +228,7 @@ bool SendAlgorithmSimulator::HasRecentLostPackets(
continue;
}
// Lost packets don't trigger an ack.
- if (it->ack_time == QuicTime::Zero()) {
+ if (it->lost) {
return true;
}
// Buffer dropped packets are skipped automatically, but still end up
@@ -252,7 +252,7 @@ void SendAlgorithmSimulator::HandlePendingAck(Transfer* transfer) {
<< " Now():" << (clock_->Now().ToDebuggingValue() / 1000) << "ms";
// Some entries may be missing from the sent_packets_ array, if they were
// dropped due to buffer overruns.
- SentPacket largest_observed(0, QuicTime::Zero(), QuicTime::Zero(), NULL);
+ SentPacket largest_observed;
list<SentPacket>::iterator it = sent_packets_.begin();
while (sender->last_acked < sender->next_acked) {
++sender->last_acked;
@@ -271,10 +271,10 @@ void SendAlgorithmSimulator::HandlePendingAck(Transfer* transfer) {
lost_packets[sender->last_acked] = info;
continue;
}
- if (it->ack_time.IsInitialized()) {
- acked_packets[sender->last_acked] = info;
- } else {
+ if (it->lost) {
lost_packets[sender->last_acked] = info;
+ } else {
+ acked_packets[sender->last_acked] = info;
}
// This packet has been acked or lost, remove it from sent_packets_.
largest_observed = *it;
@@ -310,6 +310,8 @@ void SendAlgorithmSimulator::HandlePendingAck(Transfer* transfer) {
sender->last_transfer_bandwidth =
QuicBandwidth::FromBytesAndTimeDelta(transfer->num_bytes,
transfer_time);
+ DCHECK_GE(bandwidth_.ToBitsPerSecond(),
+ sender->last_transfer_bandwidth.ToBitsPerSecond());
for (vector<Transfer>::iterator it = pending_transfers_.begin();
it != pending_transfers_.end(); ++it) {
if (transfer == &(*it)) {
@@ -336,8 +338,7 @@ void SendAlgorithmSimulator::SendDataNow(Transfer* transfer) {
bool packet_lost =
forward_loss_rate_ * kuint64max > simple_random_.RandUint64();
// Handle correlated loss.
- if (!sent_packets_.empty() &&
- !sent_packets_.back().ack_time.IsInitialized() &&
+ if (!sent_packets_.empty() && sent_packets_.back().lost &&
loss_correlation_ * kuint64max > simple_random_.RandUint64()) {
packet_lost = true;
}
@@ -354,10 +355,8 @@ void SendAlgorithmSimulator::SendDataNow(Transfer* transfer) {
QuicTime queue_ack_time = sent_packets_.empty() ? QuicTime::Zero() :
sent_packets_.back().ack_time.Add(bandwidth_.TransferTime(kPacketSize));
ack_time = QuicTime::Max(ack_time, queue_ack_time);
- // If the packet is lost, give it an ack time of Zero.
sent_packets_.push_back(SentPacket(
- sender->last_sent, clock_->Now(),
- packet_lost ? QuicTime::Zero() : ack_time, transfer));
+ sender->last_sent, clock_->Now(), ack_time, packet_lost, transfer));
} else {
DVLOG(1) << "losing packet:" << sender->last_sent
<< " because the buffer was full.";
diff --git a/net/quic/congestion_control/send_algorithm_simulator.h b/net/quic/congestion_control/send_algorithm_simulator.h
index fb64049..934b275 100644
--- a/net/quic/congestion_control/send_algorithm_simulator.h
+++ b/net/quic/congestion_control/send_algorithm_simulator.h
@@ -89,18 +89,27 @@ class SendAlgorithmSimulator {
};
struct SentPacket {
+ SentPacket()
+ : sequence_number(0),
+ send_time(QuicTime::Zero()),
+ ack_time(QuicTime::Zero()),
+ lost(false),
+ transfer(NULL) {}
SentPacket(QuicPacketSequenceNumber sequence_number,
QuicTime send_time,
QuicTime ack_time,
+ bool lost,
Transfer* transfer)
: sequence_number(sequence_number),
send_time(send_time),
ack_time(ack_time),
+ lost(lost),
transfer(transfer) {}
QuicPacketSequenceNumber sequence_number;
QuicTime send_time;
QuicTime ack_time;
+ bool lost;
Transfer* transfer;
};
diff --git a/net/quic/crypto/crypto_handshake.cc b/net/quic/crypto/crypto_handshake.cc
index 408b76b..5a928b6 100644
--- a/net/quic/crypto/crypto_handshake.cc
+++ b/net/quic/crypto/crypto_handshake.cc
@@ -13,7 +13,8 @@ namespace net {
QuicCryptoNegotiatedParameters::QuicCryptoNegotiatedParameters()
: key_exchange(0),
- aead(0) {
+ aead(0),
+ x509_ecdsa_supported(false) {
}
QuicCryptoNegotiatedParameters::~QuicCryptoNegotiatedParameters() {}
diff --git a/net/quic/crypto/crypto_handshake.h b/net/quic/crypto/crypto_handshake.h
index cc25570..bfd2f34 100644
--- a/net/quic/crypto/crypto_handshake.h
+++ b/net/quic/crypto/crypto_handshake.h
@@ -124,6 +124,13 @@ struct NET_EXPORT_PRIVATE QuicCryptoNegotiatedParameters {
// bytes of x coordinate, followed by 32 bytes of y coordinate. Both values
// are big-endian and the pair is a P-256 public key.
std::string channel_id;
+
+ // Used when generating proof signature when sending server config updates.
+ bool x509_ecdsa_supported;
+
+ // Used to generate cert chain when sending server config updates.
+ std::string client_common_set_hashes;
+ std::string client_cached_cert_hashes;
};
// QuicCryptoConfig contains common configuration between clients and servers.
diff --git a/net/quic/crypto/quic_crypto_client_config.cc b/net/quic/crypto/quic_crypto_client_config.cc
index 18c0d54..9b64e1c 100644
--- a/net/quic/crypto/quic_crypto_client_config.cc
+++ b/net/quic/crypto/quic_crypto_client_config.cc
@@ -612,7 +612,12 @@ QuicErrorCode QuicCryptoClientConfig::CacheNewServerConfig(
cached->SetProof(certs, proof);
} else {
- cached->ClearProof();
+ if (proof_verifier() != NULL) {
+ // Secure QUIC: clear existing proof as we have been sent a new SCFG
+ // without matching proof/certs.
+ cached->ClearProof();
+ }
+
if (has_proof && !has_cert) {
*error_details = "Certificate missing";
return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
diff --git a/net/quic/crypto/quic_crypto_server_config.cc b/net/quic/crypto/quic_crypto_server_config.cc
index ec88594c..bfd3ed3 100644
--- a/net/quic/crypto/quic_crypto_server_config.cc
+++ b/net/quic/crypto/quic_crypto_server_config.cc
@@ -603,7 +603,7 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello(
!info.client_nonce_well_formed ||
!info.unique ||
!requested_config.get()) {
- BuildRejection(*primary_config, client_hello, info, rand, out);
+ BuildRejection(*primary_config, client_hello, info, rand, params, out);
return QUIC_NO_ERROR;
}
@@ -1039,11 +1039,50 @@ void QuicCryptoServerConfig::EvaluateClientHello(
helper.StartedAsyncCallback();
}
+bool QuicCryptoServerConfig::BuildServerConfigUpdateMessage(
+ const IPEndPoint& client_ip,
+ const QuicClock* clock,
+ QuicRandom* rand,
+ const QuicCryptoNegotiatedParameters& params,
+ CryptoHandshakeMessage* out) const {
+ base::AutoLock locked(configs_lock_);
+ out->set_tag(kSCUP);
+ out->SetStringPiece(kSCFG, primary_config_->serialized);
+ out->SetStringPiece(kSourceAddressTokenTag,
+ NewSourceAddressToken(*primary_config_,
+ client_ip,
+ rand,
+ clock->WallNow()));
+
+ if (proof_source_ == NULL) {
+ // Insecure QUIC, can send SCFG without proof.
+ return true;
+ }
+
+ const vector<string>* certs;
+ string signature;
+ if (!proof_source_->GetProof(params.sni, primary_config_->serialized,
+ params.x509_ecdsa_supported, &certs,
+ &signature)) {
+ DVLOG(1) << "Server: failed to get proof.";
+ return false;
+ }
+
+ const string compressed = CertCompressor::CompressChain(
+ *certs, params.client_common_set_hashes, params.client_cached_cert_hashes,
+ primary_config_->common_cert_sets);
+
+ out->SetStringPiece(kCertificateTag, compressed);
+ out->SetStringPiece(kPROF, signature);
+ return true;
+}
+
void QuicCryptoServerConfig::BuildRejection(
const Config& config,
const CryptoHandshakeMessage& client_hello,
const ClientHelloInfo& info,
QuicRandom* rand,
+ QuicCryptoNegotiatedParameters *params,
CryptoHandshakeMessage* out) const {
out->set_tag(kREJ);
out->SetStringPiece(kSCFG, config.serialized);
@@ -1074,12 +1113,12 @@ void QuicCryptoServerConfig::BuildRejection(
return;
}
- bool x509_supported = false, x509_ecdsa_supported = false;
+ bool x509_supported = false;
for (size_t i = 0; i < num_their_proof_demands; i++) {
switch (their_proof_demands[i]) {
case kX509:
x509_supported = true;
- x509_ecdsa_supported = true;
+ params->x509_ecdsa_supported = true;
break;
case kX59R:
x509_supported = true;
@@ -1094,18 +1133,24 @@ void QuicCryptoServerConfig::BuildRejection(
const vector<string>* certs;
string signature;
if (!proof_source_->GetProof(info.sni.as_string(), config.serialized,
- x509_ecdsa_supported, &certs, &signature)) {
+ params->x509_ecdsa_supported, &certs,
+ &signature)) {
return;
}
- StringPiece their_common_set_hashes;
- StringPiece their_cached_cert_hashes;
- client_hello.GetStringPiece(kCCS, &their_common_set_hashes);
- client_hello.GetStringPiece(kCCRT, &their_cached_cert_hashes);
+ StringPiece client_common_set_hashes;
+ if (client_hello.GetStringPiece(kCCS, &client_common_set_hashes)) {
+ params->client_common_set_hashes = client_common_set_hashes.as_string();
+ }
+
+ StringPiece client_cached_cert_hashes;
+ if (client_hello.GetStringPiece(kCCRT, &client_cached_cert_hashes)) {
+ params->client_cached_cert_hashes = client_cached_cert_hashes.as_string();
+ }
const string compressed = CertCompressor::CompressChain(
- *certs, their_common_set_hashes, their_cached_cert_hashes,
- config.common_cert_sets);
+ *certs, params->client_common_set_hashes,
+ params->client_cached_cert_hashes, config.common_cert_sets);
// kREJOverheadBytes is a very rough estimate of how much of a REJ
// message is taken up by things other than the certificates.
diff --git a/net/quic/crypto/quic_crypto_server_config.h b/net/quic/crypto/quic_crypto_server_config.h
index 8760ace..9ec0612 100644
--- a/net/quic/crypto/quic_crypto_server_config.h
+++ b/net/quic/crypto/quic_crypto_server_config.h
@@ -16,6 +16,7 @@
#include "net/base/ip_endpoint.h"
#include "net/base/net_export.h"
#include "net/quic/crypto/crypto_handshake.h"
+#include "net/quic/crypto/crypto_handshake_message.h"
#include "net/quic/crypto/crypto_protocol.h"
#include "net/quic/crypto/crypto_secret_boxer.h"
#include "net/quic/quic_time.h"
@@ -210,6 +211,13 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig {
CryptoHandshakeMessage* out,
std::string* error_details) const;
+ bool BuildServerConfigUpdateMessage(
+ const IPEndPoint& client_ip,
+ const QuicClock* clock,
+ QuicRandom* rand,
+ const QuicCryptoNegotiatedParameters& params,
+ CryptoHandshakeMessage* out) const;
+
// SetProofSource installs |proof_source| as the ProofSource for handshakes.
// This object takes ownership of |proof_source|.
void SetProofSource(ProofSource* proof_source);
@@ -371,6 +379,7 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig {
const CryptoHandshakeMessage& client_hello,
const ClientHelloInfo& info,
QuicRandom* rand,
+ QuicCryptoNegotiatedParameters *params,
CryptoHandshakeMessage* out) const;
// ParseConfigProtobuf parses the given config protobuf and returns a
diff --git a/net/quic/quic_connection.cc b/net/quic/quic_connection.cc
index 405ba42..f40b34d 100644
--- a/net/quic/quic_connection.cc
+++ b/net/quic/quic_connection.cc
@@ -1179,17 +1179,15 @@ void QuicConnection::WriteIfNotBlocked() {
}
bool QuicConnection::ProcessValidatedPacket() {
- if ((!FLAGS_quic_allow_port_migration && peer_port_changed_) ||
- peer_ip_changed_ || self_ip_changed_ || self_port_changed_) {
+ if (peer_ip_changed_ || self_ip_changed_ || self_port_changed_) {
SendConnectionCloseWithDetails(
QUIC_ERROR_MIGRATING_ADDRESS,
"Neither IP address migration, nor self port migration are supported.");
return false;
}
- // Port migration is supported, do it now if port has changed.
- if (FLAGS_quic_allow_port_migration &&
- peer_port_changed_) {
+ // Peer port migration is supported, do it now if port has changed.
+ if (peer_port_changed_) {
DVLOG(1) << ENDPOINT << "Peer's port changed from "
<< peer_address_.port() << " to " << migrating_peer_port_
<< ", migrating connection.";
@@ -1542,6 +1540,10 @@ bool QuicConnection::OnSerializedPacket(
NOT_RETRANSMISSION);
}
+void QuicConnection::OnHandshakeComplete() {
+ sent_packet_manager_.SetHandshakeConfirmed();
+}
+
bool QuicConnection::SendOrQueuePacket(EncryptionLevel level,
const SerializedPacket& packet,
TransmissionType transmission_type) {
@@ -1796,6 +1798,9 @@ void QuicConnection::CloseConnection(QuicErrorCode error, bool from_peer) {
return;
}
connected_ = false;
+ if (debug_visitor_.get() != NULL) {
+ debug_visitor_->OnConnectionClosed(error, from_peer);
+ }
visitor_->OnConnectionClosed(error, from_peer);
// Cancel the alarms so they don't trigger any action now that the
// connection is closed.
diff --git a/net/quic/quic_connection.h b/net/quic/quic_connection.h
index a98458b..ee5955f 100644
--- a/net/quic/quic_connection.h
+++ b/net/quic/quic_connection.h
@@ -198,6 +198,9 @@ class NET_EXPORT_PRIVATE QuicConnectionDebugVisitor
// in the revival of a packet via FEC.
virtual void OnRevivedPacket(const QuicPacketHeader& revived_header,
base::StringPiece payload) {}
+
+ // Called when the connection is closed.
+ virtual void OnConnectionClosed(QuicErrorCode error, bool from_peer) {}
};
class NET_EXPORT_PRIVATE QuicConnectionHelperInterface {
@@ -363,6 +366,11 @@ class NET_EXPORT_PRIVATE QuicConnection
virtual QuicStopWaitingFrame* CreateStopWaitingFrame() OVERRIDE;
virtual bool OnSerializedPacket(const SerializedPacket& packet) OVERRIDE;
+ // Called by the crypto stream when the handshake completes. In the server's
+ // case this is when the SHLO has been ACKed. Clients call this on receipt of
+ // the SHLO.
+ void OnHandshakeComplete();
+
// Accessors
void set_visitor(QuicConnectionVisitorInterface* visitor) {
visitor_ = visitor;
diff --git a/net/quic/quic_connection_logger.h b/net/quic/quic_connection_logger.h
index d020a61..bbb87e0 100644
--- a/net/quic/quic_connection_logger.h
+++ b/net/quic/quic_connection_logger.h
@@ -62,12 +62,12 @@ class NET_EXPORT_PRIVATE QuicConnectionLogger
const QuicVersionNegotiationPacket& packet) OVERRIDE;
virtual void OnRevivedPacket(const QuicPacketHeader& revived_header,
base::StringPiece payload) OVERRIDE;
+ virtual void OnConnectionClosed(QuicErrorCode error, bool from_peer) OVERRIDE;
void OnCryptoHandshakeMessageReceived(
const CryptoHandshakeMessage& message);
void OnCryptoHandshakeMessageSent(
const CryptoHandshakeMessage& message);
- void OnConnectionClosed(QuicErrorCode error, bool from_peer);
void OnSuccessfulVersionNegotiation(const QuicVersion& version);
void UpdateReceivedFrameCounts(QuicStreamId stream_id,
int num_frames_received,
diff --git a/net/quic/quic_crypto_client_stream.cc b/net/quic/quic_crypto_client_stream.cc
index d28860a..40e4d45 100644
--- a/net/quic/quic_crypto_client_stream.cc
+++ b/net/quic/quic_crypto_client_stream.cc
@@ -464,6 +464,7 @@ void QuicCryptoClientStream::DoHandshakeLoop(
handshake_confirmed_ = true;
session()->OnCryptoHandshakeEvent(QuicSession::HANDSHAKE_CONFIRMED);
+ session()->connection()->OnHandshakeComplete();
return;
}
case STATE_IDLE:
diff --git a/net/quic/quic_crypto_server_stream.cc b/net/quic/quic_crypto_server_stream.cc
index 7bd2c03..8e07f54 100644
--- a/net/quic/quic_crypto_server_stream.cc
+++ b/net/quic/quic_crypto_server_stream.cc
@@ -15,13 +15,23 @@
namespace net {
+void ServerHelloNotifier::OnAckNotification(
+ int num_original_packets,
+ int num_original_bytes,
+ int num_retransmitted_packets,
+ int num_retransmitted_bytes,
+ QuicTime::Delta delta_largest_observed) {
+ server_stream_->OnServerHelloAcked();
+}
+
QuicCryptoServerStream::QuicCryptoServerStream(
const QuicCryptoServerConfig& crypto_config,
QuicSession* session)
: QuicCryptoStream(session),
crypto_config_(crypto_config),
validate_client_hello_cb_(NULL),
- num_handshake_messages_(0) {
+ num_handshake_messages_(0),
+ num_server_config_update_messages_sent_(0) {
}
QuicCryptoServerStream::~QuicCryptoServerStream() {
@@ -116,7 +126,16 @@ void QuicCryptoServerStream::FinishProcessingHandshakeMessage(
session()->connection()->SetDecrypter(
crypto_negotiated_params_.initial_crypters.decrypter.release(),
ENCRYPTION_INITIAL);
- SendHandshakeMessage(reply);
+
+ // We want to be notified when the SHLO is ACKed so that we can disable
+ // HANDSHAKE_MODE in the sent packet manager.
+ if (session()->connection()->version() <= QUIC_VERSION_21) {
+ SendHandshakeMessage(reply);
+ } else {
+ scoped_refptr<ServerHelloNotifier> server_hello_notifier(
+ new ServerHelloNotifier(this));
+ SendHandshakeMessage(reply, server_hello_notifier.get());
+ }
session()->connection()->SetEncrypter(
ENCRYPTION_FORWARD_SECURE,
@@ -130,6 +149,37 @@ void QuicCryptoServerStream::FinishProcessingHandshakeMessage(
encryption_established_ = true;
handshake_confirmed_ = true;
session()->OnCryptoHandshakeEvent(QuicSession::HANDSHAKE_CONFIRMED);
+
+ // Now that the handshake is complete, send an updated server config and
+ // source-address token to the client.
+ SendServerConfigUpdate();
+}
+
+void QuicCryptoServerStream::SendServerConfigUpdate() {
+ if (session()->connection()->version() <= QUIC_VERSION_21) {
+ return;
+ }
+
+ CryptoHandshakeMessage server_config_update_message;
+ if (!crypto_config_.BuildServerConfigUpdateMessage(
+ session()->connection()->peer_address(),
+ session()->connection()->clock(),
+ session()->connection()->random_generator(),
+ crypto_negotiated_params_, &server_config_update_message)) {
+ DVLOG(1) << "Server: Failed to build server config update (SCUP)!";
+ return;
+ }
+
+ DVLOG(1) << "Server: Sending server config update (SCUP): "
+ << server_config_update_message.DebugString();
+ const QuicData& data = server_config_update_message.GetSerialized();
+ WriteOrBufferData(string(data.data(), data.length()), false, NULL);
+
+ ++num_server_config_update_messages_sent_;
+}
+
+void QuicCryptoServerStream::OnServerHelloAcked() {
+ session()->connection()->OnHandshakeComplete();
}
bool QuicCryptoServerStream::GetBase64SHA256ClientChannelID(
diff --git a/net/quic/quic_crypto_server_stream.h b/net/quic/quic_crypto_server_stream.h
index ee40131..9cb242b 100644
--- a/net/quic/quic_crypto_server_stream.h
+++ b/net/quic/quic_crypto_server_stream.h
@@ -16,12 +16,37 @@ namespace net {
class CryptoHandshakeMessage;
class QuicCryptoServerConfig;
+class QuicCryptoServerStream;
class QuicSession;
namespace test {
class CryptoTestUtils;
} // namespace test
+// Receives a notification when the server hello (SHLO) has been ACKed by the
+// peer. At this point we disable HANDSHAKE_MODE in the sent packet manager.
+class NET_EXPORT_PRIVATE ServerHelloNotifier : public
+ QuicAckNotifier::DelegateInterface {
+ public:
+ explicit ServerHelloNotifier(QuicCryptoServerStream* stream)
+ : server_stream_(stream) {}
+
+ // QuicAckNotifier::DelegateInterface implementation
+ virtual void OnAckNotification(
+ int num_original_packets,
+ int num_original_bytes,
+ int num_retransmitted_packets,
+ int num_retransmitted_bytes,
+ QuicTime::Delta delta_largest_observed) OVERRIDE;
+
+ private:
+ virtual ~ServerHelloNotifier() {}
+
+ QuicCryptoServerStream* server_stream_;
+
+ DISALLOW_COPY_AND_ASSIGN(ServerHelloNotifier);
+};
+
class NET_EXPORT_PRIVATE QuicCryptoServerStream : public QuicCryptoStream {
public:
QuicCryptoServerStream(const QuicCryptoServerConfig& crypto_config,
@@ -44,6 +69,17 @@ class NET_EXPORT_PRIVATE QuicCryptoServerStream : public QuicCryptoStream {
uint8 num_handshake_messages() const { return num_handshake_messages_; }
+ int num_server_config_update_messages_sent() const {
+ return num_server_config_update_messages_sent_;
+ }
+
+ // Sends the latest server config and source-address token to the client.
+ void SendServerConfigUpdate();
+
+ // Called by the ServerHello AckNotifier once the SHLO has been ACKed by the
+ // client.
+ void OnServerHelloAcked();
+
protected:
virtual QuicErrorCode ProcessClientHello(
const CryptoHandshakeMessage& message,
@@ -90,8 +126,12 @@ class NET_EXPORT_PRIVATE QuicCryptoServerStream : public QuicCryptoStream {
// handshake message is being validated.
ValidateCallback* validate_client_hello_cb_;
+ // Number of handshake messages received by this stream.
uint8 num_handshake_messages_;
+ // Number of server config update (SCUP) messages sent by this stream.
+ int num_server_config_update_messages_sent_;
+
DISALLOW_COPY_AND_ASSIGN(QuicCryptoServerStream);
};
diff --git a/net/quic/quic_crypto_server_stream_test.cc b/net/quic/quic_crypto_server_stream_test.cc
index 8e8da00..c2e8bf8 100644
--- a/net/quic/quic_crypto_server_stream_test.cc
+++ b/net/quic/quic_crypto_server_stream_test.cc
@@ -131,6 +131,7 @@ TEST_P(QuicCryptoServerStreamTest, ConnectedAfterCHLO) {
EXPECT_EQ(2, CompleteCryptoHandshake());
EXPECT_TRUE(stream_.encryption_established());
EXPECT_TRUE(stream_.handshake_confirmed());
+ EXPECT_EQ(1, stream_.num_server_config_update_messages_sent());
}
TEST_P(QuicCryptoServerStreamTest, ZeroRTT) {
@@ -223,6 +224,7 @@ TEST_P(QuicCryptoServerStreamTest, ZeroRTT) {
}
EXPECT_EQ(1, client->num_sent_client_hellos());
+ EXPECT_EQ(1, server->num_server_config_update_messages_sent());
}
TEST_P(QuicCryptoServerStreamTest, MessageAfterHandshake) {
diff --git a/net/quic/quic_crypto_stream.cc b/net/quic/quic_crypto_stream.cc
index 25467cb..fe2d311 100644
--- a/net/quic/quic_crypto_stream.cc
+++ b/net/quic/quic_crypto_stream.cc
@@ -61,11 +61,17 @@ QuicPriority QuicCryptoStream::EffectivePriority() const {
void QuicCryptoStream::SendHandshakeMessage(
const CryptoHandshakeMessage& message) {
+ SendHandshakeMessage(message, NULL);
+}
+
+void QuicCryptoStream::SendHandshakeMessage(
+ const CryptoHandshakeMessage& message,
+ QuicAckNotifier::DelegateInterface* delegate) {
DVLOG(1) << ENDPOINT << "Sending " << message.DebugString();
session()->OnCryptoHandshakeMessageSent(message);
const QuicData& data = message.GetSerialized();
// TODO(wtc): check the return value.
- WriteOrBufferData(string(data.data(), data.length()), false, NULL);
+ WriteOrBufferData(string(data.data(), data.length()), false, delegate);
}
bool QuicCryptoStream::ExportKeyingMaterial(
diff --git a/net/quic/quic_crypto_stream.h b/net/quic/quic_crypto_stream.h
index 131e096..4cce73c 100644
--- a/net/quic/quic_crypto_stream.h
+++ b/net/quic/quic_crypto_stream.h
@@ -45,6 +45,10 @@ class NET_EXPORT_PRIVATE QuicCryptoStream
// Sends |message| to the peer.
// TODO(wtc): return a success/failure status.
void SendHandshakeMessage(const CryptoHandshakeMessage& message);
+ // As above, but registers |delegate| for notification when |message| has been
+ // ACKed by the peer.
+ void SendHandshakeMessage(const CryptoHandshakeMessage& message,
+ QuicAckNotifier::DelegateInterface* delegate);
// Performs key extraction to derive a new secret of |result_len| bytes
// dependent on |label|, |context|, and the stream's negotiated subkey secret.
diff --git a/net/quic/quic_crypto_stream_test.cc b/net/quic/quic_crypto_stream_test.cc
index b21d5c0..7664912 100644
--- a/net/quic/quic_crypto_stream_test.cc
+++ b/net/quic/quic_crypto_stream_test.cc
@@ -10,7 +10,6 @@
#include "base/memory/scoped_ptr.h"
#include "net/quic/crypto/crypto_handshake.h"
#include "net/quic/crypto/crypto_protocol.h"
-#include "net/quic/quic_flags.h"
#include "net/quic/test_tools/crypto_test_utils.h"
#include "net/quic/test_tools/quic_test_utils.h"
#include "net/quic/test_tools/reliable_quic_stream_peer.h"
@@ -104,8 +103,6 @@ TEST_F(QuicCryptoStreamTest, ProcessBadData) {
}
TEST_F(QuicCryptoStreamTest, NoConnectionLevelFlowControl) {
- ValueRestore<bool> old_flag(&FLAGS_enable_quic_connection_flow_control_2,
- true);
if (connection_->version() <= QUIC_VERSION_20) {
EXPECT_FALSE(stream_.flow_controller()->IsEnabled());
} else {
diff --git a/net/quic/quic_data_stream_test.cc b/net/quic/quic_data_stream_test.cc
index b36053c..26dcd6c 100644
--- a/net/quic/quic_data_stream_test.cc
+++ b/net/quic/quic_data_stream_test.cc
@@ -6,7 +6,6 @@
#include "net/quic/quic_ack_notifier.h"
#include "net/quic/quic_connection.h"
-#include "net/quic/quic_flags.h"
#include "net/quic/quic_utils.h"
#include "net/quic/quic_write_blocked_list.h"
#include "net/quic/spdy_utils.h"
@@ -418,9 +417,6 @@ TEST_P(QuicDataStreamTest, ConnectionFlowControlWindowUpdate) {
if (GetParam() < QUIC_VERSION_19) {
return;
}
- ValueRestore<bool> old_flag(&FLAGS_enable_quic_connection_flow_control_2,
- true);
-
Initialize(kShouldProcessData);
// Set a small flow control limit for streams and connection.
@@ -505,8 +501,6 @@ TEST_P(QuicDataStreamTest, ConnectionFlowControlViolation) {
if (GetParam() < QUIC_VERSION_19) {
return;
}
- ValueRestore<bool> old_flag(&FLAGS_enable_quic_connection_flow_control_2,
- true);
// Stream should not process data, so that data gets buffered in the
// sequencer, triggering flow control limits.
diff --git a/net/quic/quic_dispatcher.cc b/net/quic/quic_dispatcher.cc
index 5c6836a..802f00b 100644
--- a/net/quic/quic_dispatcher.cc
+++ b/net/quic/quic_dispatcher.cc
@@ -164,7 +164,6 @@ QuicDispatcher::QuicDispatcher(const QuicConfig& config,
delete_sessions_alarm_(
helper_->CreateAlarm(new DeleteSessionsAlarm(this))),
supported_versions_(supported_versions),
- supported_versions_no_connection_flow_control_(supported_versions),
current_packet_(NULL),
framer_(supported_versions, /*unused*/ QuicTime::Zero(), true),
framer_visitor_(new QuicFramerVisitor(this)) {
@@ -180,19 +179,6 @@ void QuicDispatcher::Initialize(QuicServerPacketWriter* writer) {
DCHECK(writer_ == NULL);
writer_.reset(writer);
time_wait_list_manager_.reset(CreateQuicTimeWaitListManager());
-
- // Remove all versions > QUIC_VERSION_18 from the
- // supported_versions_no_connection_flow_control_ vector.
- QuicVersionVector::iterator connection_it =
- find(supported_versions_no_connection_flow_control_.begin(),
- supported_versions_no_connection_flow_control_.end(),
- QUIC_VERSION_19);
- if (connection_it != supported_versions_no_connection_flow_control_.end()) {
- supported_versions_no_connection_flow_control_.erase(
- supported_versions_no_connection_flow_control_.begin(),
- connection_it + 1);
- }
- CHECK(!supported_versions_no_connection_flow_control_.empty());
}
void QuicDispatcher::ProcessPacket(const IPEndPoint& server_address,
@@ -375,27 +361,14 @@ QuicConnection* QuicDispatcher::CreateQuicConnection(
const IPEndPoint& client_address,
QuicPerConnectionPacketWriter* writer) {
QuicConnection* connection;
- if (FLAGS_enable_quic_connection_flow_control_2) {
- DVLOG(1) << "Creating QuicDispatcher with all versions.";
- connection = new QuicConnection(connection_id,
- client_address,
- helper_,
- writer,
- false /* owns_writer */,
- true /* is_server */,
- supported_versions_);
- } else {
- DVLOG(1) << "Connection flow control disabled, creating QuicDispatcher "
- << "WITHOUT version 19 or higher.";
- connection = new QuicConnection(
- connection_id,
- client_address,
- helper_,
- writer,
- false /* owns_writer */,
- true /* is_server */,
- supported_versions_no_connection_flow_control_);
- }
+ connection = new QuicConnection(
+ connection_id,
+ client_address,
+ helper_,
+ writer,
+ false /* owns_writer */,
+ true /* is_server */,
+ supported_versions_);
writer->set_connection(connection);
return connection;
}
diff --git a/net/quic/quic_dispatcher.h b/net/quic/quic_dispatcher.h
index e7a5e95..684461d 100644
--- a/net/quic/quic_dispatcher.h
+++ b/net/quic/quic_dispatcher.h
@@ -137,11 +137,6 @@ class QuicDispatcher : public QuicBlockedWriterInterface,
return supported_versions_;
}
- const QuicVersionVector& supported_versions_no_connection_flow_control()
- const {
- return supported_versions_no_connection_flow_control_;
- }
-
const IPEndPoint& current_server_address() {
return current_server_address_;
}
@@ -206,14 +201,6 @@ class QuicDispatcher : public QuicBlockedWriterInterface,
// skipped as necessary).
const QuicVersionVector supported_versions_;
- // Versions which do not support *connection* flow control (introduced in
- // QUIC_VERSION_19).
- // This is used to construct new QuicConnections when connection flow control
- // is disabled via flag.
- // TODO(rjshade): Remove this when
- // FLAGS_enable_quic_connection_flow_control_2 is removed.
- QuicVersionVector supported_versions_no_connection_flow_control_;
-
// Information about the packet currently being handled.
IPEndPoint current_client_address_;
IPEndPoint current_server_address_;
diff --git a/net/quic/quic_flags.cc b/net/quic/quic_flags.cc
index 58980b3..affa271 100644
--- a/net/quic/quic_flags.cc
+++ b/net/quic/quic_flags.cc
@@ -19,20 +19,11 @@ bool FLAGS_track_retransmission_history = false;
// request pacing for the server to enable it.
bool FLAGS_enable_quic_pacing = true;
-// Do not remove this flag until b/11792453 is marked as Fixed.
-// If true, turns on connection level flow control in QUIC.
-// If this is disabled, all in flight QUIC connections talking QUIC_VERSION_19
-// or higher will timeout. New connections will be fine.
-bool FLAGS_enable_quic_connection_flow_control_2 = true;
-
bool FLAGS_quic_allow_oversized_packets_for_test = false;
// When true, the use time based loss detection instead of nack.
bool FLAGS_quic_use_time_loss_detection = false;
-// If true, allow peer port migration of established QUIC connections.
-bool FLAGS_quic_allow_port_migration = true;
-
// If true, it will return as soon as an error is detected while validating
// CHLO.
bool FLAGS_use_early_return_when_verifying_chlo = true;
@@ -44,3 +35,6 @@ bool FLAGS_send_quic_crypto_reject_reason = false;
// packets, to reduce latency of data delivery to the application. The client
// must also request FEC protection for the server to use FEC.
bool FLAGS_enable_quic_fec = false;
+
+// If true, a QUIC connection with too many unfinished streams will be closed.
+bool FLAGS_close_quic_connection_unfinished_streams = false;
diff --git a/net/quic/quic_flags.h b/net/quic/quic_flags.h
index f9654af..c68b822 100644
--- a/net/quic/quic_flags.h
+++ b/net/quic/quic_flags.h
@@ -9,12 +9,11 @@
NET_EXPORT_PRIVATE extern bool FLAGS_track_retransmission_history;
NET_EXPORT_PRIVATE extern bool FLAGS_enable_quic_pacing;
-NET_EXPORT_PRIVATE extern bool FLAGS_enable_quic_connection_flow_control_2;
NET_EXPORT_PRIVATE extern bool FLAGS_quic_allow_oversized_packets_for_test;
NET_EXPORT_PRIVATE extern bool FLAGS_quic_use_time_loss_detection;
-NET_EXPORT_PRIVATE extern bool FLAGS_quic_allow_port_migration;
NET_EXPORT_PRIVATE extern bool FLAGS_use_early_return_when_verifying_chlo;
NET_EXPORT_PRIVATE extern bool FLAGS_send_quic_crypto_reject_reason;
NET_EXPORT_PRIVATE extern bool FLAGS_enable_quic_fec;
+NET_EXPORT_PRIVATE extern bool FLAGS_close_quic_connection_unfinished_streams;
#endif // NET_QUIC_QUIC_FLAGS_H_
diff --git a/net/quic/quic_flow_controller.cc b/net/quic/quic_flow_controller.cc
index 9d09031..855d165 100644
--- a/net/quic/quic_flow_controller.cc
+++ b/net/quic/quic_flow_controller.cc
@@ -179,12 +179,7 @@ void QuicFlowController::Disable() {
}
bool QuicFlowController::IsEnabled() const {
- bool connection_flow_control_enabled =
- (id_ == kConnectionLevelId &&
- FLAGS_enable_quic_connection_flow_control_2);
- bool stream_flow_control_enabled = (id_ != kConnectionLevelId);
- return (connection_flow_control_enabled || stream_flow_control_enabled) &&
- is_enabled_;
+ return is_enabled_;
}
bool QuicFlowController::IsBlocked() const {
diff --git a/net/quic/quic_framer.h b/net/quic/quic_framer.h
index 41ed0b8..dc94b86 100644
--- a/net/quic/quic_framer.h
+++ b/net/quic/quic_framer.h
@@ -526,7 +526,7 @@ class NET_EXPORT_PRIVATE QuicFramer {
bool is_server_;
// If false, skip validation that the public flags are set to legal values.
bool validate_flags_;
- // The time this frames was created. Time written to the wire will be
+ // The time this framer was created. Time written to the wire will be
// written as a delta from this value.
QuicTime creation_time_;
diff --git a/net/quic/quic_framer_test.cc b/net/quic/quic_framer_test.cc
index 9b9401b..60aad46 100644
--- a/net/quic/quic_framer_test.cc
+++ b/net/quic/quic_framer_test.cc
@@ -46,7 +46,7 @@ const size_t kVersionOffset = kConnectionIdOffset + PACKET_8BYTE_CONNECTION_ID;
// Size in bytes of the stream frame fields for an arbitrary StreamID and
// offset and the last frame in a packet.
-size_t GetMinStreamFrameSize(QuicVersion version) {
+size_t GetMinStreamFrameSize() {
return kQuicFrameTypeSize + kQuicMaxStreamIdSize + kQuicMaxStreamOffsetSize;
}
@@ -461,8 +461,7 @@ class QuicFramerTest : public ::testing::TestWithParam<QuicVersion> {
size_t stream_id_size,
bool include_version) {
// Now test framing boundaries
- for (size_t i = kQuicFrameTypeSize;
- i < GetMinStreamFrameSize(framer_.version()); ++i) {
+ for (size_t i = kQuicFrameTypeSize; i < GetMinStreamFrameSize(); ++i) {
string expected_error;
if (i < kQuicFrameTypeSize + stream_id_size) {
expected_error = "Unable to read stream_id.";
diff --git a/net/quic/quic_headers_stream_test.cc b/net/quic/quic_headers_stream_test.cc
index 94f90c6..f2265ce 100644
--- a/net/quic/quic_headers_stream_test.cc
+++ b/net/quic/quic_headers_stream_test.cc
@@ -4,7 +4,6 @@
#include "net/quic/quic_headers_stream.h"
-#include "net/quic/quic_flags.h"
#include "net/quic/quic_utils.h"
#include "net/quic/spdy_utils.h"
#include "net/quic/test_tools/quic_connection_peer.h"
@@ -326,8 +325,6 @@ TEST_P(QuicHeadersStreamTest, ProcessSpdyWindowUpdateFrame) {
}
TEST_P(QuicHeadersStreamTest, NoConnectionLevelFlowControl) {
- ValueRestore<bool> old_flag(&FLAGS_enable_quic_connection_flow_control_2,
- true);
if (connection_->version() <= QUIC_VERSION_20) {
EXPECT_FALSE(headers_stream_->flow_controller()->IsEnabled());
} else {
diff --git a/net/quic/quic_protocol.cc b/net/quic/quic_protocol.cc
index c587c56..419820a 100644
--- a/net/quic/quic_protocol.cc
+++ b/net/quic/quic_protocol.cc
@@ -169,6 +169,8 @@ QuicTag QuicVersionToQuicTag(const QuicVersion version) {
return MakeQuicTag('Q', '0', '2', '0');
case QUIC_VERSION_21:
return MakeQuicTag('Q', '0', '2', '1');
+ case QUIC_VERSION_22:
+ return MakeQuicTag('Q', '0', '2', '2');
default:
// This shold be an ERROR because we should never attempt to convert an
// invalid QuicVersion to be written to the wire.
@@ -200,6 +202,7 @@ string QuicVersionToString(const QuicVersion version) {
RETURN_STRING_LITERAL(QUIC_VERSION_19);
RETURN_STRING_LITERAL(QUIC_VERSION_20);
RETURN_STRING_LITERAL(QUIC_VERSION_21);
+ RETURN_STRING_LITERAL(QUIC_VERSION_22);
default:
return "QUIC_VERSION_UNSUPPORTED";
}
diff --git a/net/quic/quic_protocol.h b/net/quic/quic_protocol.h
index d95df71..d91b55f 100644
--- a/net/quic/quic_protocol.h
+++ b/net/quic/quic_protocol.h
@@ -281,6 +281,7 @@ enum QuicVersion {
QUIC_VERSION_19 = 19, // Connection level flow control.
QUIC_VERSION_20 = 20, // Independent stream/connection flow control windows.
QUIC_VERSION_21 = 21, // Headers/crypto streams are flow controlled.
+ QUIC_VERSION_22 = 22, // Send Server Config Update messages on crypto stream.
};
// This vector contains QUIC versions which we currently support.
@@ -290,7 +291,8 @@ enum QuicVersion {
//
// IMPORTANT: if you are adding to this list, follow the instructions at
// http://sites/quic/adding-and-removing-versions
-static const QuicVersion kSupportedQuicVersions[] = {QUIC_VERSION_21,
+static const QuicVersion kSupportedQuicVersions[] = {QUIC_VERSION_22,
+ QUIC_VERSION_21,
QUIC_VERSION_20,
QUIC_VERSION_19,
QUIC_VERSION_18,
@@ -443,6 +445,8 @@ enum QuicErrorCode {
QUIC_INVALID_PRIORITY = 49,
// Too many streams already open.
QUIC_TOO_MANY_OPEN_STREAMS = 18,
+ // The peer must send a FIN/RST for each stream, and has not been doing so.
+ QUIC_TOO_MANY_UNFINISHED_STREAMS = 66,
// Received public reset for this connection.
QUIC_PUBLIC_RESET = 19,
// Invalid protocol version.
@@ -533,7 +537,7 @@ enum QuicErrorCode {
QUIC_VERSION_NEGOTIATION_MISMATCH = 55,
// No error. Used as bound while iterating.
- QUIC_LAST_ERROR = 66,
+ QUIC_LAST_ERROR = 67,
};
struct NET_EXPORT_PRIVATE QuicPacketPublicHeader {
diff --git a/net/quic/quic_sent_packet_manager.cc b/net/quic/quic_sent_packet_manager.cc
index f233d8b..a0ce684 100644
--- a/net/quic/quic_sent_packet_manager.cc
+++ b/net/quic/quic_sent_packet_manager.cc
@@ -87,7 +87,8 @@ QuicSentPacketManager::QuicSentPacketManager(
consecutive_crypto_retransmission_count_(0),
pending_tlp_transmission_(false),
max_tail_loss_probes_(kDefaultMaxTailLossProbes),
- using_pacing_(false) {
+ using_pacing_(false),
+ handshake_confirmed_(false) {
}
QuicSentPacketManager::~QuicSentPacketManager() {
@@ -606,7 +607,9 @@ bool QuicSentPacketManager::MaybeRetransmitTailLossProbe() {
if (!it->second.in_flight || frames == NULL) {
continue;
}
- DCHECK_NE(IS_HANDSHAKE, frames->HasCryptoHandshake());
+ if (!handshake_confirmed_) {
+ DCHECK_NE(IS_HANDSHAKE, frames->HasCryptoHandshake());
+ }
MarkForRetransmission(sequence_number, TLP_RETRANSMISSION);
return true;
}
@@ -652,7 +655,7 @@ void QuicSentPacketManager::RetransmitAllPackets() {
QuicSentPacketManager::RetransmissionTimeoutMode
QuicSentPacketManager::GetRetransmissionMode() const {
DCHECK(unacked_packets_.HasInFlightPackets());
- if (unacked_packets_.HasPendingCryptoPackets()) {
+ if (!handshake_confirmed_ && unacked_packets_.HasPendingCryptoPackets()) {
return HANDSHAKE_MODE;
}
if (loss_algorithm_->GetLossTimeout() != QuicTime::Zero()) {
diff --git a/net/quic/quic_sent_packet_manager.h b/net/quic/quic_sent_packet_manager.h
index 1bedf5a..3ace014 100644
--- a/net/quic/quic_sent_packet_manager.h
+++ b/net/quic/quic_sent_packet_manager.h
@@ -111,6 +111,8 @@ class NET_EXPORT_PRIVATE QuicSentPacketManager {
virtual void SetFromConfig(const QuicConfig& config);
+ void SetHandshakeConfirmed() { handshake_confirmed_ = true; }
+
// Called when a new packet is serialized. If the packet contains
// retransmittable data, it will be added to the unacked packet map.
void OnSerializedPacket(const SerializedPacket& serialized_packet);
@@ -362,6 +364,12 @@ class NET_EXPORT_PRIVATE QuicSentPacketManager {
SendAlgorithmInterface::CongestionMap packets_acked_;
SendAlgorithmInterface::CongestionMap packets_lost_;
+ // Set to true after the crypto handshake has successfully completed. After
+ // this is true we no longer use HANDSHAKE_MODE, and further frames sent on
+ // the crypto stream (i.e. SCUP messages) are treated like normal
+ // retransmittable frames.
+ bool handshake_confirmed_;
+
DISALLOW_COPY_AND_ASSIGN(QuicSentPacketManager);
};
diff --git a/net/quic/quic_session.cc b/net/quic/quic_session.cc
index 4c8f50d..f227727 100644
--- a/net/quic/quic_session.cc
+++ b/net/quic/quic_session.cc
@@ -262,8 +262,7 @@ void QuicSession::OnWindowUpdateFrames(
DVLOG(1) << ENDPOINT
<< "Received connection level flow control window update with "
"byte offset: " << frames[i].byte_offset;
- if (FLAGS_enable_quic_connection_flow_control_2 &&
- flow_controller_->UpdateSendWindowOffset(frames[i].byte_offset)) {
+ if (flow_controller_->UpdateSendWindowOffset(frames[i].byte_offset)) {
connection_window_updated = true;
}
continue;
@@ -428,10 +427,15 @@ void QuicSession::CloseStreamInner(QuicStreamId stream_id,
// of the how many bytes the stream's flow controller believes it has
// received, for accurate connection level flow control accounting.
if (!stream->HasFinalReceivedByteOffset() &&
- stream->flow_controller()->IsEnabled() &&
- FLAGS_enable_quic_connection_flow_control_2) {
+ stream->flow_controller()->IsEnabled()) {
locally_closed_streams_highest_offset_[stream_id] =
stream->flow_controller()->highest_received_byte_offset();
+ if (FLAGS_close_quic_connection_unfinished_streams &&
+ connection()->connected() &&
+ locally_closed_streams_highest_offset_.size() > max_open_streams_) {
+ // A buggy client may fail to send FIN/RSTs. Don't tolerate this.
+ connection_->SendConnectionClose(QUIC_TOO_MANY_UNFINISHED_STREAMS);
+ }
}
stream_map_.erase(it);
@@ -440,10 +444,6 @@ void QuicSession::CloseStreamInner(QuicStreamId stream_id,
void QuicSession::UpdateFlowControlOnFinalReceivedByteOffset(
QuicStreamId stream_id, QuicStreamOffset final_byte_offset) {
- if (!FLAGS_enable_quic_connection_flow_control_2) {
- return;
- }
-
map<QuicStreamId, QuicStreamOffset>::iterator it =
locally_closed_streams_highest_offset_.find(stream_id);
if (it == locally_closed_streams_highest_offset_.end()) {
diff --git a/net/quic/quic_session_test.cc b/net/quic/quic_session_test.cc
index 83f2447..b604a7f 100644
--- a/net/quic/quic_session_test.cc
+++ b/net/quic/quic_session_test.cc
@@ -531,8 +531,6 @@ TEST_P(QuicSessionTest, OnCanWriteLimitsNumWritesIfFlowControlBlocked) {
return;
}
- ValueRestore<bool> old_flag(&FLAGS_enable_quic_connection_flow_control_2,
- true);
// Ensure connection level flow control blockage.
QuicFlowControllerPeer::SetSendWindowOffset(session_.flow_controller(), 0);
EXPECT_TRUE(session_.flow_controller()->IsBlocked());
@@ -711,8 +709,6 @@ TEST_P(QuicSessionTest, ConnectionFlowControlAccountingRstOutOfOrder) {
return;
}
- ValueRestore<bool> old_flag(&FLAGS_enable_quic_connection_flow_control_2,
- true);
// Test that when we receive an out of order stream RST we correctly adjust
// our connection level flow control receive window.
// On close, the stream should mark as consumed all bytes between the highest
@@ -741,8 +737,6 @@ TEST_P(QuicSessionTest, ConnectionFlowControlAccountingFinAndLocalReset) {
return;
}
- ValueRestore<bool> old_flag(&FLAGS_enable_quic_connection_flow_control_2,
- true);
// Test the situation where we receive a FIN on a stream, and before we fully
// consume all the data from the sequencer buffer we locally RST the stream.
// The bytes between highest consumed byte, and the final byte offset that we
@@ -789,8 +783,6 @@ TEST_P(QuicSessionTest, ConnectionFlowControlAccountingFinAfterRst) {
return;
}
- ValueRestore<bool> old_flag(&FLAGS_enable_quic_connection_flow_control_2,
- true);
// Connection starts with some non-zero highest received byte offset,
// due to other active streams.
const uint64 kInitialConnectionBytesConsumed = 567;
@@ -833,8 +825,6 @@ TEST_P(QuicSessionTest, ConnectionFlowControlAccountingRstAfterRst) {
return;
}
- ValueRestore<bool> old_flag(&FLAGS_enable_quic_connection_flow_control_2,
- true);
// Connection starts with some non-zero highest received byte offset,
// due to other active streams.
const uint64 kInitialConnectionBytesConsumed = 567;
@@ -869,8 +859,6 @@ TEST_P(QuicSessionTest, FlowControlWithInvalidFinalOffset) {
if (version() <= QUIC_VERSION_16) {
return;
}
- ValueRestore<bool> old_flag(&FLAGS_enable_quic_connection_flow_control_2,
- true);
const uint64 kLargeOffset = kInitialSessionFlowControlWindowForTest + 1;
EXPECT_CALL(*connection_,
@@ -896,8 +884,6 @@ TEST_P(QuicSessionTest, VersionNegotiationDisablesFlowControl) {
return;
}
- ValueRestore<bool> old_flag(&FLAGS_enable_quic_connection_flow_control_2,
- true);
// Test that after successful version negotiation, flow control is disabled
// appropriately at both the connection and stream level.
@@ -918,6 +904,35 @@ TEST_P(QuicSessionTest, VersionNegotiationDisablesFlowControl) {
EXPECT_FALSE(stream->flow_controller()->IsEnabled());
}
+TEST_P(QuicSessionTest, TooManyUnfinishedStreamsCauseConnectionClose) {
+ if (version() < QUIC_VERSION_18) {
+ return;
+ }
+ // If a buggy/malicious peer creates too many streams that are not ended with
+ // a FIN or RST then we send a connection close.
+ ValueRestore<bool> old_flag(&FLAGS_close_quic_connection_unfinished_streams,
+ true);
+
+ EXPECT_CALL(*connection_,
+ SendConnectionClose(QUIC_TOO_MANY_UNFINISHED_STREAMS)).Times(1);
+
+ const int kMaxStreams = 5;
+ QuicSessionPeer::SetMaxOpenStreams(&session_, kMaxStreams);
+
+ // Create kMaxStreams + 1 data streams, and close them all without receiving a
+ // FIN or a RST from the client.
+ const int kFirstStreamId = kClientDataStreamId1;
+ const int kFinalStreamId = kClientDataStreamId1 + 2 * kMaxStreams + 1;
+ for (int i = kFirstStreamId; i < kFinalStreamId; i += 2) {
+ QuicStreamFrame data1(i, false, 0, MakeIOVector("HT"));
+ vector<QuicStreamFrame> frames;
+ frames.push_back(data1);
+ session_.OnStreamFrames(frames);
+ EXPECT_EQ(1u, session_.GetNumOpenStreams());
+ session_.CloseStream(i);
+ }
+}
+
} // namespace
} // namespace test
} // namespace net
diff --git a/net/quic/quic_utils.cc b/net/quic/quic_utils.cc
index 87ec18c..8956fa2 100644
--- a/net/quic/quic_utils.cc
+++ b/net/quic/quic_utils.cc
@@ -191,6 +191,7 @@ const char* QuicUtils::ErrorToString(QuicErrorCode error) {
RETURN_STRING_LITERAL(QUIC_INVALID_STREAM_ID);
RETURN_STRING_LITERAL(QUIC_INVALID_PRIORITY);
RETURN_STRING_LITERAL(QUIC_TOO_MANY_OPEN_STREAMS);
+ RETURN_STRING_LITERAL(QUIC_TOO_MANY_UNFINISHED_STREAMS);
RETURN_STRING_LITERAL(QUIC_PUBLIC_RESET);
RETURN_STRING_LITERAL(QUIC_INVALID_VERSION);
RETURN_STRING_LITERAL(QUIC_INVALID_HEADER_ID);
diff --git a/net/quic/reliable_quic_stream_test.cc b/net/quic/reliable_quic_stream_test.cc
index f25011d..f188783 100644
--- a/net/quic/reliable_quic_stream_test.cc
+++ b/net/quic/reliable_quic_stream_test.cc
@@ -6,7 +6,6 @@
#include "net/quic/quic_ack_notifier.h"
#include "net/quic/quic_connection.h"
-#include "net/quic/quic_flags.h"
#include "net/quic/quic_utils.h"
#include "net/quic/quic_write_blocked_list.h"
#include "net/quic/spdy_utils.h"
@@ -471,9 +470,7 @@ TEST_F(ReliableQuicStreamTest, WriteOrBufferDataWithQuicAckNotifier) {
// Set a large flow control send window so this doesn't interfere with test.
stream_->flow_controller()->UpdateSendWindowOffset(kDataSize + 1);
- if (FLAGS_enable_quic_connection_flow_control_2) {
- session_->flow_controller()->UpdateSendWindowOffset(kDataSize + 1);
- }
+ session_->flow_controller()->UpdateSendWindowOffset(kDataSize + 1);
scoped_refptr<QuicAckNotifier::DelegateInterface> proxy_delegate;
@@ -526,9 +523,7 @@ TEST_F(ReliableQuicStreamTest, WriteOrBufferDataAckNotificationBeforeFlush) {
// Set a large flow control send window so this doesn't interfere with test.
stream_->flow_controller()->UpdateSendWindowOffset(kDataSize + 1);
- if (FLAGS_enable_quic_connection_flow_control_2) {
- session_->flow_controller()->UpdateSendWindowOffset(kDataSize + 1);
- }
+ session_->flow_controller()->UpdateSendWindowOffset(kDataSize + 1);
scoped_refptr<QuicAckNotifier::DelegateInterface> proxy_delegate;
@@ -634,9 +629,6 @@ TEST_F(ReliableQuicStreamTest, WriteAndBufferDataWithAckNotiferOnlyFinRemains) {
// as we check for violation and close the connection early.
TEST_F(ReliableQuicStreamTest,
StreamSequencerNeverSeesPacketsViolatingFlowControl) {
- ValueRestore<bool> old_connection_flag(
- &FLAGS_enable_quic_connection_flow_control_2, true);
-
Initialize(kShouldProcessData);
// Receive a stream frame that violates flow control: the byte offset is
diff --git a/net/quic/test_tools/crypto_test_utils.cc b/net/quic/test_tools/crypto_test_utils.cc
index 0e3a815..86ff642 100644
--- a/net/quic/test_tools/crypto_test_utils.cc
+++ b/net/quic/test_tools/crypto_test_utils.cc
@@ -62,9 +62,9 @@ class CryptoFramerVisitor : public CryptoFramerVisitorInterface {
};
// MovePackets parses crypto handshake messages from packet number
-// |*inout_packet_index| through to the last packet and has |dest_stream|
-// process them. |*inout_packet_index| is updated with an index one greater
-// than the last packet processed.
+// |*inout_packet_index| through to the last packet (or until a packet fails to
+// decrypt) and has |dest_stream| process them. |*inout_packet_index| is updated
+// with an index one greater than the last packet processed.
void MovePackets(PacketSavingConnection* source_conn,
size_t *inout_packet_index,
QuicCryptoStream* dest_stream,
@@ -84,7 +84,12 @@ void MovePackets(PacketSavingConnection* source_conn,
size_t index = *inout_packet_index;
for (; index < source_conn->encrypted_packets_.size(); index++) {
- ASSERT_TRUE(framer.ProcessPacket(*source_conn->encrypted_packets_[index]));
+ if (!framer.ProcessPacket(*source_conn->encrypted_packets_[index])) {
+ // The framer will be unable to decrypt forward-secure packets sent after
+ // the handshake is complete. Don't treat them as handshake packets.
+ break;
+ }
+
for (vector<QuicStreamFrame>::const_iterator
i = framer.stream_frames().begin();
i != framer.stream_frames().end(); ++i) {
diff --git a/net/tools/quic/end_to_end_test.cc b/net/tools/quic/end_to_end_test.cc
index 1243e34..fe1bbd1 100644
--- a/net/tools/quic/end_to_end_test.cc
+++ b/net/tools/quic/end_to_end_test.cc
@@ -197,9 +197,6 @@ class EndToEndTest : public ::testing::TestWithParam<TestParams> {
FLAGS_enable_quic_pacing = GetParam().use_pacing;
FLAGS_enable_quic_fec = GetParam().use_fec;
- if (negotiated_version_ >= QUIC_VERSION_19) {
- FLAGS_enable_quic_connection_flow_control_2 = true;
- }
VLOG(1) << "Using Configuration: " << GetParam();
client_config_.SetDefaults();
@@ -1165,8 +1162,6 @@ TEST_P(EndToEndTest, ConnectionMigrationClientPortChanged) {
// Tests that the client's port can change during an established QUIC
// connection, and that doing so does not result in the connection being
// closed by the server.
- FLAGS_quic_allow_port_migration = true;
-
ASSERT_TRUE(Initialize());
EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
diff --git a/net/tools/quic/quic_dispatcher.cc b/net/tools/quic/quic_dispatcher.cc
index 9980674..cf3a46b 100644
--- a/net/tools/quic/quic_dispatcher.cc
+++ b/net/tools/quic/quic_dispatcher.cc
@@ -169,7 +169,6 @@ QuicDispatcher::QuicDispatcher(const QuicConfig& config,
epoll_server_(epoll_server),
helper_(new QuicEpollConnectionHelper(epoll_server_)),
supported_versions_(supported_versions),
- supported_versions_no_connection_flow_control_(supported_versions),
current_packet_(NULL),
framer_(supported_versions, /*unused*/ QuicTime::Zero(), true),
framer_visitor_(new QuicFramerVisitor(this)) {
@@ -185,18 +184,6 @@ void QuicDispatcher::Initialize(int fd) {
DCHECK(writer_ == NULL);
writer_.reset(CreateWriter(fd));
time_wait_list_manager_.reset(CreateQuicTimeWaitListManager());
-
- // Remove all versions > QUIC_VERSION_18 from the
- // supported_versions_no_connection_flow_control_ vector.
- QuicVersionVector::iterator connection_it = find(
- supported_versions_no_connection_flow_control_.begin(),
- supported_versions_no_connection_flow_control_.end(), QUIC_VERSION_19);
- if (connection_it != supported_versions_no_connection_flow_control_.end()) {
- supported_versions_no_connection_flow_control_.erase(
- supported_versions_no_connection_flow_control_.begin(),
- connection_it + 1);
- }
- CHECK(!supported_versions_no_connection_flow_control_.empty());
}
void QuicDispatcher::ProcessPacket(const IPEndPoint& server_address,
@@ -376,26 +363,13 @@ QuicConnection* QuicDispatcher::CreateQuicConnection(
QuicConnectionId connection_id,
const IPEndPoint& server_address,
const IPEndPoint& client_address) {
- if (FLAGS_enable_quic_connection_flow_control_2) {
- DLOG(INFO) << "Creating QuicDispatcher with all versions.";
- return new QuicConnection(connection_id,
- client_address,
- helper_.get(),
- writer_.get(),
- false /* owns_writer */,
- true /* is_server */,
- supported_versions_);
- }
-
- DLOG(INFO) << "Connection flow control disabled, creating QuicDispatcher "
- << "WITHOUT version 19 or higher.";
return new QuicConnection(connection_id,
client_address,
helper_.get(),
writer_.get(),
false /* owns_writer */,
true /* is_server */,
- supported_versions_no_connection_flow_control_);
+ supported_versions_);
}
QuicTimeWaitListManager* QuicDispatcher::CreateQuicTimeWaitListManager() {
diff --git a/net/tools/quic/quic_dispatcher.h b/net/tools/quic/quic_dispatcher.h
index 088de31..8d7b03b 100644
--- a/net/tools/quic/quic_dispatcher.h
+++ b/net/tools/quic/quic_dispatcher.h
@@ -144,11 +144,6 @@ class QuicDispatcher : public QuicServerSessionVisitor,
return supported_versions_;
}
- const QuicVersionVector& supported_versions_no_connection_flow_control()
- const {
- return supported_versions_no_connection_flow_control_;
- }
-
const IPEndPoint& current_server_address() {
return current_server_address_;
}
@@ -215,14 +210,6 @@ class QuicDispatcher : public QuicServerSessionVisitor,
// skipped as necessary).
const QuicVersionVector supported_versions_;
- // Versions which do not support *connection* flow control (introduced in
- // QUIC_VERSION_19).
- // This is used to construct new QuicConnections when connection flow control
- // is disabled via flag.
- // TODO(rjshade): Remove this when
- // FLAGS_enable_quic_connection_flow_control_2 is removed.
- QuicVersionVector supported_versions_no_connection_flow_control_;
-
// Information about the packet currently being handled.
IPEndPoint current_client_address_;
IPEndPoint current_server_address_;